反射笔记

本文最后更新于:3 个月前

【参考】

Java 反射机制详解 - JavaGuide(√)

1 - 零碎点

反射是框架的灵魂,反射能够在运行时分析类、并且执行类中的方法。

反射能够获取任意一个类的所有的方法和属性,并且还能够调用这些方法和属性。

2 - 反射的应用场景

2.1 - 概述

框架、动态代理、注解。这三个内容会用到反射。

2.2 - 展开

==框架==

通过反射,能够大量的使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。


==动态代理==

各种框架中也大量使用了动态代理,动态代理的实现也依赖反射。

比如在动态代理的实现过程中,会使用反射类Method来调用指定的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;

public DebugInvocationHandler(Object target) {
this.target = target;
}


public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
System.out.println("after method " + method.getName());
return result;
}
}

==注解==

Java中的注解也用到了反射。

比如使用Spring的时候,@Component注解能够将一个类声明为Spring Bean,@Value注解能够读取到配置文件中的值。

这些都是通过反射分析类,来获取类/属性/方法/方法的参数上的注解,然后根据注解做进一步的分析。

3 - 反射机制的优缺点

3.1 - 优点

能够让代码更灵活,比如用于框架、动态代理来扩展切面功能、注解来方便开发

3.2 - 缺点

(1)==安全问题:==运行的时候能够分析并操作类,会增加安全问题,比如运行时使用反射创建对象会无视泛型的安全检查。

(2)==性能问题:==相对来说,发射的性能会比正射要差一些。

4 - 反射实战

4.1 - 获取 Class 对象的四种方式

获取一个类的方法、变量等信息需要从 Class 对象获取,因此要想动态的获取这些信息则需要获取Class对象。


Java 提供了四种获取一个类的 Class 对象的方式。

(1)通过类名获取:TargetObject.class

1
Class alunbarClass = TargetObject.class;

通过此方式获取 Class 对象不会进行初始化

(2)通过类的完整名称(路径)获取:Class.forName(“cn.javaguide.TargetObject”)

1
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");

(3)通过类的对象获取:o.getClass()

1
2
TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();

(4)通过类加载器获取:xxxClassLoader.loadClass()传入类路径

1
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");

通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行

4.2 - 反射的一些基本操作

(1)创建要使用反射操作的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TargetObject {
private String value;

public TargetObject() {
this.value = "alec";
}

public void publicMethod(String s) {
System.out.println("I am " + s);
}

public void privateMethod() {
System.out.println("value is " + value);
}
}

(2)使用反射操作这个类的方法和参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 通过反射创建对象:获取要操作的类的 Class 对象,并创建这个类的实例
Class<?> targetClass = Class.forName("TargetObject");
TargetObject targetObject = (TargetObject)targetClass.newInstance();

// 获取要操作的类中定义的所有的方法
Method[] methods = targetClass.getDeclaredMethods();
for (Method e : methods) {
System.out.println(e.getName());
}

// 获取指定的public方法并调用
Method publicMethod = targetClass.getDeclaredMethod("publicMethod", String.class);
publicMethod.invoke(targetObject, "alec");

// 取消private方法的安全检查然后调用
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);

// 获取指定的参数并对参数进行修改
Field field = targetClass.getDeclaredField("value");
field.setAccessible(true);
field.set(targetObject, "alec_changed");
privateMethod.invoke(targetObject);

}
}
/*结果*/
publicMethod
privateMethod
I am alec
value is alec
value is alec_changed

5 - 反射类的常用方法总结

5.1 - Class.getDeclaredFields() 和 Class.getMethods() 的区别

参考:https://www.cnblogs.com/wy697495/p/9631909.html

==区别:==

两个方法的区别主要在于:getMethods()返回的是该类以及超类的公共方法。getDeclaredMethods()返回该类本身自己声明的包括公共、保护、默认(包)访问和私有方法,但并不包括超类中的方法。

总结:其实Class中有很多相似的方法比如:getAnnotations()和getDeclaredAnnotations(),以及getFields()和getDeclaredFields()等等,不同之处和上面基本一样

==例子:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*定义要被反射获取信息的类*/
public class TargetClass {
public TargetClass() {
}

public void publicMethod_1(){

}

protected void protectedMethod_2(){

}

void defaultMethod_3(){

}

private void privateMethod_4(){

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*方法测试*/
public class TargetObject {
private String value;

public TargetObject() {
this.value = "alec";
}

public void publicMethod(String s) {
System.out.println("I am " + s);
}

public void privateMethod() {
System.out.println("value is " + value);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*结果*/
=============getMethods==================
publicMethod_1
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
===========getDeclaredMethods=============
protectedMethod_2
privateMethod_4
defaultMethod_3
publicMethod_1
==========================================

5.2 - 通过反射调用类的各种元素

image-20221114193018687

1
2
3
4
5
6
// 通过反射可以得到
Classes,内部类;
Field,属性;
Constructor,构造器;
Method,方法;
Annotation,注解;

反射笔记
https://alec-97.github.io/posts/1454350640/
作者
Shuai Zhao
发布于
2022年11月14日
许可协议