一直以来反射技术都是 Java
中的闪亮点,这也是目前大部分框架(如 Spring/Mybatis
等)得以实现的支柱。在 Java
中,Class
类与 java.lang.reflect
类库一起对反射技术进行了全力的支持
Java
反射机制是在 运行状态
中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java
语言的反射机制
反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释。一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);
上面这样子进行类对象的初始化,我们可以理解为「正」。而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new
关键字来创建对象了。这时候,我们使用 JDK
提供的反射 API
进行反射调用
Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类 Apple
,而第二段代码则是在运行时通过字符串值才得知要运行的类 com.chenshuyi.reflect.Apple
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是 Class
类中的方法。所以先要获取到每一个字节码文件对应的 Class
类型的对象
在 Java
中 Class
类与 java.lang.reflect
类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有
Class
类:运行在内存中的所有类都是该 Class
类的实例对象,每个 Class
类对象内部都包含了本来的所有信息。记着一句话,通过反射干任何事,先找 Class
准没错!Constructor
类:描述一个类的构造方法,内部包含了构造方法的所有信息,例如参数类型,参数名字,访问修饰符等Field
类:描述一个类的属性,内部包含了该属性的所有信息,例如数据类型,属性名,访问修饰符等Method
类:描述一个类的所有方法(包括抽象方法),内部包含了该方法的所有信息,与 Constructor
类似,不同之处是 Method
拥有返回值类型信息,因为构造方法是没有返回值的Class
类如图是类的正常加载过程:反射的原理在与 Class
对象。熟悉一下加载的时候:Class
对象的由来是将 Class
文件读入内存,并为之创建一个 Class
对象
Class
类的实例表示正在运行的 Java
应用程序中的类和接口。也就是 JVM
中有 N
多的实例每个类都有其 Class
对象(包括基本数据类型)Class
没有公共构造方法。Class
对象是在加载类时由 Java
虚拟机以及通过调用类加载器中的 defineClass
方法自动构造的。也就是这不需要我们自己去处理创建,JVM
已经帮我们创建好了没有公共的构造方法,方法共有 64
个太多了。下面用到哪个就详解哪个吧
Class
对象Object ——> getClass();
Class
属性Class
类的静态方法:forName(String className)
第一个是因为 Object
类中的 getClass
方法,因为所有类都继承 Object
类。从而调用 Object
类来获取
public class ReflexTest {
public static void main(String[] args) {
/*第一种方式获取Class对象*/
Student student = new Student();/*这一new产个生一Student对象,一个Class对象*/
Class<? extends Student> studentClass1 = student.getClass();
System.out.println(studentClass1.getName());
/*第二种方式获取Class对象*/
Class<Student> studentClass2 = Student.class;
System.out.println(studentClass1 == studentClass2);
/*第三种方式获取Class对象*/
try {
/*注意此字符串必须是真实路径,就是带包名的类路径,包名.类名*/
Class<?> studentClass3 = Class.forName("org.dabao.reflex.Student");
System.out.println(studentClass2 == studentClass3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:
org.dabao.reflex.Student
true
true
注意:在运行期间,一个类,只有一个 Class
对象产生。三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法
Constructor
类Constructor
类存在于反射包 java.lang.reflect
中,反映的是 Class
对象所表示的类的构造方法。获取 Constructor
对象是通过 Class
类中的方法获取的,Class
类与 Constructor
相关的主要方法如下
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
static Class> |
forName(String className)<、code> |
返回与带有给定字符串名的类或接口相关联的 Class 对象 |
Constructor |
getConstructor(Class>... parameterTypes) |
返回指定参数类型、具有public访问权限的构造函数对象 |
Constructor>[] |
getConstructors() | 返回所有具有public访问权限的构造函数的Constructor对象数组 |
Constructor |
getDeclaredConstructor(Class>... parameterTypes) |
返回指定参数类型、所有声明的(包括private)构造函数对象 |
Constructor>[] |
getDeclaredConstructor() |
返回所有声明的(包括private)构造函数对象 |
T | newInstance() | 创建此 Class 对象所表示的类的一个新实例 |
其他方法请查看 API
Field
类Field
类存在于反射包 java.lang.reflect
中,它提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。同样的道理,我们可以通过 Class
类的提供的方法来获取代表字段信息的 Field
对象,Class
类与 Field
对象相关方法如下
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
Field |
getDeclaredField(String name) |
获取指定name名称的(包含private修饰的)字段,不包括继承的字段 |
Field[] |
getDeclaredField() |
获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段 |
Field |
getField(String name) |
获取指定name名称、具有public修饰的字段,包含继承字段 |
Field[] |
getField() |
获取修饰符为public的字段,包含继承字段 |
其他方法请查看 API
Method
类Method
类存在于反射包 java.lang.reflect
中,它提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。下面是 Class
类获取 Method
对象相关的方法
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
Method |
getDeclaredMethod(String name, Class>... parameterTypes) |
返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法 |
Method[] |
getDeclaredMethod() |
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法 |
Method |
getMethod(String name, Class>... parameterTypes) |
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法 |
Method[] |
getMethods() |
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法 |
JDK
动态代理我们知道 Spring
主要有两大思想,一个是 IOC
,一个就是 AOP
。对于 IOC
容器中 bean
的创建就是使用了 Java
的反射技术;而对于 Spring
的核心 AOP
来说,我们知道 AOP
的实现原理之一就是 JDK
的动态代理机制,而 JDK
的动态代理本质就是利用了反射技术来实现的
JDK
的动态代理机制中,有两个重要的类和接口,一个是 InvocationHandler
接口、一个是 Proxy
类,这一个类和接口是实现我们动态代理所必须用到的
Proxy
类Proxy
类来自于 java.lang.reflect
包下,它就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance()
这个方法
Object proxy = Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader
:一个 ClassLoader
类加载器对象,定义了由哪个 ClassLoader
对象来对生成的代理对象进行加载interfaces
:一个 Interface
对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我代理对象就能调用这组接口中的方法了h
:一个 InvocationHandler
对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler
对象上通过 Proxy.newProxyInstance()
创建的代理对象是在 JVM
运行时 动态生成的一个对象
,它并不是我们的 InvocationHandler
类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象
InvocationHandler
接口InvocationHandler
接口也来自于 java.lang.reflect
包下。每一个动态代理类都必须要实现 InvocationHandler
这个接口,并且每个代理类的实例都关联到了一个 handler
,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler
这个接口的 invoke()
方法来进行调用
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
proxy
:指代我们所代理的那个真实对象method
:指代的是我们所要调用真实对象的某个方法的 Method
对象args
:指代的是调用真实对象某个方法时接受的参数有关 JDK
动态代理的原理 1
:https://blog.csdn.net/weixin_38192427/article/details/113093728
有关 JDK
动态代理的原理 2
:https://blog.csdn.net/Dream_Weave/article/details/84183247