Java的反射

来源:互联网 发布:淘宝二手ps4主机能买吗 编辑:程序博客网 时间:2024/06/01 10:06
1.什么是反射
当我们在需要一个类的实例时,一般都是new一个对象出来,然后再在该对象上调用方法。假设目前我们有个Bird类:
public class Bird {    private static String master = "amy";    private String name;    public static void sayHello() {        System.out.println("Hello " + master);    }    public Bird(String name) {        this.name = name;    }    public Bird() {}    public void fly() {        System.out.println("bird fly");    }    private void eat() {        System.out.println();    }}
我们一般是这么使用:
Bird bird = new Bird();bird.fly();
输出:
bird fly
而利用java的反射,可以通过类的名称来创建对象,通过方法的名称来调用方法,如下所示:
Class<?> clazz = Class.forName("Bird");Object obj = clazz.newInstance();Method method = clazz.getMethod("fly");method.invoke(obj);
输出:
bird fly
从代码中可以看到,我们先是通过类的名称"Bird"获得类的类对象(即Bird类的Class对象),然后通过Class对象获取了代表类的fly方法的Method对象,最后通过Method对象的invoke方法调用了Bird类的fly方法。在这里,我们自始至终对Bird类毫无感知。

程序中对象的类型一般都是在编译时确定下来的,而通过java的反射机制,我们可以在运行时动态的获取类的信息,并生成类的对象并调用其方法。Java的反射机制主要提供以下功能:
  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  • 在运行时调用任意一个对象的方法
2.反射的原理
我们知道,java中任何一个类都会被编译成字节码文件,即.class文件,比如上例中的Bird.java文件,编译成功后生成Bird.class文件,类的所有信息都保存在它的.class文件中。当使用到某个类时,类的.class文件便会被jvm加载到内存中,jvm通过解析该.class文件,便可以获得该类的所有信息。

为了描述每个类的信息,java中用Class类来描述一个类的信息(注意:Class也是一个类, 即Class类是用来描述类的类),每个类的.class文件被jvm解析后,jvm就会为其创建一个Class对象(放置于jvm的方法区),这个对象里保存了这个类的所有信息。假设我们程序中有Bird、Fish这2个类,那么jvm将它们的.class文件加载后,jvm中就会有2个Class对象,一个描述了Bird类的信息,另一个描述了Fish这个类的信息。每个类在jvm中有且只有一个Class对象。只要拿到类的Class对象,我们就可以知道类有什么方法、有什么成员变量,也可以轻松的创建类的实例对象。反射的本质就是获取到类的Class对象。
3.反射的接口
3.1 获取类的class对象
(1)使用Class类的forName静态方法,如:
Class<?> clazz = Class.forName("Bird");
(2)调用类的.class方法,如:
Class<?> clazz = Bird.class; 
3)调用对象的getClass方法,如:
Bird bird = new Bird();Class<?> clazz = bird.getClass();
注意,方法(1)和方法(2)的区别是,方法(1)加载类时会执行类的初始化语句,而方法(2)不会。
3.2 判断对象是否是某个类的实例
调用Class对象的isInstance(Object)方法就可以判断某个对象是否是某个类的实例,如:
Class<?> clazz = Class.forName("Bird");Bird bird = new Bird();String str = "Bird";System.out.println(clazz.isInstance(obj));System.out.println(clazz.isInstance(str));
输出:
true
false
3.3 获取类的构造函数
Java中用Constructor类来描述一个类的构造函数。通过Class对象的以下方法,就可以获得一个类的构造函数:
Constructor getConstructor(Class[] params); --------- 获取类的某个特定的公有构造函数,
Constructor[] getConstructors(); ---------------------- 获取类的所有公有构造函数
Constructor getDeclaredConstructor(Class[] params); - 获取类的某个特定的构造函数,不论是什么访问级别
都可以 Constructor[] getDeclaredConstructors(); -- -----获取类的所有构造函数,所有访问级别的都返回
假设clazz是Bird类的Class对象,以下方法返回Bird(String name)这个构造函数:
Constructor ctr = clazz.getConstructor(String.class);
3.4 创建对象
(1)调用Class对象的newInstance方法来创建实例
Object obj = clazz.newInstance();
该方法要求类具有不含参数的构造函数。
(2)调用Constructor对象的的newInstance()方法来创建实例
Class<?> clazz = Class.forName("Bird");Constructor ctr = clazz.getConstructor(String.class);Object bird = ctr.newInstance("Big");
该方法可以通过指定的构造函数来创建实例。
3.5 获取类的方法
Java中用Method类来描述类的一个方法,类的每个方法都对应一个Method对象,可以调用Class对象的以下方法来获取类的方法:
Method getMethod(String name, Class[] params); ---------- 获取类的某个特定的公有方法
Method[] getMethods(); ------------------------------------ 获取类的所有公共方法,包括继承而来的公有方法
Method getDeclaredMethod(String name, Class[] params) -- 获取特写的某个方法,不区分访问类型
Method[] getDeclaredMethods() ------------ 获取类的所有方法(不论什么访问权限),不包括继承而来的方法
注意,静态方法也会返回。
如:
Class<?> clazz = Class.forName("Bird");Method[] methods = clazz.getMethods();for (Method method1 : methods) {    System.out.println(method1);}
输出为:
public void Bird.fly()public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionpublic final void java.lang.Object.wait() throws java.lang.InterruptedExceptionpublic boolean java.lang.Object.equals(java.lang.Object)public java.lang.String java.lang.Object.toString()public native int java.lang.Object.hashCode()public final native java.lang.Class java.lang.Object.getClass()public final native void java.lang.Object.notify()public final native void java.lang.Object.notifyAll()
上述方法获取了Bird类的所有方法,包括从Object类继承的方法,但并没有返回Bird类的私有方法eat()。如果调用的是getDeclaredMethods()方法,则输出为:
public void Bird.fly()private void Bird.eat()
getDeclaredMethods()方法返回类的所有方法,不论是公有的还是私有的,但不包括继承而来的方法。
3.6 获取类的成员变量
Java中用Filed类来描述类的一个成员变量,类的每个成员变量都对应一个Filed方法。可以调用Class对象的如下方法,以获取类的成员变量:
Field getField(String name); ---------- 获取某个特定的公有成员变量;
Field[] getFields(); ------------------- 获取类的所有公有成员变量;
Field getDeclaredField(String name) -- 获取类的某个特定成员变量,不区分访问类型;
Field[] getDeclaredFields() -- ----------获取类的所有成员变量,不区分访问类型
注意,static变量也会返回。
如下:
Class<?> clazz = Class.forName("Bird");Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {    System.out.println(field);}
输出为:
private static java.lang.String Bird.masterprivate java.lang.String Bird.name
3.7 调用方法
获取到类的某个方法的Method对象后,就可以调用该方法了:
String className = "Bird";String methodName = "fly";Class<?> clazz = Class.forName(className);Object obj = clazz.newInstance();Method method = clazz.getMethod(methodName);method.invoke(obj);
输出:
bird fly