黑马程序员 — 基础加强(上)

来源:互联网 发布:淘宝换货要确认收货吗 编辑:程序博客网 时间:2024/05/21 17:15
-------android培训、java培训、期待与您交流! ----------

基础加强(上)



IDE开发工具:

什么是IDE:

ide -> integrity development environment(集成开发环境)


IDE开发工具都支持使用工程化方式管理一个项目的程序开发过程,一般来说一个相对独立的项目就是一个工程,一个项目中涉及的多个java文件,
资源文件等用一个工程进行管理(在这里可以看看目前工作间的某个工程的结构),在不使用工程管理的情况下,如果一个项目中包括多个java源文件,
编程人员需要精心维护这些源文件之间、以及源文件与其它文件的目录关系,需要逐一编译这些源文件,需要手工启动运行编译后的结果,如果将一个
程序的所有源文件用一个工程来组织,开发工具能对所有的源文件集中管理,记住每个源文件的位置和相互关系,工程中有哪几个源文件启动类是哪个,
启动参数设置等配置信息工工程中都记录




设置某个工程的javac和java:

选择工具,右键 -> properties可以设置javac,右键 -> run as -> open run dialog 可以设置java 。先将某个工程的java改为1.5运行看版本
问题,然后再设置整个工作间的javac,看是否影响(好像不影响),然后再单独设置该工程的javac,让其也为jdk1.5,再运行就没版本问题了


快捷键的使用技巧:


快捷键的位置: General -> Keys, 设置alt+/键进行内容提示时,要注意解除alt+/原来的绑定关系,直接输入alt+/就可以找到它的绑定关系
删除绑定关系时也可以使用Remove bingding 这个按钮,




MyEclipse 和 Eclipse 的关系:
MyEclipse是Eclipse的一个插件,扩展了Eclipse的功能,就可以开发javaEE的程序,厂商已经把两者结合到了一起



可变参数:


一个方法接受的参数不固定
可变参数的特点:
只能出现在参数列表的最后
...位于变量类型的变量名之间,前后有无空格都可以

调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数


package cn.itcast.day1;public class VarableParameter {public static void main(String[] args){System.out.println(add(1,2));System.out.println(add(1,2,4,5,6,7,8,76,4,3));}public static int add(int x, int ... args){int sum = x;for (int i = 0; i < args.length; ++i)sum += args[i];return sum;}}



享元模式:

在程序设计中,常常会有很多变量的值是一样的,而且它们很少发生变化,那么就可以打它们看做成一个变量,做为内部属性
而那些经常发生改变的量把它们做成外部属性



枚举:

为什么要有枚举:

问题:要定义星期几或性别的变量,该怎么定义? 假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;
枚举就是要让某个类型的变量的联取值只能为若干个固定的值中的一个,否则,编译器就会报错,枚举可以让编译器在编译时就可以控制

源程序中填写的非法值,普通变量的方法在开发阶段无法实现这一目标


package cn.itcast.day1;public class EnumTest{public static void main(String[]args){//WeekDay w = WeekDay.Friday;//System.out.println(w.nextDay());WeekDay w = WeekDay.FRIDAY;System.out.println(w);System.out.println(w.name());System.out.println(w.ordinal());  // 返回当前对象在枚举中的第几个位置System.out.println(WeekDay.valueOf("WEDNESDAY"));  // 把字符串变为WeekDay对象System.out.println(WeekDay.values().length);}public enum WeekDay{SUNDAY(1),MONDAY(2),TUESDAY(3),WEDNESDAY(4),THURSDAY(5),FRIDAY(6),SATURDAY(7);  // 元素列表必须在首位private WeekDay(){System.out.println("first");}private WeekDay(int day){System.out.println("second");}}public enum trafficLamp{RED(60){public trafficLamp nextLamp(){return GREEN;}},GREEN(50){public trafficLamp nextLamp(){return YELLOW;}},YELLOW(10){public trafficLamp nextLamp(){return RED;}};public abstract trafficLamp nextLamp();private int time;private trafficLamp(int time){this.time = time;}}}



装饰和继承的关系:



装饰模式比继承要灵活,避免了继承体系的臃肿,而且降低了类于类之间的关系
装饰类因为增强已有对象,具备的功能和已有的功能是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于一个体系中


Class 类


Class  --> 代表一个什么样的事物???

java共用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定
的,不同的实例对象有不同的属性值。java程序中的各个java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?
这个类的名字就是Class,要注意小写class关键字的区别,Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的
包名,字段名称的列表,方法名称的列表,学习反射,首先就要明白Class这个类。

java程序中的各个java程序属于同一类事物,描述这类事物的java类名就是Class


对比提问: 众多的人用一个什么类表示,众多的java类用一个什么类表示?

   人      -->   Person
 java类    -->   Class

对比提问:  Person类代表人,它的实例对象就是张三,李四,这样一个个具体的人,Class类代表java类,它的各个实例对象又分别对应什么呢?


  对应各个类在内存中的字节码,例如: Person类的字节码,ArrayList类的字节码,等等。
  一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存
  中的内容是不同的,这一个个的空间可分别用一个个的对象来表示 ,这些对象显然具有相同的类型,这个类型是什么呢?
  
如何等到各个字节码对应的实例对象(Class类型):

1.类名 Class, 例如: System.class
2.对象.getClass(),例如: new Date().getClass()
3.Class.forName("类名"); 例如: Class.forName("java.util.Date");





反射:


反射就是把java类中的各种成分映射成相应的java类,例如: 一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量、方法、构造方法、
包等等信息也用一个个的java类来表示,就汽车是一个类,汽车中的发动机,变速箱等等也是一个一个的类。表示java类的Class类显然要提供一系列的方法,
来获得其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应的实例对象来表示,它们是Field、Method、Constructor、Package等等。


一个类中的的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?
这正是学习和应用反射的要点。 




Constructor类:

得到某个类所有的构造方法:


例子: Constructor[] constructor = Class.forName("java.lang.String").getConstructors();



得到某一个构造方法:

例子: Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

注意: 获取方法时要用到类型


package cn.itcast.day1;import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;import java.util.*;public class ReflectTest {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubtry {String str1 = "abc";Class cla1 = str1.getClass();Class cla2 = String.class;Class cla3 = Class.forName("java.lang.String");System.out.println(cla1 == cla2); // true  都是同样的字节码System.out.println(cla1 == cla3); // trueSystem.out.println(cla1.isPrimitive()); // false  判断是否为基本类型System.out.println(int.class.isPrimitive()); // true int是基本类型System.out.println(int.class == Integer.class); // false  int为基本类型,Integer不是基本类型System.out.println(int.class == Integer.TYPE);  //true System.out.println(int[].class.isPrimitive());  // falseSystem.out.println(int[].class.isArray());  // true//需要注意的是,这两个方法都需要类型,getConstructor需要的是的类型而newInstance需要的是相同类型的对象Constructor ct1 = String.class.getConstructor(StringBuffer.class);  // 通过参数的个数来确定哪个构造方法String str2 = (String)ct1.newInstance(new StringBuffer("abc"));System.out.println(str2);ReflectPoint pt1 = new ReflectPoint(3,5);Field fieldY = pt1.getClass().getField("y");System.out.println(fieldY.get(pt1));  // fieldY只是对应的字节码,不是对象,要得到对象的值可以用get()方法获取//Field filedX = pt1.getClass().getField("x");  //error 因为x是私有的,所以不能直接访问Field filedX = pt1.getClass().getDeclaredField("x");//OK//System.out.println(filedX.get(pt1));  // x是私有的,不能直接使用 可以用setAccessible(true)方法filedX.setAccessible(true);System.out.println(filedX.get(pt1));changeStringValue(pt1);System.out.println(pt1);Method methodCharAt = String.class.getMethod("charAt", int.class);System.out.println(methodCharAt.invoke(str1, 2));//System.out.println(methodCharAt.invoke(null, 2));  // 如果第一个参数为null,那么这个方法必须为static(静态方法)System.out.println(methodCharAt.invoke(str1, new Object[]{2}));// 调用一个类的main方法,用普通的方式调用 //TestArguments.main(new String[]{"11"}); // 普通的方法String startingClassName = args[0];Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);mainMethod.invoke(null, (Object)new String[]{"111","222"}); // 这样的话,编译器会作特殊处理,编译时不反参数当作数组看待,也就不会数组打散成若干个参数了int[] a1 = new int[]{1,2,3};int[] a2 = new int[4];int[][] a3 = new int[2][3];String[] a4 = new String[]{"a","b","c"};System.out.println(a1.getClass() == a2.getClass());//System.out.println(a1.getClass() == a3.getClass());//System.out.println(a1.getClass() == a4.getClass());System.out.println(a1.getClass().getSuperclass().getName());  // 它们的父类都是ObjectSystem.out.println(a4.getClass().getSuperclass().getName());Object b1 = a1;Object b2 = a2;//Object[] b5 = a1;   // 这样是错误的,因为a1是int数组, int 是基本类型, 不是Object的子类Object[] b3 = a3;   // a3是数组的数组,数组也是Object,所以OKObject[] b4 = a4;System.out.println(a1);   // 这样的话,只会等到a1的hashcode,得不到想要的结果System.out.println(a4);System.out.println(Arrays.asList(a1));  // 这里error,jdk1.5会把数组当做一个Object对象,而不是当作若干个System.out.println(Arrays.asList(a4));  //OKprintObject(a1);printObject("a");}catch (Exception e) {e.printStackTrace();}finally {}}public static void printObject(Object obj){Class cls = obj.getClass();if (cls.isArray()){int len = Array.getLength(obj);for (int i = 0; i < len; ++i){System.out.println(Array.get(obj,i));}}else {System.out.println(obj);}}private static void changeStringValue(Object obj){Field[] fields = obj.getClass().getFields();for (Field field : fields){if (field.getType() == String.class){try{String oldValue = (String)field.get(obj);String newValue = oldValue.replace('b', 'a');field.set(obj, newValue);} catch (Exception e) {// TODO: handle exception}}}}}class TestArguments{public static void main(String[] args){for (String arg : args){System.out.println(arg);}}



创建实例对象:

通常方式: String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));

注意: 调用获取的方法是要用上面相同类型的实例对象

Class.newInstance()方法:

例子: String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的实现代码用到了缓存机制来保存默认的构造方法的实例对象。



Method类:

Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常方式: System.out.println(str.charAt(1));
反射方式:  System.out.println(charAt.invoke(str,1));

注意: 如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法


jdk.14和jdk1.5的invoke方法的区别:

jdk1.5: public Object invoke(Object obj, Object ....args);
jdk1.4: public Object invoke(Object obj, Object[] args);
即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中
的一个参数,所以,调用 charAt方法的代码也可以用jdk1.4改写为charAt invoke("str",new Object[]{1})的形式







用反射方式执行某个类中的main方法:

目标: 写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法


问题:

启动java程序main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来
调用这个main方法时,如何invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数
组中的每个元素对应一个参数,当把一个字符串作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法,所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{"xxx"}),
javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题

解决方法:

mainMethod.invoke(null,new Object[]{new String[]{"xxx"}})
mainMethod.invoke(null,(Object)new String[]{"xxx"})
编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了






数组的反射:


具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,
即可以当作Object类型使用,又可以当作Object[]类型使用
Arrays工具类用于完成对数组的反射操作



思考题: 怎么可以得到数组的类型?

目前不能得到数组的类型,不过可以得到数组元素的类型






HashSet:


提示:

通常来说,一个类的两个实例对象equals()方法的比较结果相等时,它们的哈希码也必须相等,
但反之则不成立,即equals()方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的
两个对象的equals()方法比较的结果可以不等,例如: 字符串“bb”和"Aa"的equals()方法比较结果
肯定不相等,但它们的hashCode方法返回的值却相等


当一个对象被存储进hashSet集合中后,就不能修改这个对象中也那些参与计算哈希值的字段了,
否则,对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了,在这种情况下,即使在
contains方法使用该对象当前的引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果
,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄漏






JavaBean:

JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则



如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean类中,这种JavaBean的实例对象通常称为值对象,(Value Object,简称VO),
这些信息在类中用私有的字段来存储,如果要读取或设置这些字段的值,则需要通过一些相应的方法来访问,这些方法叫什么好呢?JavaBean中的属性是
根据setter和getter方法来确定的,而不是根据其中的成员变量,如果方法名为setId,中文意思就是设置id,如果方法名为getId,则获取id的信息。

总之: 一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类中的内部成员变量


JavaBean给我们提供了什么方便:


一个符合javaBean特点的类可以当作普通的类一样使用,但把它当作JavaBean来使用肯定会有一些好处,我们才会去了解和应用JavaBean


好处如下:


在java EE 开发中,经常要使用到JavaBean很多环境就要求按JavaBean的方式进行操作,

JDK中提供了对JavaBean进行操作的API,这套API就称为内省,如果你要自己去通过getX访问私有的x,用内省这套API操作Javabean比用普通的方式更方便




用Eclipse将读取属性和设置属性的流水代码分别抽取成方法:


只要调用这个方法,并给这个方法传递一个对象,属性名和设置值,它就能完成属性修改的功能
得到JavaBean最好采用"Obj.getClass()"方式,而不要采用"类名.class"的方式,这样程序更通用
采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的x属性,在程序中把一个类
当作JavaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的BeanInfo对象封装了把这个类当作JavaBean看的结果信息



注解:


Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件中出现。


自定义注解及其应用:


定义一个最简单的注解: public @interface MyAnnotation{}
把它加在某个类上:  @MyAnnotation public class Annotation Tset{}
根据反射测试的问题,引出@Retention元注解的讲解,其三种取值:
RetetionPolicy.SOURCE、RetetionPolicy.Class、RetetionPolicy.RUNTIME: 分别对应,java源文件 --> class文件 --> 内存中的字节码 -->




元数据的作用:

如果要对于元数据的作用进行分类,还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

编写文档:通过代码里标识的元数据生成文档。
代码分析:通过代码里标识的元数据对代码进行分析。

编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查



@SuppressWarnings,其参数有:

deprecation,使用了过时的类或方法时的警告
unchecked,执行了未检查的转换时的警告
fallthrough,当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path,在类路径、源文件路径等中有不存在的路径时的警告
serial,当在可序列化的类上缺少serialVersionUID 定义时的警告
finally ,任何 finally 子句不能正常完成时的警告
all,关于以上所有情况的警告


原创粉丝点击