反射,reflection,听其名就像照镜子一样,可以看见自己也可以看见别人。在java语言中这是一个很重要的特性。下面是来自sun公司官网关于反射的介绍:
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect(内省)" upon itself, and manipulate(操作) internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them. The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program. One tangible(实际的) use of reflection is in JavaBeans, where software components can be manipulated visually via(通过) a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded. 反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。这种能自我检查和操作内部属性的java类我们不常看到,但是在其他的比如C或者C++语言中很不就存在这个特性。像Pascal、C、C++无法获得内部方法的任何信息;一个实际运用是在JavaBean中,通过一个构造工具操作javaBean的各个组件,这个构造工具就是运用反射功能在javaBean动态加载时来获取他的属性。
要了解反射机制就必须对java.lang.Class有所了解,见18、19章;
先看看Class的构造器:
/* * Constructor. Only the Java Virtual Machine creates Class */ private Class() {}
注释很明确的告诉了我们,这个类是有JVM来创建的,所以我们就不用麻烦了。如果我们拿到一个类的类型信息,就可以利用反射获取其各种成员以及方法了。(注:Class 从JDK1.5版本后就开始更多为泛型服务了)那么我们怎么拿到一个类型的信息呢?假设我们有一个Role类:
package org.Smart; /* * a class Role has some methods and attribute */ public class Role { public Role(){} public Role(String s){} private String s; private int x; public void fun1(String s){ System.out.println(s); } public void fun2(){ System.out.println("ok"); } }
测试:
public class Sample { @SuppressWarnings("rawtypes") public static void main(String...s) { Class c=Role.class; Method[] m=c.getDeclaredMethods(); for(int x=0;x<m.length;x++){ System.out.println(m.toString()); } } }
获得Class 实例的几种方式:
Class cls1 = Role.class; Class cls2 = Class.forName("org.Smart.Role"); Role r=new Role(); Class cls3=r.getClass(); Class cls4= Boolean.TYPE; //仅适用基本数据类的包装类
注意第二种方式中,forName中的参数一定是完整的类名(包名+类名),并且这个方法需要捕获异常。现在得到cls1就可以创建一个Role类的实例了,利用Class的newInstance方法相当于调用类的默认的构造器
Class c; try { c = Class.forName("org.Smart.Role"); Role r=(Role) c.newInstance(); //等同Role r=new Role();利用newInstance()理解为Role的无参构造方法 r.fun2(); } catch (ClassNotFoundException e) { e.printStackTrace(); }
获得构造器:getConstructors/getDeclaredConstructors (不含私有/含私有)并可以设置为私有或公有的
获得属性:getFields/getDeclaredFields
获得方法:getMethods/getDeclaredMethods
调用类的方法:
Class c= Role.class; Object obj=c.newInstance(); Method fun=c.getDeclaredMethod("fun1", String.class); //若方法fun1没有参数写null,有参数写入参数类型 fun.invoke(obj, "e");//没有参数写null,有参数传参数值
设置属性值:
Field f=c.getDeclaredField("x"); f.setInt(obj, 32); //int x;必须是public声明的,setInt与int型对应;
讲到这里使用了java.lang.reflect包中的Method/Field/Constructor类,这些类都是接收Class.get()获得的方法/属性/构造器,还有一个最重要的运用:jvm核心之一Proxy代理,将在下章内容中讲解;
Class的其他重要方法:
static Class<?> forName(String name, boolean initialize, ClassLoader loader) //Class内部方法 static Class<?> forName(String className)//外部调用方法 <A extends Annotation>A getAnnotation(Class<A> annotationClass) Annotation[] getAnnotations() ClassLoader getClassLoader()//获取类加载器 Class<?>[] getInterfaces() //获取接口 String getName()//获取类名 Package getPackage() //获取包类型 Class<? super T> getSuperclass()//获取父类
下面接着讲注解:
JDK 5中引入了源代码中的注解(annotation)这一机制。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。
java注解又叫java标注,java提供了一套机制,使得我们可以对方法、类、参数、包、域以及变量等添加标准(即附上某些信息)。且在以后某个时段通过反射将标注的信息提取出来以供使用。
Java从1.5版本以后默认内置三个标注:
@Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。
@Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息,可以设定在程序里的所有的元素上.
@SuppressWarnings:这一个类型可以来暂时把一些警告信息消息关闭.
但是,仅仅这三个标注是不能满足我们开发时一些需求的。所以java允许我们自定义注解来使用。
从某种角度来说,可以把注解看成是一个XML元素,该元素可以有不同的预定义的属性。而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解,就相当于把一部分元数据从XML文件移到了代码本身之中,在一个地方管理和维护。
如何自己设计一个注解?
先看一下rt.jar下面的java.lang.annotation包:
Target.class
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { //@Target自己用自己来声明自己,只能用在ANNOTATION_TYPE之上 ElementType[] value(); } //Java中提供了四种元注解,专门负责注解其他的注解,分别如下 //@Documented将注解包含在JavaDoc中32 public @interface Documented { //@Inheried允许子类继承父类中的注解 //@Retention元注解,表示保存在什地方。可选的RetentionPoicy参数包括:public @interface Retention{ //RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉 public enum RetentionPolicy { //RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认) //RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息 //@Target元注解,表示使用在什么地方。可用的ElementType参数包括 //ElementType.CONSTRUCTOR: 构造器声明 public enum ElementType { //ElementType.FIELD: 成员变量、对象、属性(包括enum实例) //ElementType.LOCAL_VARIABLE: 局部变量声明 //ElementType.METHOD: 方法声明 //ElementType.PACKAGE: 包声明 //ElementType.PARAMETER: 参数声明 //ElementType.TYPE: 类、接口(包括注解类型)或enum声明 //ElementType.ANNOTATION_TYPE Annotation type declaration
@interface是一个关键字,在设计注解的时候必须把一个类型定义为@interface,而不能用class或interface关键字,@Retention用来声明注解的保留策略,有CLASS、RUNTIME和SOURCE这三种属性,分别表示注解保存在字节码.class文件、JVM运行时刻和源代码.java文件中。只有当声明为RUNTIME时,才能通过反射机制获取到注解信息。@Target用来声明注解可以添加哪些元素类型;
SOURCE代表的是这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译之后,Annotation的数据就会消失,并不会保留在编译好的.class文件里面。
ClASS的意思是这个Annotation类型的信息保留在程序源码里,同时也会保留在编译好的.class文件里面,在执行的时候,并不会把这一些信息加载到虚拟机(JVM)中去.注意一下,当你没有设定一个Annotation类型的Retention值时,系统默认值是CLASS.
RUNTIME,表示在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的.
@Documented的目的就是让这一个Annotation类型的信息能够显示在javaAPI说明文档上;没有添加的话,使用javadoc生成API文档的时候就会找不到这一个类型生成的信息.
如果需要把Annotation的数据继承给子类,那么就会用到@Inherited这一个Annotation类型.
我们在来看下@Override注解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }从上面可以得出:该注解只能在方法前使用,保存在源代码中,不会把@T...@R保存在.class文件中,但是通过反编译还是会打印出这两个注解?
使用@Override的时候只需要一个声明即可,而没有传递参数,称为标记注解(marker annotation ),代表某种配置语义。
使用@SuppressWarnings("xxxtype"),里面传递了一个参数;其实里面是一个名值对语句:value="xxxtype",省略了value=这个字符串;
所有的Annotation会自动继承java.lang.annotation这一个接口,所以不能再去继承别的类或是接口. ;只能用public或默认(default)这两个访问权限声明这个注解类;方法的返回值只能是基本类型(与注解传来的参数相接)byte,short,char,int,long,float,double,boolean和String,Enum,Class,annotations等数据类型,以及这一些类型的数组;如果只有一个成员变量,最好把变量名称设为"value",后加括号 String value(),这样注解时可以省略value这个字段,形式上是一个无参方法;多个成员变量后面都要加(),像int x();
自己设计一个Description注解类:
@Target(ElementType.TYPE) //使用在类前,注意后面没有;分号 @Retention(RetentionPolicy.RUNTIME) //保证在jvm中 public @interface Description { String info(); String str(); }
使用Description注解:
@Description(info="skx",str="good name!!") public class UseDescription { }
查看注解:
Class c=UseDescription.class; //获取UseDescription里面的@Description注解 Description obj=(Description) c.getAnnotation(Description.class); //获取Description.info方法的返回值 System.out.println(obj.info()); //skx注解处理过程: