聊聊反射

355 阅读4分钟

1. 反射是什么?

反射是一种机制,是一种能力,是指JVM在运行过程中,对于任意一个类,都可以知道这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意方法和属性

2. 反射的原理?

如果你对 JVM 的类加载机制有所了解(这里不了解也没关系),就会知道,类在编译的时候,会生成一个 .class 文件,当类被使用的时候,这个 class 文件就会被读取并被载入到虚拟机中,生成对应类型的 Class 对象,这个被创建出的 Class 对象中就包含了我们原本的类中一切信息,通过这个对象,我们就可以去获取这个类的属性和方法,并且把这些属性和方法转换成一个对象,从而达到改变/执行的目的。

3. 反射的使用?

使用反射的第一步,是获取一个 Class 对象,我们可以通过以下几种方式去获取一个 Class 对象

获取 Class 对象

// 1. 根据类的全限定名
Class str1 = Class.forName("java.lang.String");

// 2. .class获取
Class str2 = String.class;

// 3. 继承自 Object 类的 getClass 方法
String s = new String();
Class str3 = s.getClass();

拿到了 Class 对象,下面我就可以对这个 Class 对象进行进一步的剖析,获取它的构造方法,字段和一般方法

获取Constructor,Method,Filed

在获取之前,我们首先来新建个一个 Test 类来供我们使用

@Data
class Test {

    private String id;

    private String name;

    public String code;
}

这里需要⚠️注意,我在这里新建了两个private的字段和一个public 的字段,是为了给大家展示,即使是反射,也无法获取private的属性~

// 获取 constructor
Constructor[] constructors = test.getConstructors();
Arrays.stream(constructors).forEach(System.out::println);

System.out.println("------------------");

// 获取 filed
Field[] fields = test.getFields();
Arrays.stream(fields).forEach(System.out::println);

System.out.println("------------------");

// 获取 method
Method[] methods = test.getMethods();
Arrays.stream(methods).forEach(System.out::println);

打印的结果如下:

public show.shanhe.interview.reflection.Test()
------------------
public java.lang.String show.shanhe.interview.reflection.Test.code
------------------
public boolean show.shanhe.interview.reflection.Test.equals(java.lang.Object)
public java.lang.String show.shanhe.interview.reflection.Test.toString()
public int show.shanhe.interview.reflection.Test.hashCode()
public java.lang.String show.shanhe.interview.reflection.Test.getName()
public void show.shanhe.interview.reflection.Test.setName(java.lang.String)
public java.lang.String show.shanhe.interview.reflection.Test.getId()
public java.lang.String show.shanhe.interview.reflection.Test.getCode()
public void show.shanhe.interview.reflection.Test.setCode(java.lang.String)
public void show.shanhe.interview.reflection.Test.setId(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

Filed的打印中,我们可以看到只有code字段被打印了出来,其余两个private的字段并没有出现在我们打印的列表中。

拿到了方法,我们就可以用反射去执行方法

Method setName = test.getMethod("setName", String.class);
Test t = test.newInstance();
setName.invoke(t,"shanhe");
System.out.println(t.getName());

使用Methodinvoke方法,就可以去执行setName方法,这样就可以达到给对象赋值的目的~

关于反射的使用方法,到这里就告一段落,下面我们来看一下在项目中,我们如何使用反射去达到我们的目的,完成自动化的操作。

4. 反射的实际应用场景

在我们的实际业务开发中,我们很少用到反射去实现我们的业务功能,因为在业务执行的过程中,我们使用反射会使代码的执行效率变得很低,所以反射一般是应用在我们设计框架的时候去使用,我们平时使用到的一些框架和技术,其底层都是基于反射去实现的,简而言之,反射是用来造轮子的,那么反射造的轮子,我们日常项目开发中经常用到的有哪些呢?

说起 Spring 的特性,大多数人脑海中浮现的的下面这两个名字,其实这两个特性都是基于反射去实现的。

AOP

基于我们对 AOP 的了解,我们不难知道(如果不知道,请等待设计模式之代理模式的文章),AOP是基于动态代理去实现对方法的增强处理,那么动态代理类一般都会去继承InvocationHandler这个接口,然后通过重写接口中的invoke方法,就可以实现AOP所要达到的目的。

IOC

IOC的实现,实际上就是通过工厂模式+反射来完成的,首先,我们通过注解或者xml的方式,将类(Bean)注册到BeanFatory中,然后程序在执行的过程中,通过bean的全路径名,使用反射去获取一个对象实例

当然,这里没有列举出来的还有很多很多,比如文件的反编译,比如java的agent机制,比如tomcat,比如动态生成类的框架,几乎我们所能见到的框架中,都或多或少使用了反射这个神器来实现,反射的使用熟练程度,通常也是用来区分CRUD程序员和造轮子程序员的最大区别,也是进阶路上必不可少的一道坎儿,掌握了反射,我们就可以有更多的思路去实现,去优化我们的代码~

学会使用反射+设计模式这是一个成长的标志,所以,如果你有学到,请给我点赞+关注,这是给一个坚持原创的人最大的支持和鼓励,另外设计模式系列博客预热中,敬请期待~