JAVA反射浅析

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

就是说在运行时,我们可以写一个程序,这个程序能够获取任意一个类的各种信息,对于对象,我们还可以调用它的方法,看起来有点牛逼,上帝之手,不同于通常我们new对象然后调用方法的地方就是这是动态的。

Java提供了一个反射库java.lang.reflect


还有一个位于java.lang包中的Class类,下面解释一下这个类,这个类是反射中不可少的一环。

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。JVM利用运行时类型信息选择相应的方法执行。

而保存这些信息的类就是Class类。

一个Class对象就将表示一个特定类的属性。

那么我们要怎么在程序中获得某各类的Class对象呢?Java提供了三种手段。

①所有类的父类Object类有个getClass()方法


源码注释如下

    /**
     * Returns the runtime class of this {@code Object}. The returned
     * {@code Class} object is the object that is locked by {@code
     * static synchronized} methods of the represented class.
     *
     * <p><b>The actual result type is {@code Class<? extends |X|>}
     * where {@code |X|} is the erasure of the static type of the
     * expression on which {@code getClass} is called.</b> For
     * example, no cast is required in this code fragment:</p>
     *
     * <p>
     * {@code Number n = 0;                             }<br>
     * {@code Class<? extends Number> c = n.getClass(); }
     * </p>
     *
     * @return The {@code Class} object that represents the runtime
     *         class of this object.
     * @jls 15.8.2 Class Literals
     */
    public final native Class<?> getClass();

②Class类有个重载的静态方法forName(),这里列出最简单的形式


也看下源码

    /**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName(className, true, currentLoader)}
     * </blockquote>
     *
     * where {@code currentLoader} denotes the defining class loader of
     * the current class.
     *
     * <p> For example, the following code fragment returns the
     * runtime {@code Class} descriptor for the class named
     * {@code java.lang.Thread}:
     *
     * <blockquote>
     *   {@code Class t = Class.forName("java.lang.Thread")}
     * </blockquote>
     * <p>
     * A call to {@code forName("X")} causes the class named
     * {@code X} to be initialized.
     *
     * @param      className   the fully qualified name of the desired class.
     * @return     the {@code Class} object for the class with the
     *             specified name.
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located
     */
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

关键的还是forName0()这个方法

    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
        throws ClassNotFoundException;

    /**
     * Creates a new instance of the class represented by this {@code Class}
     * object.  The class is instantiated as if by a {@code new}
     * expression with an empty argument list.  The class is initialized if it
     * has not already been initialized.
     *
     * <p>Note that this method propagates any exception thrown by the
     * nullary constructor, including a checked exception.  Use of
     * this method effectively bypasses the compile-time exception
     * checking that would otherwise be performed by the compiler.
     * The {@link
     * java.lang.reflect.Constructor#newInstance(java.lang.Object...)
     * Constructor.newInstance} method avoids this problem by wrapping
     * any exception thrown by the constructor in a (checked) {@link
     * java.lang.reflect.InvocationTargetException}.
     *
     * @return  a newly allocated instance of the class represented by this
     *          object.
     * @throws  IllegalAccessException  if the class or its nullary
     *          constructor is not accessible.
     * @throws  InstantiationException
     *          if this {@code Class} represents an abstract class,
     *          an interface, an array class, a primitive type, or void;
     *          or if the class has no nullary constructor;
     *          or if the instantiation fails for some other reason.
     * @throws  ExceptionInInitializerError if the initialization
     *          provoked by this method fails.
     * @throws  SecurityException
     *          If a security manager, <i>s</i>, is present and
     *          the caller's class loader is not the same as or an
     *          ancestor of the class loader for the current class and
     *          invocation of {@link SecurityManager#checkPackageAccess
     *          s.checkPackageAccess()} denies access to the package
     *          of this class.
     */
    @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException
    {
        if (System.getSecurityManager() != null) {
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }

        // NOTE: the following code may not be strictly correct under
        // the current Java memory model.

        // Constructor lookup
        if (cachedConstructor == null) {
            if (this == Class.class) {
                throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {
                Class<?>[] empty = {};
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // Disable accessibility checks on the constructor
                // since we have to do the security check here anyway
                // (the stack depth is wrong for the Constructor's
                // security check to work)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                c.setAccessible(true);
                                return null;
                            }
                        });
                cachedConstructor = c;
            } catch (NoSuchMethodException e) {
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers();
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
        try {
            return tmpConstructor.newInstance((Object[])null);
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }

③如果T是 任意的Java类型(或者void关键字),那么T.class将代表匹配的类对象。

上面三种方法我做下试验,我们写一个Student类和一个测试的main方法

package dailyprg0714;

public class Student {
    private String name;

    public Student(String name) {
		super();
		this.name = name;
	}

	public void say(){
        System.out.println("Hello, I'm "+name+" !");
    }
}
package dailyprg0714;

public class Test {
    public static void main(String[] args) {
        Student student = new Student("Jenny");

        Class class1 = student.getClass();
        System.out.println(class1.toString());

        try {
            Class class2 = Class.forName("dailyprg0714.Student");
            System.out.println(class2.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

        Class class3 = Student.class;
        System.out.println(class3.toString());
    }
}

我们运行一下,


三种方法都得到了Class对象。

JVM为每个类型管理一个Class对象,也只有一个,我们可以看一下上面获得的三个对象是不是同一个,我们添加两行代码

System.out.println(class1==class2);
System.out.println(class1==class3);

结果都为true,也就是说这个对象是唯一的


获得了这个对象之后我们就能调用Class类提供的方法来分析类了,

在java.lang.reflect包中有三个类FieldMethodConstructor,从名字也能看出来它们分别用于描述类的域、方法和构造器。

而Class类中的getFields、getMethods和getConstructors方法将分别返回类的public域、方法和构造器数组,其中包括超类的公有成员。

另外Class类的getDeclaredFields、getDeclaredMethods和getDeclaredConstructors方法分别返回类中声明的全部域、方法和构造器,包括私有和受保护的,但不包括超类成员。

我们还是拿Student类试验一下,修改下main的代码

package dailyprg0714;

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

public class Test {
    public static void main(String[] args) {
        Student student = new Student("Jenny");

        Class class1 = student.getClass();
        System.out.println(class1.toString());

        try {
            Class class2 = Class.forName("dailyprg0714.Student");
            System.out.println(class2.toString());
            System.out.println(class1==class2);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Class class3 = Student.class;
        System.out.println(class3.toString());
        System.out.println(class1==class3);
        System.out.println();

        Field[] fields = class1.getDeclaredFields();
        Method[] methods = class1.getDeclaredMethods();
        Constructor[] constructors = class1.getConstructors();

        for(Field field : fields){
            System.out.println(field.getName());
        }
        for(Method method : methods){
            System.out.println(method.getName());
        }
        for(Constructor constructor : constructors){
            System.out.println(constructor.getName());
        }
    }
}

看下输出的结果


当然还能调用Field、Method和Constructor的其他方法,不止是getName来获得更多的信息,具体的还是看源码或者官方文档哈。只要访问权限允许,我们完全可以获得一个Student对象的实例域的具体值,甚至可以修改它的值。对于私有域需要用到

setAccessible方法来设置访问权限。

当然,我们也可以调用任意一个方法,这里的关键就是Method类提供的invoke()方法,它允许调用包装在当前Method对象中的方法。invoke的源码

    @CallerSensitive
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

我们也试一下调用student的say方法,在Method遍历的时候加入一段代码,用于执行student这个对象的say方法,如果say是Student类的静态方法那么就不需要传入这个参数了

        for(Method method : methods){
            System.out.println(method.getName());
            try {
                method.invoke(student);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
我们看下结果


OK!

反射的更多功能就要去研究反射库了,看下有哪些方法!



猜你喜欢

转载自blog.csdn.net/whut2010hj/article/details/81045864