What(什么是反射)
在JVM运行的时候会动态加载类,对于任意一个类都能获取到该类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就是Java的反射机制(reflection)
Why(为什么要用反射)
- 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
- 获取任意对象的属性,并且能改变对象的属性
- 调用任意对象的方法
- 判断任意一个对象所属的类
- 实例化任意一个类的对象
How(怎么用)
前置类
class Person{
private Integer age = 10;
public void Say(String sentence){
System.out.println(sentence);
}
private void Hello(String name,Integer age){
System.out.println("Hi,I am "+name+"and "+age+" years old");
}
public static void Bye(String name){
System.out.println("Bye Bye "+name);
}
public Person(Integer age) {
this.age = age;
System.out.println("This is not default constructor");
}
public Person(){
System.out.println("This is default constructor");
}
}
获取Class对象
Class<?> aClass = Class.forName("Person");//通过Class类的forName静态方法
Class<? extends Person> aClass1 = person1.getClass();//通过已有对象的getClass方法
Class<String> stringClass = String.class;//直接通过类名的.class
实例化对象(当使用的类的构造函数不是public的,或者没有无参构造函数的话,是不可以用newInstance方法直接创造实例的)
通过Class对象实例化对象
Person person = (Person) aClass.newInstance();
通过constructor实例化对象
Constructor<?> constructor = aClass.getConstructor();
Person person1 = (Person) constructor.newInstance();
两者的区别:通过Class对象实例化的对象只能通过默认无参构造函数实例化,而通过constructor实例化可以选择其他构造函数。例:
Class<?> aClass = Class.forName("Person");
Person person = (Person) aClass.newInstance();
Constructor<?> constructor = aClass.getConstructor(Integer.class);
Person person1 = (Person) constructor.newInstance(20);
运行结果
获取类的方法
Method[] methods = aClass.getMethods();//获取所有public方法(包括该类的父类的方法和实现接口的方法)
Method[] declaredMethods = aClass.getDeclaredMethods();//获取当前类申明的所有方法
Method say = aClass.getDeclaredMethod("Say", String.class);//获取特定的方法(第一个参数为方法名,第二个为该函数需要传入的参数的类型)
System.out.println(methods);
System.out.println("-------------------------------");
System.out.println(declaredMethods);
System.out.println("-------------------------------");
System.out.println(say);
运行结果
获取类的构造方法
Constructor<?>[] constructors = aClass.getConstructors();//获取所有public构造方法(包括父类)
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();//获取该类所有已申明构造方法
Constructor<?> constructor1 = aClass.getDeclaredConstructor(Integer.class);//获取特定的构造方法(参数为构造方法的参数的类型)
System.out.println("-------------------------------");
System.out.println(Arrays.toString(declaredConstructors));
System.out.println("-------------------------------");
System.out.println(constructor1);
运行结果
获取类的属性
Field[] fields = aClass.getFields();//获取所有public变量(包括父类)
Field[] declaredFields = aClass.getDeclaredFields();//获取该类所有已申明的变量
Field age = aClass.getDeclaredField("age");
System.out.println("Fields Next:");
System.out.println(Arrays.toString(fields));
System.out.println(Arrays.toString(declaredFields));
System.out.println(age);
运行结果
invoke执行方法
public Object invoke(Object obj, Object... args)
第一个参数为类的实例,第二个参数为相应函数中的参数
执行普通方法(如果该方法为privet,那么需要调用AccessibleObject上的setAccessible方法来允许访问):
Constructor<? extends Person> constructor2 = aClass1.getConstructor();
Person person2 = constructor2.newInstance();
Method hello = aClass.getDeclaredMethod("Hello", String.class, Integer.class);
hello.setAccessible(true);//因为Hell方法是privet,因此要调用setAccessible方法来允许访问
hello.invoke(person2,"Bob",15);
运行结果
执行静态方法(第一个参数就是Class对象(这里还可以使用null空置该参数,也可以执行当前Class对象的方法))
Method bye = aClass.getDeclaredMethod("Bye", String.class);//这是一个静态方法
bye.invoke(aClass,"Jack");//bye.invoke(null,"Jack");
运行结果
这里的Java.Lang.reflect.AccessibleObject类是Filed,Method和Constructor类对象的基类,它提供了将反射对象标记为在使用它时抑制默认Java语言访问控制检查的功能。
以下借用大佬的图:
所以对于私有private的成员变量或方法,都可以通过setAccessible来恢复访问!
使用方法就是在获取了privet的方法或变量后紧挨着调用setAccessible方法设置为true即可
原理
- 当写完一个Java项目之后,每个Java文件都会被编译为一个class文件
- 当程序运行的时候,这些class文件会被ClassLoader(类加载器)加载到JVM中,加载完一个类之后就会在JVM中产生一个Class对象
- 通过Class对象就能获取方法,构造方法,属性
当程序运行的时候需要动态加载一些类,在加载完成后,在堆中就会产生一个Class类对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。这些类有可能在只有在某些特殊情况下才会被使用而在某些情况不会被使用,如果把每种情况都用传统的方法编写出来,那么代码将会很冗余;因此用Java的反射能使代码的耦合性提高
Spring 框架的 IOC(动态加载管理 Bean),Spring通过配置文件配置各种各样的bean,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载
还有Spring AOP(动态代理)功能都和反射有关系。