反射机制是Java中的一个难点,反射机制也是各个框架的核心,个人认为正是反射机制的存在,才显得Java的OOP特性的强大,这篇博客中,笔者会基于自己的理解和阅读的书籍以及博客,做一个比较详细的介绍,剖析底层实现原理(仅供参考),如有存在错误的地方,欢迎指正。
Java反射机制
Java反射机制是在运行状态中,对任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象的方法的功能称为Java语言的反射机制。
反射机制主要提供的功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法。
反射机制原理大致流程如下
现在对反射机制的有一个大致的了解后,我们现在要深入剖析其内部实现。
反射的一切基础都是围绕着Class类对象展开的,而Class对象就是用过.class文件获得的。Java中使用Class类表示某个.class文件并且任何一个.class文件都是Class这个类的一个实例对象。
接上面的原理图后,我们现在以.class文件开始,展开介绍底层实现反射的剖析
接下来我们将按照上面的流程,分别展开具体的介绍
获取Class对象
三种方式获取
(1)通过类名.class获取
public class Test {
public static void main(String[] args) {
// 获取Person的Class对象
Class demo = Person.class;
// 输出:class com.llm.test.Person
System.out.println(demo);
}
}
(2)Object类的成员方法getClass()方法获取
public class Test {
public static void main(String[] args) {
// 实例化Person对象
Person person = new Person();
// 获取Person的Class对象
Class demo = person.getClass();
// 输出:class com.llm.test.Person
System.out.println(demo);
}
}
(3)通过Class.forName(“全限定类名”)方法获取
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class demo = Class.forName("com.llm.test.Person");
// 输出:class com.llm.test.Person
System.out.println(demo);
}
}
Class对象的常用方法:
// 获得类名
public String getSimpleName();
// 获取完整类名,包名+类名
public String getName();
// 获取Class的一个实例
// 必须要有public的无参构造函数,否则无法实例化
// 但是java9中已经不推荐使用该方法了
// 使用 类对象.getDeclaredConstructor().newInstance()代替
public T newInstance();
测试如下:
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class demo = Class.forName("com.llm.test.Person");
System.out.println(demo.getSimpleName()); // 输出:Person
System.out.println(demo.getName()); // 输出:com.llm.test.Person
System.out.println(demo.newInstance()); // 输出:com.llm.test.Person@b4c966a
System.out.println(demo.getConstructor().newInstance()); // 输出:com.llm.test.Person@b4c966a
}
}
Constructor类的获取
该类是构造方法的类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化读反射类的对象,利用Class对象的方法获取。
主要有以下四中方式获取:
// 根据参数获取构造方法对象,只获取public修饰的
public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 根据参数获取构造方法对象,包括private修饰的
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
// 获取所有public方法
public Constructor<?>[] getConstructors()
// 获取所有构造方法包含private
public Constructor<?>[] getDeclaredConstructors()
举例代码:
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class demo = Class.forName("com.llm.test.Person");
// 输出 : public com.llm.test.Person(java.lang.String,int)
System.out.println(demo.getConstructor(String.class,int.class));
// 输出:private com.llm.test.Person(java.lang.String)
System.out.println(demo.getDeclaredConstructor(String.class));
// 输出:
// public com.llm.test.Person(java.lang.String,int)
//public com.llm.test.Person()
for (Constructor constructor : demo.getConstructors()) {
System.out.println(constructor);
}
// 输出:
// private com.llm.test.Person(java.lang.String)
//public com.llm.test.Person(java.lang.String,int)
//public com.llm.test.Person()
for (Constructor declaredConstructor : demo.getDeclaredConstructors()) {
System.out.println(declaredConstructor);
}
}
}
Constructor类常用方法
比较简单就不举例了
// 创建对象实例
public T newInstance(Object ... initargs)
// flag为true可以直接访问私有类型的构造方法,反之则不可
public void setAccessible(boolean flag)
Method类
Method类是反射类的所有成员方法的,与Constructor类同样的原理使用。也是利用Class对象获取。
获取方法:
// 根据方法名和参数类型获取一个方法对象,只有public方法
public Method getMethod(String name, Class<?>... parameterTypes)
// 根据方法名和参数类型获取所有public修饰的方法,包括父类的public方法
public Method[] getMethods()
// 根据方法名和参数类型获取一个方法对象,可以获取private修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
// 根据方法名和参数类型获取所有的方法,包括私有,但是不包括父类中所有方法,即获取不到父类的方法
public Method[] getDeclaredMethods()
Method类中的常用方法
// 根据args参数调用对象obj的该成员方法
public Object invoke(Object obj, Object... args)
//设置私有成员方法的访问权限
public void setAccessible(boolean flag)
Field类
Field类是属性类,是类中所有属性的对象,通过Field对象可以给对应的属性赋值和取值。也是用Class对象获取。
// 根据属性名获取属性对象,只能获取public
public Field getField(String name)
// 获取所有public属性对象
public Field[] getFields()
// 根据属性名获取属性对象,包括private
public Field getDeclaredField(String name)
// 获取所有属性对象,包括private的
public Field[] getDeclaredFields()
Field的常用方法比较多,都比较简单,这里介绍两个
// 同上一样,设置私有访问权限
public void setAccessible(boolean flag)
// 获取属性类型,返回的是Class对象
public Class<?> getType()
此外,Field的其他各种setXxx和getXxx方法都是根据实际的类型,然后进行具体调用
Java反射机制大致就是这些,底层是通过一些类的封装和调用完成的。