5、注解:jdk1.5的新特性;未来的开发模式基本都是基于注解的;是非常重要的知识点;比枚举更重要;
(1)java.lang包中提供的几个基本注解;
@SuppressWarnings(“deprecation”) 压缩警告;(过时了)
@Deprecated 这个注解表示标记所用的方法过时了;
@override 表示使用了覆盖父类的方法;
注解格式:@开头,后面加注解名称,然后后面加括号,括号内加上该注解的属性,没有结束标记如分号;
标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
注解应用的逻辑结构:
注解类——应用了“注解类”的类——对“应用了注解类的类”进行反射操作的类;
(2)自定义注解及其使用;
注解就是一个特殊的类,格式和接口的定义几乎一样,只有一点区别;
以@interface为注解的关键字;注解的属性就是按接口里的方法格式定义的;
调用注解的属性就和调用方法一样;
(接口是没有自定义的特有信息的,所以一般是没有成员属性的;)
和枚举一样,所有自定义的注解都是接口Annotation的子类;
注解和接口一样,类和方法前面的修饰符都是可以省略的,默认就是public abstract;
不管你写与不写,都只有这样一样修饰符;
注解的应用:
Class类中的方法;
获取在该类上的注解信息;不是获取注解声明;这是很重要的一个方法,获取注解的信息;用于给程序传递一些特定的值;这里的方法是获取注解的子类对象;
<A extends Annotation> A getAnnotation(Class<A> annotationClass) ;
Annotation[] getAnnotations() ;
Annotation[] getDeclaredAnnotations()
判断某注解是否在该类上;
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
注解的包java.lang.annotation
【补充】
元注解/元数据/元信息;
元注解是指注解上面的注解;
常用的元注解有:
@Retent(RetentionPolicy.RUNTIME): (翻译:保持力许可)
该注解有一个返回枚举的属性value();
该枚举类型是RetentionPolicy,共三个元素,CLASS,RUNTIME,SOURCE;
该枚举表示的含义是注解在类上保留的声明周期;不加该注解就默认是RetentionPolicy.CLASS
即表示一个注解的生命周期有三个阶段:java源文件——class文件——内存中的字节码;
【补充】字节码对象不是.class文件,字节码对象是指class文件经过类加载器处理转换后得到的二进制位表示的东西;
@override 的生命周期是 :RetentionPolicy.SOURCE
@Deprecated 的生命周期是: RetentionPolicy.SOURCE
@SuppressWarnings 的生命周期:RetentPolicy.RUNTIME
另一个常用元注释是:
@Target(ElementType.TYPE)
该注解和@Retention注解一样,也只有一个属性value();返回类型也依然是一个枚举数组;
该枚举为ElementType,共8个元素;其中TYPE 表示可以放在类上,枚举上,接口中,注解上等;......
该注解指定自定义的注解可以放在一个类当中的位置,如包上,方法上等;不加该注解就默认哪里都可以;
(3)自定义注解的属性定义;
注解就是一个特殊的接口,里面的抽象方法就是通常注解中所说的属性;给注解定义一个属性就相当于定义一个接口中的抽象方法;
注解和接口一样,类和方法前面的修饰符都是可以省略的,默认就是public abstract;不管你写与不写,都只有这样一样修饰符;
当注解被声明时,其实就相当于运用了一个匿名内部类的模式;属性的定义中省略了return语句等复杂的书写;特别定义了一些简单书写的规范;
【反复验证了这个理解一定是对的】
声明某个注解就相当于建立了该注解的一个子类对象;即在注解名称前写出了@就是新建了实例对象;如一些定一个返回注解类型的属性;
AnnoDemo annodemo() default @AnnoDemo;
即注解的实例对象就是: @+名字+括号(赋值);
定义属性的default值:
直接在抽象方法,后面空格 default,然后空格值,最后再写分号;
在声明注解对属性赋值时,属性放在注解后面的括号中,内容格式如同键值对形式,“属性=值”的格式,多个属性用逗号分开;
【易错点】
注解如同接口一样,声明一个注解就必须要对里面的方法进行赋值;除非在定义注解的时候用default定义了默认值,此时可以选择性赋值;
(我的理解是注解是特殊的接口,不是特殊的类;而且当作接口去理解来理解其他的细节更好理解;如如何调用注解的属性以及注解的属性声明时必须要进行赋值);
【易错点】
注解属性的返回值类型只能是 Primitive,String,class,enum,Annotation ,以及这些类型的一维数组类型;
当声明注解时只有一个属性必需要赋值并且是value()属性时,可以简化括号内的赋值书写,不写属性名,直接写值即可,如@SuppressWarnings(“deprecation”);
返回值类型为数组的属性,在使用属性赋值时注意格式,但是如果数组中只赋值一个元素的时候,可以简化书写,省略大括号;如常用的@Target(ElementType.TYPE)就是一个数组简化书写了的;
如果注解没有定义属性,在一个程序上声明注解的时候,就不需要写括号;如 @Override ; @Deprecated ;
注解的详细语法可以通过看java语言规范了解,即看java的language specification。
【心得经验】
注解可以和枚举很好的结合使用,而枚举可以很好的和switch语句结合使用;
我觉得注解就像javabean一样,又是对某常用部分的一个模版式简化,主要用来简化一些东西,对做大型项目中很有用,如资源配置等;
6、泛型:也是JDK1.5的新特性;
(1)泛型的基本使用:
泛型的好处:
(a)将运行时期出现的问题转移到了编译时期;
(b)避免了类型转换的麻烦;比如需要调用子类方法没有泛型就必须要强转;
(c)提高了安全性;
泛型的生命周期:
泛型的作用时段只在编译时期,在运行时期就已经去泛型定义了,所以没有C++中的泛型智能;据说要在运行期有效就要修改jvm指令集,厂家不愿搞怕麻烦;
【易错处】
通过反射可以给带有泛型类型参数的集合添加不同类型的元素,但是集合泛型定义为String类型除外;
泛型的定义:
在集合中泛型的类型参数不可以使用多态形式声明,必须左右一致,但是可以通过分句声明泛型来用多态表示,如;
List l = new ArrayList<String>(); List<Object> l2 = l;
在定义集合数组时要注意:集合类型的数组在声明时可以定义参数化泛型,但是在定义new集合数组时不可以定义参数化泛型;
List<String>[] l;正确; new List<String>[] ;错误;
【易错处】
泛型的类型参数接收的值只能为引用数据类型,不能是基本数据类型;
(泛型最典型应用就是集合,从这里一想就明白;)
【易忽略】
我们在使用异常的时候也可以通过定义泛型的方式来处理问题;
注意一点 catch语句括号内不能用泛型代替异常定义;因为catch中的异常是别人传来的;
泛型的推断:
取类型的最小公倍数的性质;
(2)泛型的限定:
泛型的通配符:<?>
【易错处】
List<String>的集合是不能用List<Object>来表示,因为这是两个完全不同的集合;泛型上没有多态;
List<?>用通配符定义的泛型和没有定义泛型性质差不多;也相当于List<Object>;等于没有定义泛型;
如果需要调用子类方法,还是需要进行强转;
<? extends T> 表示接受T类型或者T的子类型;
<? super T> 表示接受T类型或者T的父类型;;
<? extends T&E> 表示接受同时是T和E类型的子类型;
泛型的限定中,用&表示多个边界;
(3)自定义泛型;在crud上应用的比较广泛;
可以定义多个泛型在<>中,中间用逗号分开,如Map集合等;
泛型可以定义在类上,接口上,和方法上;
类和接口上泛型定义在类名的后面,方法上泛型定义在方法名的前面;
方法上定义的泛型不与类定义的泛型相冲突,方法上的泛型是可以独立定义的;即可使用类上的泛型也可以另外自定义泛型;
【易错处】
特殊之处:静态方法不可以访问类上定义的泛型;只能独立在方法上自定义,因为静态成员是独立于对象的;
(4)通过反射获取泛型的参数化类型;
只能获取方法中传入的带有泛型的参数列表的泛型类型参数
Method方法中的 Type[] getGenericParameterTypes()
Type的子接口 ParameterizedType 中有三个方法可以获得泛型原始数据类型;
Type[] getActualTypeArguments() 获取带泛型参数的最原始数据类型;
Type getRawType() 获取参数列表带泛型的参数的类型;
7、类加载器;
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。
BootStrap加载器加载的是JRE/lib/rt.jar里的文件;
ExtClassLoader 加载器加载的是JRE/lib/ext/*.jar文件中的类;
AppClassLoader加载器加载的是CLASSPATH指定的所有jar或目录;
这三者的加载顺序关系是祖父孙的关系;不是说类的关系;是一个树状结构;
【类加载器委托机制】
类加载加载类文件时,就相当于发起者,发起者先委托给父级的加载器先加载,若父级上有,就直接加载,则子级加载器就不再加载了,如父级加载器没有,就再返回子类进行加载;如果最后回到发起者没有找到,那么就会抛出异常;
【易错处】
只要类的加载器一旦确认,后面的类下面调用的类都是用这个加载器加载的,是不会再去回到子级加载器或者父级加载器去加载的;
类加载器的委托机制可以有利于集中管理,不会出现多份字节码的现象;
java虚拟机遇到了一个类,首先是由当前线程的类加载器去加载线程中的第一个类;
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某一个类加载器去加载某个类;
模版方法设计模式;
自定义类加载器就是使用了这个模式;
8、动态代理:
VM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
代理通用方法最终的实现就是用的装饰设计模式的思想和面向父类或者接口编程的思想体现的;
Class类中的getInterface()方法可以获得对象实现的所有的接口;这样就可以间接实现所有的接口了;
把getProxy()当成了bean中的getter;所以就有了这个特殊的Proxybean;