0.前言
:::danger Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法。 :::
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可 以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看 到类的结构,所以,我们形象的称之为:反射。
- Java反射机制提供的功能
1.描述
:::danger Class是所有类的类,即其他类作为Class的实例存在,和Object不一样,Object是所有类的父类 ::: 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接 口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含 了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
- Class本身也是一个类
- Class 对象只能由系统建立,不是new出来的
- 一个加载的类在 JVM 中只会有一个Class实例,因为类只加载一次
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class对象
2.常用方法
| static Class forName(String name) | 返回指定类名 name 的 Class 对象 | | —- | —- | | Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 | | getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称 | | Class getSuperClass() | 返回当前Class对象的父类的Class对象 | | Class [] getInterfaces() | 获取当前Class对象的接口 | | ClassLoader getClassLoader() | 返回该类的类加载器 | | Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class | | Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 | | Field[] getDeclaredFields() | 返回Field对象的一个数组 | | Method getMethod(String name,Class … paramTypes) | 返回一个Method对象,此对象的形参类型为paramType |
3.获取class实例
1.方式一
若已知具体的类,通过类的class属性获取,该方法最为安全可靠, 程序性能最高
//方式一:调用运行时类的class属性
Class clazz1 = Person.class;
2.方式二
已知某个类的实例,调用该实例的getClass()方法获取Class对象
//方式二:通过运行时类对象,调用getClass()方法
Person p = new Person();
Class clazz2 = p.getClass();
3.方式三
:::danger 最灵活,使用场景最多,必须将路径写全 ::: 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方 法forName()获取,可能抛出ClassNotFoundException
//方式三:调用Class的静态方法:forName(classPath)
//最灵活,使用场景最多
//必须将路径写全
Class clazz3 = Class.forName("reflectionExample.Person");
4.方式四
使用类加载器
//方式四:使用类的加载器:ClassLoader
//获取到自定义类的类加载器
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("reflectionExample.Person");
4.可以获取到Class实例的类型
- class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
2.类加载
1.加载过程
:::danger
加载和链接阶段由JVM控制,无法干涉,初始化阶段就可受人为控制(即类中的代码由人编写)
3个阶段都是类加载过程,和对象的初始化无关,其中默认初始化和显式初始化也指的是类变量(staic)
:::
1.加载
将class文件字节码内容转换为二进制字节流加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问 入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的 过程需要类加载器参与。
2.链接
将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值(即默认初始化)的阶段,这些内存 都将在方法区中进行分配。
解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
3.初始化(显式初始化类变量)
执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中 所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信 息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类 的初始化。
- 虚拟机会保证一个类的
()方法在多线程环境中被正确加锁和同步 2.类加载器
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方 法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为 方法区中类数据的访问入口。
- 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器 中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
3.创建运行时对象
要求:
- 运行时类必须提供空参构造器
- 构造器的权限必须足够,通常为public
通常要求提供空参构造器的原因:
- 便于通过反射创建运行时类对象
- 便于子类调用super()时,父类有此构造器
:::danger 没有无参构造也能创建对象,只要在操作的时候指定的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。Class<Person> clazz = (Class<Person>) Class.forName("reflectionExample.Person");
//通过newInstance()调用person中的空参构造器创建了一个对象
//要求: 1.运行时类必须提供空参构造器;2.构造器的权限必须足够,通常为public
//通常要求提供空参构造器的原因: 1.便于通过反射创建运行时类对象;2.便于子类调用super()时,父类有此构造器
Person p = clazz.getDeclaredConstructor().newInstance();
System.out.println(p);
:::4.获取运行时类的完整结构
:::danger 反射可以获取的属性结构包括:
Field、Method、Constructor、Superclass、Interface、Annotation :::1.获取实现的全部接口
Class<? extends ReflectionTest> aClass = this.getClass();
Class<?>[] interfaces = aClass.getInterfaces();
2.获取所继承的父类
Class<? extends ReflectionTest> aClass = this.getClass();
Class<?> superclass = aClass.getSuperclass();
3.获取全部构造器
Class<? extends ReflectionTest> aClass = this.getClass();
//获取public的构造器
Constructor<?>[] constructors = aClass.getConstructors();
//获取所有构造器
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor c: declaredConstructors) {
//获取修饰符
int modifiers = c.getModifiers();
//获取方法名
String name = c.getName();
//获取参数类型
Class[] parameterTypes = c.getParameterTypes();
}
4.获取全部方法
Method[] methods1 = clazz.getDeclaredMethods(); //获取所有方法
for (Method m : methods1) {
System.out.println(m);
Class<?>[] parameterTypes = m.getParameterTypes(); //获取参数类型
Class<?> returnType = m.getReturnType(); //获取返回类型
Class<?>[] exceptionTypes = m.getExceptionTypes(); //获取异常信息
int modifiers = m.getModifiers(); //获取修饰符
}
5.获取全部属性
Field[] fields1 = clazz.getDeclaredFields();
for (Field f : fields1) {
System.out.println(f);
String name = f.getName(); //获取属性名
Class<?> type = f.getType(); //获取类型
int modifiers = f.getModifiers(); //整数形式返回修饰符
}
6.获取注解
Class<? extends ReflectionTest> aClass = this.getClass();
//获取所有注解
Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();
7.获取泛型
Class clazz = Person.class;
//获取到带泛型的父类
Type a = clazz.getGenericSuperclass();
//将类型转为ParameterizedType
ParameterizedType p = (ParameterizedType) a;
//获取到泛型并返回数组(因为泛型可能有多个,比如map)
Type[] type = p.getActualTypeArguments();
//可以转回Class类型使用getName()
// System.out.println(((Class)type[0]).getName());
System.out.println(type[0].getTypeName());
8.获取类所在的包
Class<? extends ReflectionTest> aClass = this.getClass();
Package aPackage = aClass.getPackage();
System.out.println(aPackage.getName());
5.调用指定结构
1.调用指定方法
```java //非静态 Class clazz = Person.class; Person p = (Person) clazz.getDeclaredConstructor().newInstance(); //1.获取指定的某个方法,args:方法名,形参类型(要传类的加载器) Method showNation = clazz.getDeclaredMethod(“showNation”, String.class); //2.设置可访问 showNation.setAccessible(true); //3.invoke()调用,args:对象,实参,返回值即为调用的方法的返回值,对象Object,及为哪个对象调用方法 String nation = (String) showNation.invoke(p, “China”); System.out.println(nation);
//调用静态方法 Method disPlay = clazz.getDeclaredMethod(“disPlay”); disPlay.setAccessible(true); disPlay.invoke(Person.class);//可以填null
<a name="pBWAN"></a>
## 2.调用指定属性
![image.png](https://cdn.nlark.com/yuque/0/2022/png/25654866/1652840750517-7bd1da88-d187-46a7-9523-7ae02e29a40d.png#clientId=u85603415-d020-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=330&id=u4abdfe62&margin=%5Bobject%20Object%5D&name=image.png&originHeight=371&originWidth=1077&originalType=binary&ratio=1&rotation=0&showTitle=false&size=179974&status=done&style=none&taskId=udc4b6ba5-6318-4853-a557-02d2a5ca09c&title=&width=957.3333333333334)![image.png](https://cdn.nlark.com/yuque/0/2022/png/25654866/1652840762616-31242060-85a8-4297-85df-e74aa6245689.png#clientId=u85603415-d020-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=370&id=ua289bd92&margin=%5Bobject%20Object%5D&name=image.png&originHeight=416&originWidth=1042&originalType=binary&ratio=1&rotation=0&showTitle=false&size=178434&status=done&style=none&taskId=u497a9100-db28-47df-a9b1-08c719db57d&title=&width=926.2222222222222)
```java
Class clazz = Person.class;
//通过反射实例化对象
Person p = (Person) clazz.getDeclaredConstructor().newInstance();
//1.获取指定属性,不受权限限制
Field name = clazz.getDeclaredField("name");
//2.保证当前属性可访问
name.setAccessible(true);
//3.设置属性值,传入运行时类和属性
name.set(p, "tom");
//4.获得属性值
System.out.println(name.get(p));
3.调用构造器
Class clazz=Person.class;
//1.指明构造器的参数列表,不指定则调用无参构造
Constructor constructor= clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
//2.使用构造器创建新对象并赋值
Person p= (Person) constructor.newInstance("tom");
System.out.println(p);
6.代理模式
1.静态代理
package reflectionExample.proxyExample;
/*
静态代理:
1.代理类和被代理类共同实现同一个接口
2.被代理类实现具体方法,代理类控制被代理类的方法执行
3.将被代理类的对象作为属性注入代理类
4.在代理类的方法中调用被代理类的方法
*/
//接口
interface ClothFactory {
void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory {
//将被代理类的对象做为属性
private ClothFactory clothFactory;
public ProxyClothFactory(ClothFactory clothFactory) {
this.clothFactory = clothFactory;
}
@Override
public void produceCloth() {
System.out.println("代理类做准备工作");
clothFactory.produceCloth();
System.out.println("代理类做收尾工作");
}
}
//被代理类
class LilinClothFactory implements ClothFactory {
@Override
public void produceCloth() {
System.out.println("被代理类开始生产衣服");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//实例化被代理类
LilinClothFactory lilinClothFactory = new LilinClothFactory();
//传入代理类
ClothFactory clothFactory = new ProxyClothFactory(lilinClothFactory);
//执行代理类的方法
clothFactory.produceCloth();
}
}
2.动态代理
1.介绍及相关API
2.步骤
3.示例
package reflectionExample.proxyExample;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 动态代理:
* 即不预先定义被代理类对应的代理类,而是通过反射在运行时根据要代理的类来动态生成一个和被代理类的类加载器相同、实现的接口相同、而方法则通过handler反射调用同名方法的代理类对象,
* 并且通过反射获取到代理类对象所调用的方法,在handler中通过反射调用原被代理类的同名方法,实现动态代理
* 需要解决的问题:
* 1.如何根据加载到内存的被代理类,动态的创建一个代理类及其对象
* 2.当通过代理类的对象调用方法a,如何动态的去调用被代理类中同名的a方法
*
* 写法:
* 1.一个实现InvocationHandler接口的类,将被代理类对象传入其中,重写invoke(),在其中调用被代理类对象的具体方法
* 2.使用Proxy.newProxyInstance()生成代理类对象实例,在参数中将实现InvocationHandler接口的对象传入
* 3.当通过生成的代理类对象调用方法时,就会调用InvocationHandler中的invoke()即:
* 被代理类对象在InvocationHandler中,InvocationHandler在代理类对象实例中
*
* */
//接口
interface Human {
String getBelief();
void eat(String food);
}
//被代理类
class Superman implements Human {
@Override
public String getBelief() {
return "I belive i can fly";
}
@Override
public void eat(String food) {
System.out.println("eat:" + food);
}
}
//代理工厂,作用就是生产代理类对象,并将控制器传入其中
class ProxyFactory {
//返回一个代理类的对象,解决问题1
//不能写Human,不然就写死了
/**
* 大致思路就是当外部调用这个静态方法时,传入被代理类对象obj,这个方法就会使用obj相同的类加载器、接口和一个实现了InvocationHandler接口的类的对象
* 去动态的创建一个代理类对象,而当通过创建的这个代理类对象调用方法时,就会通过InvocationHandler实现类中的invoke方法去通过
* 反射获取到被代理类中的同名方法并调用(因为在创建代理类对象时,就已经将InvocationHandler实现类对象作为参数传入)。
*
* @param obj 被代理类对象
* @return 代理类对象
*/
public static Object getProxyInstance(Object obj) {//obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler(obj);
//args:被代理类的加载器,类的接口,要调用的对象(就是在调用这个方法时,会将这个对象传入,当通过这个创建的代理类对象调用方法时,就会调用handler中的方法)
//创建的代理类对象,和被代理类的类加载器相同、实现的接口相同、而方法则通过handler反射调用同名方法
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
//控制器类,用来调用被代理类的方法
class MyInvocationHandler implements InvocationHandler {
private Object obj; //需要使用被代理类的对象进行赋值
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
//当通过代理类调用方法a时,就会自动的调用如下的方法,解决问题2
//将被代理类要执行的方法a声明在invoke中,参数是当调用代理类的方法时传入的
//args:代理类的对象,代理类的对象调用的方法,该方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理类的对象调用的方法,也作为被代理类要调用的方法
//mothod.invoke方法的返回值就作为上层方法的返回值
//也就是创建了代理类之后,在调用方法时,通过反射获取到代理类调用的方法,再通过反射调用被代理类中同名的方法
return method.invoke(obj, args);
}
}
public class ProxyTest {
public static void main(String[] args) {
/**
* 根据传入的被代理类对象的不同,生成了不同的代理类对象,实现了动态代理
* */
Superman superman = new Superman();
//代理类的对象
//将本例中被代理类传入
Human proxyFactory = (Human) ProxyFactory.getProxyInstance(superman);
//此时就会自动调用InvocationHandler实现类中的invoke方法,并将这里的proxyFactory、eat、素椒杂酱面作为参数传入
proxyFactory.eat("素椒杂酱面");
System.out.println(proxyFactory.getBelief());
System.out.println("--------------------------------------------");
//将静态代理中的例子作为被代理类传入
LilinClothFactory lilinClothFactory = new LilinClothFactory();
ClothFactory proxyFactory1 = (ClothFactory) ProxyFactory.getProxyInstance(lilinClothFactory);
proxyFactory1.produceCloth();
}
}