关于内省使用到反射, java.lang.reflet 是JDK什么版本提出的。 应该是JDK 最初就有,在随后版本在一直补充。
在其中最难理解的是内省:内省
“JDK1.5”(开发代码猛虎)的一个重要主题就是通过新增一些特性来简化开发,这些特性包括泛型、foreach循环、自动拆装箱、枚举、可变参数、内省、静态导入等。使用这些特性有助于我们编写更加清晰,强悍,安全的代码。
1.泛型(Generic)
C++通过模板技术可以指定集合的元素类型,而java在1.5之前一直没有相对应的功能,一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对他们进行强制的类型转换。猛虎引入了泛型,它允许指定集合里元素的类型,这样你可以强类型在编译时刻进行类型检查的好处。
2.foreach循环(Enhanced for loop)
foreach循环的加入简化了集合的遍历。假设我们要遍历一个集合对其中的元素进行一些处理。典型的代码为:
3.自动拆装箱(Autoboxing/unboxing)
自动拆装箱大大方便了基本类型数据和它们包装类的使用
自动装箱:基本类型自动转为包装类(int >> Integer)
自动拆箱:包装类自动转为基本类型(Integer >> int)
在JDK1.5之前,我们总是对集合不能存放基本类型而耿耿于怀,现在自动转换机制能解决我们的问题
4.类型安全的枚举(Type safe enums)
JDK1.5加入了一个全新的“类”–枚举类型。为此JDK1.5引入了一个新关键字enum。我们可以这样定义一个枚举类型。
5.可变参数(Var args)
可变参数使程序员可以声明一个接受可变数目参数的方法。注意,可变参数必须是函数生命中的最后一个参数。加入我们要写一个简单的方法打印一些对象:
在JDK1.5之前,我们可以用重载来实现,但是这样就需要写很多的重载函数,显得不是很有效。如果使用可变参数的话,我们只需要一个函数就行了:
在引入可变参数以后,Java的反射包也更加方便使用了,对于
6.内省(Introspector)
内省是java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用访问某个属性的getter和setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。
一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。
7.静态导入(Static import)
要使用静态成员(方法和变量)我们给出提供这个方法的类。使用静态导入可以使被导入类的所有静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名
8.注解(Annotation)
一、概述
1、注解(Annotation)相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记。以后,javac编译器、开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。 2、标记可以加在包,类,字段,方法,方法的参数以及局部变量上。 3、在java.lang包中提供了最基本的注解(annotation)。 4、格式:@注解类名。 如果只有一个value名称的属性或其他属性缺省,则可@注解名(”属性值”); 如果有多个或不缺省或者需重新赋值,则@注解名(属性名=”属性值”,…)。
二、java中三种最基本的注解
1、如@SuppressWarning(”deprecation”):表示压制过时警告;或者说不要警告过时提示了 SupressWarning是告知编译器或开发工具等不需要再提示指定的警告了; “deprecation”是警告的信息,即过时警告。 2、@Deprecated:表示告知调用者,该成员函数、字段等已经过时,不再推荐使用。 源代码标记@Deprecated是在JDK1.5中作为内置的annotation引入的,用于表明类(class)、方法(method)、字段(field)已经不再推荐使用,并且在以后的JDK版本中可能将其删除,编译器在默认情况下检测到有此标记的时候会提示警告信息。 例如:假定之前的某个类升级了,其中的某个方法已经过时了,不能够将过时的方法删除,因为可能会影响到之前调用此这个方法的某些程序,这时就可以通过在方法上加这个注解来标记。 3、@Override:表示下面的方法是在覆盖(父类方法),如果不存在覆盖,就会报错。 加上此注解,可对类中的方法判断是否是要覆盖的父类的方法。典型的例子即在类中覆盖equals(Object obj)方法时,其中的参数类型必须是Object,才能被覆盖;若不是,则不存在覆盖。此时如果加上了此注解就会提示警告。
三、注解的应用结构图
三、注解的应用结构图
四、自定义注解及其应用
1、定义格式:@interface名称{statement}
如: 最简单的注解类:public @interface MyAnnotation{}
2、元注解(注解的注解)
即在定义注解类的时候加注解。如两个常用于元注解的注解:Retention和Target 1)Retetion:用于说明注解保留在哪个阶段(即注解的生命周期)。 一个注解的生命周期包含:java源程序--(javac)-->class文件--(类加载器)-->内存中的字节码 分别对应Retetion这个枚举类的值: RetetionPolicy.SOURSE:java源文件时期,如@Overried和@SuppressWarning RetetionPolicy.CLASS: class文件时期(默认阶段) RetetionPolicy.RUNTIME:运行时期,如@Deprecated 如:在某注解类上加@Retention(RetentionPolicy.RUNTIME),表示此注解会一直存在。
注:
1、当在源程序上加了注解,javac将java源程序编译为class文件时,会对注解的生命周期进行判断。如果该注解只保留在源程序,则编译时会将该注解进行相应的处理操作,如去掉。其他类推。
2、class文件中不是字节码,只有把class文件中的内容加载进内存,用类加载器加载处理后(进行完整的检查等处理),最终得到的二进制内容才是字节码。
注意:其中代表类的值是TYPE。因为class、enum、interface和@interface等都是平级的,所以统属于Type。不可用CLASS表示。
3、注解的应用
通过反射方式来获取自定义的注解类,步骤跟注解的应用结构一致,
如: 第一、定义注解类:@interfaceA{} 第二、应用了“注解类”的类:@A class B{} 第三、对“应用注释类的类”进行反射操作的类:class c{...},操作如下: B.class.isAnnotionPresent(A.class);//判断是否存在此注解类 A a = B.class.getAnnotation(a.class);//存在的话则得到这个注释类的对象
示例:
五、为注解添加基本属性
1、属性:
一个注解相当于一个胸牌,但仅通过胸牌还不足以区别带胸牌的两个人,这时就需要给胸牌增加一个属性来区分,如颜色等。
2、定义格式:同接口中的方法一样:String color();
定义缺省格式:String value() default “heima”;
3、应用:直接在注解的括号中添加自身的属性,如:
@MyAnnotation(color=”red”)
1)如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@SuppressWarnings(“deprecation”)。
2)可以为属性值指定缺省值(default),应用时同样可以重新设置属性值。
3)用反射方式获得注解对应的实例对象后,可以通过该对象调用属性对应的方法来获取属性值。
六、为注解增加高级属性
1、可以为注解增加的高级属性的返回值类型有:
1)八种基本数据类型
2)String类型
3)Class类型
4)枚举类型
5)注解类型
6)前五种类型的数组
2、数组类型的属性:
如:int[]arrayArr() default {1,2,3};//可以不定义默认值
应用:@MyAnnotation(arrayArr={2,3,4}) //可重新赋值
注:若数组属性中只有一个元素(或重新赋值为一个元素),这时属性值部分可省略大括号。
3、枚举类型的属性:
假设定义了一个枚举类TrafficLamp,它是EnumTest的内部类,其值是交通灯的三色。
定义:EnumTest.TrafficLamplamp();
应用:@MyAnnotation(lamp=EnumTestTrafficLamp.GREEN)
4、注解类型的属性:
假定有个注解类:MetaAnnotation,其中定义了一个属性:String value()
定义:MetaAnnotation annotation() default @MetaAnnotation(”xxx”);
应用:@MyAnnotation(annotation=@MetaAnnotation(”yyy”))//重新赋值
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个
@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotationma =MyAnnotation.annotation();
System.out.println(ma.value());
5、Class类型的属性:
定义:Class cls();
应用:@MyAnnotation(cls=AnnotationDemo.class)
注:这里的.class必须是已定义的类,或是已有的字节码对象
6、注解的详细语法可通过查看java语言规范了解即javaLanguage Specification
示例: