Java反射机制学习笔记

Java反射机制

简介

通过反射API可以获取程序在运行时刻的内部结构。反射API中提供的动态代理可以原生实现AOP中的方法拦截功能。通过反射获取到的Java类内部结构后再进行运用,和直接运用这个类效果相同,但额外的提供了运行时刻的灵活性。反射的最大一个弊端是性能比较差。相同的操作,用反射API所需的时间大概比直接的使用要慢一两个数量级。可以考虑在适当的时机来使用反射API。

基本用法

Java反射机制主要有两个作用。第一个主要作用是获取程序再运行时刻的内部结构。只需要少量的代码就能便利出一个Java类的内部结构来,其中包括构造方法、声明的域和定义的方法等。第二个主要的作用是在运行时刻对一个Java类进行操作。可以动态的创建一个Java类的对象,获取某个域的值以及调用某个方法。在Java源代码中编写的对类和对象的操作,都能通过反射来实现。

例如现在有一个简单的Java类:

class MyClass {
    public int count;
    public MyClass(int start) {
        count = start;
    }
    public void increase(int step) {
        count += step;
    }
}

通过一般的做法和反射API来操作类及其对象都非常的简单。

MyClass myClass = new MyClass(0);
myClass.increase(2);
System.out.println("Normal -> " + myClass.count);
try {
    Counstructor constructor = MyClass.class.getConstructor(int.class);  //获取构造方法
    MyClass myClassReflect = constructor.newInstance(10);  //创建对象
    Method method = MyClass.class.getMethod("increase", int.class);  //获取方法
    Field field = MyClass.class.getField("count");  //获取域,也就是变量
    System.out.println("Reflect -> " + field.getInt(myClassReflect)); //获取域的值
} catch (Exception e) {
    e.printStackTrace();
}

上面的例子中用到了四个反射的API:getConstructor()、newInstance()、getMethod()以及getField()。其中值得说明的是getMethod方法的一些注意事项。看下面的例子:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Pet {
private int age;
private String name;

public void eat() {
    System.out.println("eat.....");
}
private void sound() {
    System.out.println("sound.....");
}
}

class Dogge extends Pet {
private String gender;

public void run() {
    System.out.println("run.....");
}

@Override
public void eat() {
    System.out.println("eat meat.....");
}

private void bark() {
    System.out.println("woo.....");
}

public Dogge() {

}

public Dogge(String gender) {
    this.gender = gender;
}
}

public class JavaTest{

public static void main(String[] args) throws
InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException,
SecurityException,
NoSuchMethodException {

    Class<?> c1 = Dogge.class;
    Constructor<?>[] c = c1.getConstructors();
    Dogge d1 = (Dogge) c[1].newInstance("heheda");

    /**
     * eat方法是继承类重写之后的public方法
     * 可以通过reflect调用。
     */
    Method eat = c1.getMethod("eat") ;   //
    eat.invoke(d1);

    /**
     * sound方法是子类的private方法,通过普通的访问渠道调用不了
     * 但是通过reflect渠道(像这里直接reflect是不行的)能够调用,在下一个例子中将讲到。
     */
    //Method sound = c1.getDeclaredMethod("sound") ;
    //sound.invoke(d1);

    /**
     * bark方法在继承类中是private的
     * 这里如果用getMethod()方法来reflect该方法,是无法获取到的,原因在后面
     * 注意要用到setAccessible方法,因为是private方法,需要赋予权限。
     */
    Method bark = c1.getDeclaredMethod("bark") ;
    bark.setAccessible(true);
    bark.invoke(d1);

    /**
     * run方法在继承类中,是public方法
     * 直接reflect即可
     */
    Method run = c1.getMethod("run") ;
    run.invoke(d1);
    }
}
程序结果为:
eat meat.....
woo.....
run.....

上面的例子说明了通过普通的reflect能够调用到子类public,父类public,private(注意这个要设置响应的权限setAccessible(true))。下面就说明下为什么父类的private方法不能直接getMethod方法获得,而是要通过getDeclaredMethod()方法去反射得到,先来看看官方文档中对getMethod()的介绍,:

/**
 * Returns a Method object that reflects the specified public
 * member method of the class or interface represented by this
 * Class object. The name parameter is a
 * String specifying the simple name of the desired method. The
 * parameterTypes parameter is an array of Class
 * objects that identify the method‘s formal parameter types, in declared
 * order. If parameterTypes is null, it is
 * treated as if it were an empty array.
 *
 * Let C be the class represented by this object:
 * C is searched for any matching methods. If no matching
 *      method is found, the algorithm of step 1 is invoked recursively on
 *      the superclass of C.
 *  If no method was found in step 1 above, the superinterfaces of C
 *      are searched for a matching method. If any such method is found, it
 *      is reflected.
 *
 *
 * To find a matching method in a class C:&nbsp; If C declares exactly one
 * public method with the specified name and exactly the same formal
 * parameter types, that is the method reflected. If more than one such
 * method is found in C, and one of these methods has a return type that is
 * more specific than any of the others, that method is reflected;
 * otherwise one of the methods is chosen arbitrarily.
 *
 */
public Method getMethod(String name, Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {

// be very careful not to change the stack depth of this
// checkMemberAccess call for security reasons
// see java.lang.SecurityManager.checkMemberAccess

    checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
    两种方法在这一行出现了分歧
    Method method = getMethod0(name, parameterTypes);
    if (method == null) {
        throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
    }
    return method;
}

此处直接调用了native方法getMethod0方法,继续追踪代码就能发现:

private Method getMethod0(String name, Class[] parameterTypes) {
    // Note: the intent is that the search algorithm this routine
    // uses be equivalent to the ordering imposed by
    // privateGetPublicMethods(). It fetches only the declared
    // public methods for each class, however, to reduce the
    // number of Method objects which have to be created for the
    // common case where the method being requested is declared in
    // the class which is being queried.
    Method res = null;
    // Search declared public methods
    关键地方出现了,和下面的getDeclaredMethod方法可以进行对比,问题出现在该方法第一个参数的不同,此处是true,getDeclaredMethod方法此处的参数为false.
    if ((res = searchMethods(privateGetDeclaredMethods(true),
                             name,
                             parameterTypes)) != null) {
        return res;
    }
    // Search superclass‘s methods
    if (!isInterface()) {
        Class c = getSuperclass();
        if (c != null) {
            if ((res = c.getMethod0(name, parameterTypes)) != null) {
                return res;
            }
        }
    }
    // Search superinterfaces‘ methods
    Class[] interfaces = getInterfaces();
    for (int i = 0; i < interfaces.length; i++) {
        Class c = interfaces[i];
        if ((res = c.getMethod0(name, parameterTypes)) != null) {
            return res;
        }
    }
    // Not found
    return null;
}

下面是对getDeclaredMethod()的介绍:

/**
 * Returns a Method object that reflects the specified
 * declared method of the class or interface represented by this
 * Class object. The name parameter is a
 * String that specifies the simple name of the desired
 * method, and the parameterTypes parameter is an array of
 * Class objects that identify the method‘s formal parameter
 * types, in declared order.  If more than one method with the same
 * parameter types is declared in a class, and one of these methods has a
 * return type that is more specific than any of the others, that method is
 * returned; otherwise one of the methods is chosen arbitrarily.
 *
 */
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {

// be very careful not to change the stack depth of this
// checkMemberAccess call for security reasons
// see java.lang.SecurityManager.checkMemberAccess

    checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader());
    两种方法在这一行出现了分歧,这里调用的是privateGetDeclaredMethods(false),因此我们需要进一步去调查该方法。
    Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
    if (method == null) {
        throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
    }
    return method;
}

进入了privateGetDeclaredMethods()方法的定义处,其中的代码如下:

// Returns an array of "root" methods. These Method objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
// 接受传入参数的名称为publicOnly,因此能明白了为什么getMethod()方法无法反射出类中的private方法了。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
    checkInitted();
    Method[] res = null;
    if (useCaches) {
        clearCachesOnClassRedefinition();
        if (publicOnly) {
            //这个if里面的代码就是getMethod()方法调用时所执行的代码,将所有的public方法都返回了
            if (declaredPublicMethods != null) {
                res = (Method[]) declaredPublicMethods.get();
            }
        } else {
            //else里面的方法即在调用getDeclaredMethod方法时所执行的。返回了所有定义的方法。
            if (declaredMethods != null) {
                res = (Method[]) declaredMethods.get();
            }
        }
        if (res != null) return res;
    }
    // No cached value available; request value from VM
    res = getDeclaredMethods0(publicOnly);
    if (useCaches) {
        if (publicOnly) {
            declaredPublicMethods = new SoftReference(res);
        } else {
            declaredMethods = new SoftReference(res);
        }
    }
    return res;
}

看到这里,就应该差不多能弄明白了getMethod()getDeclaredMethod()方法之间的区别。

由于数组的特殊性,Array类提供了一系列的静态方法用来创建数组和对其中元素进行操作和访问。例如如下的操作:

Object array = Array.newInstance(String.class,10); //等价 new String[10]
Array.set(array,0,"Hello");
Array.set(array, 1, "world");
System.out.println(Array.get(array, 0));

使用Java反射API的时候可以绕过Java默认的访问控制检查,比如可以直接获取到对象的私有域的值或是调用私有方法。只需要在获取到Constructor、Field和Method类的对象之后,调用setAccessible方法并设为true即可。有了这种机制,就可以很方便的在运行时刻获取到程序的内部状态。

时间: 2024-10-19 03:47:13

Java反射机制学习笔记的相关文章

Thinking in Java---类型信息和java反射机制学习笔记

前面学习的多态给了我们一个很好的承诺:我们编写的代码只要与基类打交道,而不用为每一个新增加的子类写一份代码.但是这种思想在我们想要访问子类自己定义的方法时,就会有问题了.如下面的代码所示: class Base1{ void f(){ System.out.println("Base.f()"); } } class Sub extends Base1{ void f(){ System.out.println("Sub.f()"); } void g(){ Sys

java反射机制学习笔记及例子代码

Java 反射的API 反射中常常用的几个类如下所示: java.lang 包中: --Class 类:代表一个类 Java 中,无论生成某个类的多少个对象,这些对象都会对应同一个 Class 对象 Java.lang.reflect 包中: --Field 类:代表类的成员变量(类的属性) --Method 类:代表类的方法,一个方法对应一个 Method 对象 --Constructor 类:代表类的构造方法 --Array 类:提供动态创建数组,以及访问数组的元素的静态方法 利用反射机制调

JAVA的反射机制学习笔记(二)

上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了,自己的步伐完全被打乱了~不能继续被动下去,得重新找到自己的节奏. 4.获取类的Constructor 通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例 Class<T>类提供了几个方法获取类的构造器. public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反

JAVA反射机制—学习总结

最近收到很多关于Java反射机制的问题留言,其实Java反射机制技术方面没有太多难点,或许是大家在学习过程中遗漏了细小知识点,导致一些问题无法彻底理解,现在我们简单的总结一下,加深印象. 什么是反射机制? "JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制."我们通过一些例子,更好理解反射机制.Class类 我们知道Java是一门面向对象

JAVA反射机制学习随笔

JAVA反射机制是用于在运行时动态的获取类的信息或者方法,属性,也可以用来动态的生成类,由于所有类都是CLASS的子类,我们可以用一个CLASS类的实例来实例化各种类 例如: Class<?> class1 = null; Class<?> class2 = null; //写法1, 可能抛出 ClassNotFoundException [多用这个写法] class1 = Class.forName("cn.lee.demo.Person"); System.

Java反射机制学习

Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”. 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的.这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制. Java 反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类. 在运行时构造任意一个类的对象. 在运行时判断任意一个类所具有的成员变量和方法. 在运行时调用任意一个对象的

java反射机制学习:初始反射机制

本人小白一枚,想和大家一起分享我学习java的笔记和心得. 反射机制: 指的是可以于运行时加载.探知.使用编译期间完全未知的类. 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性: Class clazz = Class.forName(com.danchel.reflect.User); 加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象

黑马程序员---Java反射机制学习

由现在这个时间补上反射机制的学习笔记,本想报20期的JavaEE班,无奈真担心自己过不去,所以这段时间,一直的复习现在改报21期的吧!! 准备知识:一 1.Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method:    其中class代表的时类对象,    Constructor-类的构造器对象,    Field-类的属性对象,    Method-类的方法对象. 2.在Java中,每个class都有一个相应的Class对象.也就是说,当我们编写一

java反射机制学习小结

之前一直对java的反射机制理解得很模糊,今天因为学习spring,所以花了些时间总算把它理顺了,记录一下 另外,推荐读读这篇文章,写的挺好的http://blog.csdn.net/woshixuye/article/details/7700455 class Person { private String name; private int age; // public Person(String name,int age){ // this.name=name; // this.age=a