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™ 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);
}
}
到这我们也就可以得出以下几点信息:
- Class类也是类的一种,与class关键字是不一样的。
- 手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件) 。
- 每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
- Class类只存私有构造函数,因此对应Class对象只能由JVM创建和加载。
- Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(关于反射稍后分析)。
(2.2.1) 类加载
(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 基础 - 反射机制详解