------- android培训、java培训、期待与您交流! ----------
Java 5中的新特性
一、静态带入
(1) import语句可以导入一个类或某个包中的所有类
(2 )import static语句导入一个类中的某个静态方法或所有静态方法
语法举例:
import static java.lang.Math.sin;
import static java.lang.Math.*;
二、可变参数
当一个方法接受参数个数不固定是,可以使用“...”来接收参数
可变参数的特点互
A:只能出现在参数列表的最后,这个要记住
B:...位于变量类型和变量名之间,前后有无空格都可以;
C:调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数 组的形式访问可变参数
代码举例
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(add(1,2,3,5));
System.out.println(add(2,3,5));
}
public static int add(int x,int ...args) {
int sum = x;
for(int i=0;i<args.length;i++) {
sum += args[i];
}
return sum;
}
三、增强for循环
语法:
for ( type 变量名:集合变量名 ) { … }
注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
举例:
public static int add(int x,int ...args) {
int sum = x;
for(int arg:args) {
sum += arg;
}
return sum;
}
四、自动装箱与拆箱
自动装箱:
Integer num1 = 12;
自动拆箱:
System.out.println(num1 + 12);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12; 这块相等,<=127都是真的
System.out.println(num1 == num2);
Integer num3 = 129; 这块不相等,因为是对象
Integer num4 = 129;
System.out.println(num3 == num4);
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12) ; 这块的道理同上
System.out.println(num5 == num6);
五、枚举
枚举的定义
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就 会报 错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的 方式在开 发阶段无法实现这一目标。
普通类实现枚举的步骤
A:私有的构造方法
B:每个元素分别用一个公有的静态成员变量表示
C:可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语 句转移成了一个个独立的类。
枚举的高级应用
A、枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
B、枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把 枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
C、带构造方法的枚举
a构造方法必须定义成私有的
b枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
D、带方法的枚举
a定义枚举TrafficLamp
b实现普通的next方法
c实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用 类似内部类的方式进行定义。
d增加上表示时间的构造方法
E、枚举只有一个成员时,就可以作为一种单例的实现方式。
六、反射
反射的概念
反射就是把Java类中的各种成分映射成相应的java类。
例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示
反射中的几个常用类
Constructor类
运行环境Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式: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();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
Field类
Field类代表某个类中的一个成员变量
演示用eclipse自动生成Java类的构造方法
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。
示例代码:
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
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对象对应的是一个静态方法!
jdk1.4和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})形式。
数组的反射
A:具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此 处比较与值无关)。
B:代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的 Class。
C:基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非 基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使 用。
D:Arrays.asList()方法处理int[]和String[]时的差异。
E:Array工具类用于完成对数组的反射操作。
反射的作用
A:框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
B:框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
C:综合案例
先直接用new 语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成 ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异。
引入了elipse对资源文件的管理方式的讲解。
七、JavaBean
A:JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主 要用于访问私有的字段,且方法名符合某种命名规则。
B:如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中, 这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息 在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应 的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其 中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名为 setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方法名 为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀, 剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首 字母改成小的。
setId()的属性名àid
isLast()的属性名àlast
setCPU的属性名是什么?àCPU
getUPS的属性名是什么?àUPS
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的, 它根本看不到java类内部的成员变量。
C:一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用 肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:
在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!
JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。
八、Java中的注解
先通过@SuppressWarnings的应用让大家认识和了解一下注解:
通过System.runFinalizersOnExit(true);的编译警告引出@SuppressWarnings("deprecation")
@Deprecated
直接在刚才的类中增加一个方法,并加上@Deprecated标注,在另外一个类中调用这个方法。
@Override
public boolean equals(Reflect other)方法与HashSet结合讲解
总结:
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
看java.lang包,可看到JDK中提供的最基本的annotation。
自定义注解及其应用
A:定义一个最简单的注解:public @interface MyAnnotation {}
B:把它加在某个类上:@MyAnnotation public class AnnotationTest{}
C:用反射进行测试AnnotationTest的定义上是否有@MyAnnotation
D:根据发射测试的问题,引出@Retention元注解的讲解,其三种取值:RetetionPolicy.SOURCE、 RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:java源文件-->class文件-->内存 中的字节码。
为注解增加基本属性
什么是注解的属性
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。如果还想区分出是传智播客哪个班的学生,这时候可以为胸牌在增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")
定义基本类型的属性和应用属性:
在注解类中增加String color();
@MyAnnotation(color="red")
用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象
为属性指定缺省值:
String color() default "yellow";
value属性:
String value() default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。
为注解增加高级属性
数组类型的属性
int [] arrayAttr() default {1,2,3};
@MyAnnotation(arrayAttr={2,3,4})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括
枚举类型的属性
EnumTest.TrafficLamp lamp() ;
@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
注解类型的属性:
MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotation ma = myAnnotation.annotationAttr();
System.out.println(ma.value());
泛型
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如,
Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要 能传 进去
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明 知故犯
Vector<Object> v = new Vector<String>(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
泛型中的?通配符
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
九、类加载器
简要介绍什么是类加载器和类加载器的作用
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
十、代理
程序中的代理
A:为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异 常处理、日志、计算方法的运行时间、事务管理、等等
B:编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方 法,并在调用方法时加上系统功能的代码。
C:如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置 文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能 时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间 后,又想去掉系统功能也很容易
AOP
A:系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
B:用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
C:交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
D:使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
A:要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态 代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
B:JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类, 即动态代理类。
C:JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作 具有相同接口的目标类的代理。
D:CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所 以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
E:代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果 外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
分析JVM动态生成的类
A:创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法 的各个参数。
B:编码列出动态类中的所有构造方法和参数签名
C:编码列出动态类中的所有方法和参数签名
D:创建动态类的实例对象
用反射获得构造方法
编写一个最简单的InvocationHandler类
调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
E:总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
a、生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
b、产生的类字节码必须有个一个关联的类加载器对象;
c、生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在 一 个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相 当于 插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象, 它 是在创建动态类的实例对象的构造方法时传递进去的。在上面的 InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被 调用运行了。
F:用Proxy.newInstance方法直接一步就创建出代理对象。