Java 反射 笔记

在提高代码的抽象性、可扩展性时,经常需要动态地获取一些类、方法或者属性。这个时候往往需要通过反射去获取。
比如 加载数据库驱动、打印日志获取类、Spring IOC加载Bean 等。

Class.forName("com.mysql.jdbc.Driver");
private final Logger log = LoggerFactory.getLogger(getClass());

(1) 反射

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以构造任意一个对象所属的类,可以构造任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

反射被视为动态语言的关键。

(1.1) 反射使用

对比一下日常创建对象和用反射创建对象

日常创建一个UserModel类

public static void main(String[] args) {
    // 正常使用
    UserModel userModel = new UserModel();
    userModel.setId(1L);
    userModel.setUserName("张三");
    System.out.println(userModel); // UserModel{id=1, userName='张三'}
}

 
通过反射创建UserModel类

public static void main(String[] args)  throws Exception {
        // 通过反射获取类的 Class 对象实例
        Class clz = Class.forName("cn.wkq.java.reflection.UserModel");
        // 根据 Class 对象实例获取 Constructor 对象
        Constructor constructor = clz.getConstructor();
        // 使用 Constructor 对象的 newInstance 方法获取反射类对象
        Object object = constructor.newInstance();
        System.out.println(object); // UserModel{id=null, userName='null'}
        // 获取方法的 Method 对象 
        Method method = clz.getMethod("setId", Long.class);
        // 利用 invoke 方法调用方法
        method.invoke(object, 4L);
        System.out.println(object); // UserModel{id=4, userName='null'}
}

 

package cn.wkq.java.reflection;

/**
 * @author weikeqin
 */
public class UserModel {

    /**
     * 用户ID
     */
    private Long id;

    /**
     * 用户名
     */
    private String userName;

    // 省略 getter setter

    @Override
    public String toString() {
        return "UserModel{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                '}';
    }
}

 

(1.2) 反射的优缺点

优点

在运行时获得类的各种内容,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

缺点

反射会消耗一定的系统资源,频繁调用性能较差
反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

 

(2) 反射基础

Class 和 java.lang.reflect 一起对反射提供了支持。
java.lang.reflect 类库主要包含了以下三个类:

// Constructor : 可以用 Constructor 创建新的对象。
Java.lang.reflect.Constructor;  

// Field : 可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; 
Java.lang.reflect.Field;        

// Method : 可以使用 invoke() 方法调用与 Method 对象关联的方法; 
Java.lang.reflect.Method;       

 

(2.1) Class类

Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。
Class类的实例表示java应用运行时的类(class、enum)或接口(interface、annotation)。
数组同样也被映射为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象。

/**
 * Instances of the class {@code Class} represent classes and
 * interfaces in a running Java application.  An enum is a kind of
 * class and an annotation is a kind of interface.  Every array also
 * belongs to a class that is reflected as a {@code Class} object
 * that is shared by all arrays with the same element type and number
 * of dimensions.  The primitive Java types ({@code boolean},
 * {@code byte}, {@code char}, {@code short},
 * {@code int}, {@code long}, {@code float}, and
 * {@code double}), and the keyword {@code void} are also
 * represented as {@code Class} objects.
 *
 * <p> {@code Class} has no public constructor. Instead {@code Class}
 * objects are constructed automatically by the Java Virtual Machine as classes
 * are loaded and by calls to the {@code defineClass} method in the class
 * loader.
 *
 * <p> The following example uses a {@code Class} object to print the
 * class name of an object:
 *
 * <blockquote><pre>
 *     void printClassName(Object obj) {
 *         System.out.println("The class of " + obj +
 *                            " is " + obj.getClass().getName());
 *     }
 * </pre></blockquote>
 *
 * <p> It is also possible to get the {@code Class} object for a named
 * type (or for void) using a class literal.  See Section 15.8.2 of
 * <cite>The Java&trade; Language Specification</cite>.
 * For example:
 *
 * <blockquote>
 *     {@code System.out.println("The name of class Foo is: "+Foo.class.getName());}
 * </blockquote>
 *
 * @param <T> the type of the class modeled by this {@code Class}
 * object.  For example, the type of {@code String.class} is {@code
 * Class<String>}.  Use {@code Class<?>} if the class being modeled is
 * unknown.
 *
 * @author  unascribed
 * @see     java.lang.ClassLoader#defineClass(byte[], int, int)
 * @since   JDK1.0
 */
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.
     * This constructor is not used and prevents the default constructor being
     * generated.
     */
    private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }

    /**
     * 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);
    }

}

到这我们也就可以得出以下几点信息:

  1. Class类也是类的一种,与class关键字是不一样的。
  2. 手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件) 。
  3. 每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
  4. Class类只存私有构造函数,因此对应Class对象只能由JVM创建和加载。
  5. Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(关于反射稍后分析)。

(2.2.1) 类加载

 Java类加载机制

 Java类字节码详解

Java类生命周期

(2.2.2) Class对象常用方法

方法名 说明 用例
forName() 1.获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
2. 为了产生Class引用,forName()立即就进行了初始化。
Class.forName("com.mysql.jdbc.Driver");
getClass() 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。 LoggerFactory.getLogger(getClass());
getName() 取全限定的类名(包括包名),即类的完整名字。
isInterface() 判断Class对象是否是表示一个接口
getInterfaces() 获取接口
getSupercalss() 获取父类
newInstance() 创建实例
getFields() 获取公有方法
getDeclaredFields() 获取所有方法(包括私有方法)
getConstructor() 获取构造函数

(2.2) Constructor类及其用法

/**
 * {@code Constructor} provides information about, and access to, a single
 * constructor for a class.
 *
 * <p>{@code Constructor} permits widening conversions to occur when matching the
 * actual parameters to newInstance() with the underlying
 * constructor's formal parameters, but throws an
 * {@code IllegalArgumentException} if a narrowing conversion would occur.
 *
 * @param <T> the class in which the constructor is declared
 *
 * @see Member
 * @see java.lang.Class
 * @see java.lang.Class#getConstructors()
 * @see java.lang.Class#getConstructor(Class[])
 * @see java.lang.Class#getDeclaredConstructors()
 *
 * @author      Kenneth Russell
 * @author      Nakul Saraiya
 */
public final class Constructor<T> extends Executable {
}

(2.3) Method类及其用法

/**
 * A {@code Method} provides information about, and access to, a single method
 * on a class or interface.  The reflected method may be a class method
 * or an instance method (including an abstract method).
 *
 * <p>A {@code Method} permits widening conversions to occur when matching the
 * actual parameters to invoke with the underlying method's formal
 * parameters, but it throws an {@code IllegalArgumentException} if a
 * narrowing conversion would occur.
 *
 * @see Member
 * @see java.lang.Class
 * @see java.lang.Class#getMethods()
 * @see java.lang.Class#getMethod(String, Class[])
 * @see java.lang.Class#getDeclaredMethods()
 * @see java.lang.Class#getDeclaredMethod(String, Class[])
 *
 * @author Kenneth Russell
 * @author Nakul Saraiya
 */
public final class Method extends Executable {
}

(2.4) Field类及其用法

/**
 * A {@code Field} provides information about, and dynamic access to, a
 * single field of a class or an interface.  The reflected field may
 * be a class (static) field or an instance field.
 *
 * <p>A {@code Field} permits widening conversions to occur during a get or
 * set access operation, but throws an {@code IllegalArgumentException} if a
 * narrowing conversion would occur.
 *
 * @see Member
 * @see java.lang.Class
 * @see java.lang.Class#getFields()
 * @see java.lang.Class#getField(String)
 * @see java.lang.Class#getDeclaredFields()
 * @see java.lang.Class#getDeclaredField(String)
 *
 * @author Kenneth Russell
 * @author Nakul Saraiya
 */
public final
class Field extends AccessibleObject implements Member {
}

(3) 反射使用

https://docs.oracle.com/javase/8/docs/api/

反射机制常用的类

Class 和 java.lang.reflect 一起对反射提供了支持
java.lang.reflect 类库主要包含了以下三个类:

// Constructor : 可以用 Constructor 创建新的对象。
Java.lang.reflect.Constructor;  

// Field : 可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; 
Java.lang.reflect.Field;        

// Method : 可以使用 invoke() 方法调用与 Method 对象关联的方法; 
Java.lang.reflect.Method;       

(3.1) Class对象获取

获取反射中的Class对象有3种方法

1. 使用 Class.forName 静态方法
当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class.forName("com.mysql.jdbc.Driver");

 
2. 使用 .class 方法
这种方法只适合在编译前就知道操作的 Class。

Class clz = String.class;

 
3. 使用类对象的 getClass() 方法

private final Logger log = LoggerFactory.getLogger(getClass());

(3.2) 通过反射创建类对象

1. 通过Class对象的newInstance()方法创建类对象

// 通过`Class`对象的`newInstance()`方法创建类对象
Class clz = UserModel.class;
UserModel userModel = (UserModel) clz.newInstance();

 
2. 通过Constructor对象的newInstance()方法创建类对象

// 通过`Constructor`对象的`newInstance()`方法创建类对象
Class clz = UserModel.class;
Constructor constructor = clz.getConstructor();
UserModel userModel = (UserModel) constructor.newInstance();

 

(3.3) 通过反射获取类的构造器

1. 获取类的构造器

通过 getConstructor() 方法获取类的构造器 入参选填(入参不同,获取的构造器不同)

Class clz = UserModel.class;
Constructor c = clz.getConstructor();
System.out.println(c); // public cn.wkq.java.reflection.UserModel()

对应的构造方法是

public UserModel() {
}

 

2. 获取类的指定构造器

Class clz = UserModel.class;
Constructor c2 = clz.getConstructor(String.class);
System.out.println(c2); // public cn.wkq.java.reflection.UserModel(java.lang.String)

对应的构造方法是

public UserModel(String userName) {
    this.userName = userName;
}

 

(3.4) 通过反射获取类属性

1. 获取类的属性

通过 Class 对象的 getFields() 方法可以获取 Class 类的属性
备注: 无法获取私有属性。

Class clz = UserModel.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
    System.out.println(field.getName());  // remark
}

2. 获取类的所有属性(包括私有属性)
使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性

Class clz = UserModel.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
    // id
    // userName
    // remark
}

(3.5) 通过反射获取类的方法

获取类的方法

Class clz = UserModel.class;
Method method = clz.getMethod("setId", Long.class);
System.out.println(method); // public void cn.wkq.java.reflection.UserModel.setId(java.lang.Long)

(3.6) 获取类名

String name = clz.getName();
System.out.println(name);
String simpleName = clz.getSimpleName();
System.out.println(simpleName); // UserModel

 

(4) 反射机制执行流程

public static void main(String[] args)  throws Exception {
        // 通过反射获取类的 Class 对象实例
        Class clz = Class.forName("cn.wkq.java.reflection.UserModel");
        // 根据 Class 对象实例获取 Constructor 对象
        Constructor constructor = clz.getConstructor();
        // 使用 Constructor 对象的 newInstance 方法获取反射类对象
        Object object = constructor.newInstance();
        System.out.println(object); // UserModel{id=null, userName='null'}
        // 获取方法的 Method 对象 
        Method method = clz.getMethod("setId", Long.class);
        // 利用 invoke 方法调用方法
        method.invoke(object, 4L);
        System.out.println(object); // UserModel{id=4, userName='null'}
}

 
反射机制执行流程

(4.1) 反射获取类实例 Class.forName()

Class.forName()反射获取类信息,实现是一个native方法 。

源码位置:java.lang.Class

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    // 通过反射获取调用进来的类信息,从而获取当前的 ClassLoader
    Class<?> caller = Reflection.getCallerClass();
    // 调用native方法进行获取Class信息
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
                                        ClassLoader loader,
                                        Class<?> caller)
    throws ClassNotFoundException;

源码位置: java.lang.ClassLoader

// Returns the class's class loader, or null if none.
static ClassLoader getClassLoader(Class<?> caller) {
    // This can be null if the VM is requesting it
    if (caller == null) {
        return null;
    }
    // Circumvent security check since this is package-private
    return caller.getClassLoader0();
}

(4.2) 反射获取构造方法 clz.getConstructor()

@CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {
    // 检查访问权限是否是public
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    // 获取public权限的构造方法
    return getConstructor0(parameterTypes, Member.PUBLIC);
}
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                    int which) throws NoSuchMethodException
{
    Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
    for (Constructor<T> constructor : constructors) {
        if (arrayContentsEq(parameterTypes,
                            constructor.getParameterTypes())) {
            return getReflectionFactory().copyConstructor(constructor);
        }
    }
    throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}

 

//
//
// java.lang.reflect.Constructor handling
//
//

// Returns an array of "root" constructors. These Constructor
// objects must NOT be propagated to the outside world, but must
// instead be copied via ReflectionFactory.copyConstructor.
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
    checkInitted();
    Constructor<T>[] res;
    // 反射数据
    ReflectionData<T> rd = reflectionData();
    if (rd != null) {
        // 判断返回 public构造方法 还是 声明的构造方法 
        res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
        if (res != null) return res;
    }
    // No cached value available; request value from VM
    if (isInterface()) {
        @SuppressWarnings("unchecked")
        Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
        res = temporaryRes;
    } else {
        // 
        res = getDeclaredConstructors0(publicOnly);
    }
    if (rd != null) {
        if (publicOnly) {
            rd.publicConstructors = res;
        } else {
            rd.declaredConstructors = res;
        }
    }
    return res;
}
// reflection data that might get invalidated when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
    volatile Field[] declaredFields;
    volatile Field[] publicFields;
    volatile Method[] declaredMethods;
    volatile Method[] publicMethods;
    volatile Constructor<T>[] declaredConstructors;
    volatile Constructor<T>[] publicConstructors;
    // Intermediate results for getFields and getMethods
    volatile Field[] declaredPublicFields;
    volatile Method[] declaredPublicMethods;
    volatile Class<?>[] interfaces;

    // Value of classRedefinedCount when we created this ReflectionData instance
    final int redefinedCount;

    ReflectionData(int redefinedCount) {
        this.redefinedCount = redefinedCount;
    }
}
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
    SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
    int classRedefinedCount = this.classRedefinedCount;
    ReflectionData<T> rd;
    // 使用缓存的信息
    if (useCaches &&
        reflectionData != null &&
        (rd = reflectionData.get()) != null &&
        rd.redefinedCount == classRedefinedCount) {
        return rd;
    }
    // 
    // else no SoftReference or cleared SoftReference or stale ReflectionData
    // -> create and replace new instance
    return newReflectionData(reflectionData, classRedefinedCount);
}
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                            int classRedefinedCount) {
    if (!useCaches) return null;

    while (true) {
        ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
        // try to CAS it...
        if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
            return rd;
        }
        // else retry
        oldReflectionData = this.reflectionData;
        classRedefinedCount = this.classRedefinedCount;
        if (oldReflectionData != null &&
            (rd = oldReflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
    }
}

 

(4.3) 使用Constructor对象的newInstance方法获取反射类对象 constructor.newInstance()

@CallerSensitive
public T newInstance(Object ... initargs)
    throws InstantiationException, IllegalAccessException,
           IllegalArgumentException, InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, null, modifiers);
        }
    }
    if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        throw new IllegalArgumentException("Cannot reflectively create enum objects");
    ConstructorAccessor ca = constructorAccessor;   // read volatile
    if (ca == null) {
        ca = acquireConstructorAccessor();
    }
    @SuppressWarnings("unchecked")
    T inst = (T) ca.newInstance(initargs);
    return inst;
}

(4.4) 反射获取方法 clz.getMethod()

@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    Method method = getMethod0(name, parameterTypes, true);
    if (method == null) {
        throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
    }
    return method;
}
private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
    MethodArray interfaceCandidates = new MethodArray(2);
    Method res =  privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
    if (res != null)
        return res;

    // Not found on class or superclass directly
    interfaceCandidates.removeLessSpecifics();
    return interfaceCandidates.getFirst(); // may be null
}

(4.5) 调用方法 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);
}

 

// NOTE that there is no synchronization used here. It is correct
// (though not efficient) to generate more than one MethodAccessor
// for a given Method. However, avoiding synchronization will
// probably make the implementation more scalable.
private MethodAccessor acquireMethodAccessor() {
    // First check to see if one has been created yet, and take it
    // if so
    MethodAccessor tmp = null;
    if (root != null) tmp = root.getMethodAccessor();
    if (tmp != null) {
        methodAccessor = tmp;
    } else {
        // Otherwise fabricate one and propagate it up to the root
        tmp = reflectionFactory.newMethodAccessor(this);
        setMethodAccessor(tmp);
    }

    return tmp;
}
public MethodAccessor newMethodAccessor(Method var1) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
        return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
    } else {
        NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
        DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
        var2.setParent(var3);
        return var3;
    }
}

其他

使用反射会遇到的问题

// java.lang.ClassNotFoundException: cn.wkq.java.reflection.UserModel2
Class clz = Class.forName("cn.wkq.java.reflection.UserModel2");

// java.lang.NoSuchMethodException: cn.wkq.java.reflection.UserModel.setId(long)
Method method = clz.getMethod("setId", long.class);

Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
 
// Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
method.invoke(object, 2);

参考

[1] 大白话说Java反射:入门、使用、原理
[2] Java基础篇:反射机制详解
[3] Java 基础 - 反射机制详解