Parad0x7e Lv1

需求引出反射

为什么会有反射这个机制,来看看下面的例子。

  1. 根据配置文件re.properties指定信息,创建Dog对象并调用方法hi
  2. 这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)
    配置文件内容:
1
2
classfullpath=com.test.Dog  
method=hi


Dog类

1
2
3
4
5
6
7
8
9
package com.test;   
public class Dog {
public String name = "旺财";
public void hi() {
System.out.println("hi" + name);
}
public void say() {
System.out.println("say hello");
}


测试代码

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
package com.ref;  
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

// 反射问题的引入
@SuppressWarnings({"all"})
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 根据配置文件 re.properties 指定信息, 创建 Dog 对象并调用方法 hi
// 传统的方式 new 对象 -》 调用方法 // Dog Dog = new Dog(); // Dog.hi(); ===> Dog.cry() 修改源码. // 我们尝试做一做 -> 明白反射 // 1. 使用 Properties 类, 可以读写配置文件 Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();//"com.entity.Dog"
String methodName = properties.get("method").toString();//"hi"
System.out.println("classfullpath=" + classfullpath);
System.out.println("method=" + methodName);
// 2. 创建对象 , 传统的方法,行不通 =》 反射机制
// new classfullpath(); classfullpath();是"com.entity.Dog"一个字符串 // new 一个字符串是不行的!!!
// 3. 使用反射机制解决 // (1) 加载类, 返回 Class 类型的对象 cls
Class cls = Class.forName(classfullpath);
// (2) 通过 cls 得到你加载的类 com.entity.Dog 的对象实例
Object o = cls.newInstance();
System.out.println("o 的运行类型=" + o.getClass()); //运行类型
// (3) 通过 cls 得到你加载的类 com.entity.Dog 的 methodName"hi" 的方法对象 // 即:在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
// (4) 通过 method1 调用方法: 即通过方法对象来实现调用方法
System.out.println("=============================");
method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)
}
}

运行结果

现在我们可以不修改源码,来调用其他的方法,修改配置文件 将

1
2
classfullpath=com.test.Dog  
method=say


在传统方法下,我们必须修改源码才能实现,有了反射机制,我们修改配置文件即可实现。

Java反射机制

Java Reflection

  1. 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。
  2. 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。
1
2
Person对象 p --> 类型Person类
Class对象 cls ---> 类型Class类

Java 反射机制原理示意图

Java 反射机制作用

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时得到任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理

反射相关的主要类

  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method:代表类的方法,Method对象表示一个类的一个方法
  3. java.lang.reflect.Field:代表类的成员变量,Field对象表示一个类的一个成员变量
  4. java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示一个类的一个构造器
    除了 Class 在 java.lang 包中,其余反射相关类(如 Method、Field、Constructor)都在 java.lang.reflect 包中

反射优点和缺点

优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
缺点:使用反射基本是解释执行,执行速度有影响

反射调用优化 - 关闭访问检查

  1. Method和Field、Constructor对象都有setAccessible()方法
  2. setAccessible作用是启动和禁用访问安全检查的开关
  3. 参数值为true表示反射对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查
    测试代码
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
40
41
42
43
44
45
46
47
import com.test.Dog;  
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection02 {
public static void m1() {
Dog dog = new Dog();
long start = System.currentTimeMillis();
for (int i = 0; i < 90; i++) {
dog.hi();
}
long end = System.currentTimeMillis();
System.out.println("m1() 耗时=" + (end - start) + " 传统");
}
//反射机制调用方法 hi
public static void m2() throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class cls = Class.forName("com.test.Dog");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);//反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m2() 耗时=" + (end - start) + " 反射");
}
//反射调用优化 + 关闭访问检查
public static void m3() throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class cls = Class.forName("com.test.Dog");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true);//在反射调用方法时,取消访问检查
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);//反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m3() 耗时=" + (end - start) + " 反射优化");
}
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
m1();//传统
m2();//反射
m3();//反射优化
}
}

测试结果,优化后对效率的提升其实并不是很大

Class类

基本介绍

  1. Class也是类,因此也继承Object类

  2. Class类对象不是new出来的,而是系统创建的
    new对象调试过程如下


    通过反射去得到对象,调试过程如下




  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次

  4. 每个类的实例都会记得自己是由哪个Class实例所生成

  5. 通过Class对象可以完整地得到一个类的完整结构,通过一系列API

  6. Class对象是存放在堆的

  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名,访问权限等等)
    测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.test;  
import java.util.ArrayList;
public class Class01 {
public static void main(String[] args) throws ClassNotFoundException {
// Class 类对象不是 new 出来的,而是系统创建的
// (1) 传统 new 对象 // Dog dog = new Dog(); // (2) 反射方式 ClassLoader 类, 仍然是通过 ClassLoader 类加载 Dog 类的 Class 对象
Class cls1 = Class.forName("com.test.Dog");
//3. 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
Class cls2 = Class.forName("com.test.Dog");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
Class cls3 = Class.forName("com.test.Dog");
System.out.println(cls3.hashCode());
}
}

Class类常用方法

方法名 功能说明
static Class forName(String name) 返回指定类名 nameClass 对象
Object newInstance() 调用缺省构造函数,返回该 Class 对象的一个实例
getName() 返回此 Class 对象所表示的实体(类、接口、数组类、基本类型等)名称
Class[] getInterfaces() 获取当前 Class 对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示此 Class 所表示的实体的超类的 Class
Constructor[] getConstructors() 返回一个包含某些 Constructor 对象的数组
Field[] getDeclaredFields() 返回 Field 对象的一个数组
Method getMethod(String name, Class... paramTypes) 返回一个 Method 对象,此对象的形参类型为 paramType

还有一些常用方法的演示
此时目录结构如下:

测试代码

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
package com.test;  
import com.entity.Car;
import java.lang.reflect.Field;
public class Class02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classAllPath = "com.entity.Car";
// 1.获取Car类对应的Class对象
// <?> 表示不确定类型的Class Class<?> cls = Class.forName(classAllPath);
// 2.输出cls
// 显示cls对象,是哪个类的Class对象 com.entity.Car // 输出cls运行类型 java.lang.Class System.out.println(cls);
System.out.println(cls.getClass());
// 3.得到包名
System.out.println(cls.getPackage());
// 4.得到全类名
System.out.println(cls.getName());
// 5.通过cls生成对象实例
Car car = (Car)cls.newInstance();
System.out.println(car);
// 6.通过反射获取属性 brand
Field field = cls.getField("brand");
System.out.println(field.get(car)); // 奔驰
// 7.通过反射给属性赋值 field.set(car, "宝马");
System.out.println(field.get(car));
// 8.通过反射获取某个类的所有属性
Field[] fields = cls.getFields();
for (Field f : fields) {
System.out.print(f.get(car) + "\t");
}
}}

获取Class类对象

  1. 已知全类名且在类路径下:
    Class cls1 = Class.forName("java.lang.Cat");
    用途:配置文件读取全路径加载类,可能抛 ClassNotFoundException
  2. 已知具体类:
    Class cls2 = Cat.class;
    用途:参数传递,安全可靠,性能最高。
    1. 基本数据类型:
      Class cls = int.class; // 同理支持 char、boolean、float、double、byte、long、short
    2. 包装类:
      Class cls = Integer.TYPE; // 返回对应基本类型的 Class
  3. 已有对象实例:
    Class cls3 = 对象.getClass(); // 运行类型
    用途:通过已有对象获取 Class。
  4. 用类加载器:
    ClassLoader cl = 对象.getClass().getClassLoader();
    Class cls4 = cl.loadClass("类的全类名");
    测试代码
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
package com.test;  
import com.entity.Car;
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
// 1.Class.forName
String classAllPath = "com.entity.Car"; // 通过读取配置文件获取
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);
// 2.类名.class , 应用场景: 用于参数传递
Class<?> cls2 = Car.class;
System.out.println(cls2);
// 3.对象.getClass(), 应用场景,有对象实例
Car car = new Car();
Class<?> cls3 = car.getClass();
System.out.println(cls3);
// 4.通过类加载器【4 种】来获取到类的 Class 对象
//(1)先得到类加载器 car ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到 Class 对象
Class<?> cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
//cls1 , cls2 , cls3 , cls4 其实是同一个对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
// 5.基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到 Class 类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass); // int
// 6.基本数据类型对应的包装类,可以通过 .TYPE 得到 Class 类对象 Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
// 其它包装类 BOOLEAN, DOUBLE, LONG,BYTE 等等
System.out.println(type1);
System.out.println(integerClass.hashCode());//?
System.out.println(type1.hashCode());//?
}
}

哪些类型有Class对象

  1. 外部类(顶层普通类)
  2. 成员内部类(非 static,属于对象)
  3. 静态内部类(static,属于类)
  4. 局部内部类(方法/代码块内定义)
  5. 匿名内部类(无名字,一次性实现)
  6. 接口(interface
  7. 数组(任何维度,如 int[]String[][]
  8. 枚举(enum
  9. 注解(@interface
  10. 基本数据类型(intcharboolean…)
  11. void(作为“返回类型”的 Class,单例 Void.TYPE
    测试代码
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.test;  

public class Class03 {
/* ===== 1. 外部类 ===== */
public static class Outer {
}
/* ===== 2. 成员内部类 ===== */
public class MemberInner {
}
/* ===== 3. 静态内部类 ===== */
public static class StaticInner {
}
public void local() {
/* ===== 4. 局部内部类 ===== */
class LocalInner {
} print("局部内部类", LocalInner.class);
/* ===== 5. 匿名内部类 ===== */
Runnable anon = new Runnable() {
@Override
public void run() {
} };
print("匿名内部类", anon.getClass());
}

/* ===== 6. 接口 ===== */
interface MyInterface {
}
/* ===== 7. 枚举 ===== */
enum MyEnum {A, B}

/* ===== 8. 注解 ===== */
@interface MyAnno {
}
public static void main(String[] args) throws NoSuchFieldException {
/* 1. 外部类 */
print("外部类", Outer.class);
/* 2. 成员内部类 */
print("成员内部类", MemberInner.class);
/* 3. 静态内部类 */
print("静态内部类", StaticInner.class);
/* 4 & 5. 局部 / 匿名内部类 */
new Class03().local();
/* 6. 接口 */
print("接口", MyInterface.class);
/* 7. 枚举 */
print("枚举", MyEnum.class);
/* 8. 注解 */
print("注解", MyAnno.class);
/* 9. 数组 */
int[] arr = new int[0];
print("数组", arr.getClass());
/* 10. 基本数据类型 */
print("基本数据类型(int)", int.class);
/* 11. void */
print("void", void.class);
}

private static void print(String label, Class<?> cls) {
System.out.printf("%-12s -> %s%n", label, cls);
}
}

类加载

反射机制是 Java 实现“动态语言”特性的关键,它允许运行时动态加载类

  1. 静态加载
    编译阶段就把相关类加载进来;如果类不存在,编译直接报错,依赖性强。
  2. 动态加载
    运行阶段真正用到时才加载;即使类文件缺失,只要执行路径没走到,就不会报错,依赖性降低。
1
2
3
4
5
6
7
8
9
10
Java 类加载时机对比:
1. 创建对象时(new
→ 静态加载
2. 子类被加载时,其父类也会立即被加载
→ 静态加载
3. 调用类中的静态成员(静态变量/静态方法)
→ 静态加载
4. 通过反射:
`Class.forName("com.entity.Dog")`
→ 动态加载

通过反射获取类的结构信息

java.lang.Class类

  1. getName: 获取全类名
  2. getSimpleName: 获取简单类名
  3. getFields: 获取所有public修饰的属性,包含本类以及父类的
  4. getDeclaredFields: 获取本类中所有属性
  5. getMethods: 获取所有public修饰的方法,包含本类以及父类的
  6. getDeclaredMethods: 获取本类中所有方法
  7. getConstructors: 获取本类所有public修饰的构造器
  8. getDeclaredConstructors: 获取本类中所有构造器
  9. getPackage: 以Package形式返回包信息
  10. getSuperClass: 以Class形式返回父类信息
  11. getInterfaces: 以Class[]形式返回接口信息
  12. getAnnotations: 以Annotation[]形式返回注解信息

java.lang.reflect.Field类

  1. getModifiers: 以int形式返回修饰符
    • 说明: 默认修饰符是0,public是1,private是2,protected是4,static是8,final是16,public(1) + static(8) = 9
  2. getType: 以Class形式返回类型
  3. getName: 返回属性名

java.lang.reflect.Method类

  1. getModifiers: 以int形式返回修饰符
    • 说明: 默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
  2. getReturnType: 以Class形式获取返回类型
  3. getName: 返回方法名
  4. getParameterTypes: 以Class[]返回参数类型数组

java.lang.reflect.Constructor类

  1. getModifiers: 以int形式返回修饰符
  2. getName: 返回构造器名(全类名)
  3. getParameterTypes: 以Class[]返回参数类型数组

通过反射创建对象

创建对象的两种方式

  1. 方式一:调用类中的public修饰的无参构造器
  2. 方式二:调用类中的指定构造器

Class类相关方法

  • newInstance:调用类中的无参构造器,获取对应类的对象
  • getConstructor(Class... cls):根据参数列表,获取对应的public构造器对象
  • getDeclaredConstructor(Class... cls):根据参数列表,获取对应的所有构造器对象

Constructor类相关方法

  • setAccessible:暴破 (感觉翻译的好奇怪)
  • newInstance(Object... obj):调用构造器

通过反射访问类中的成员

访问属性

  1. 根据属性名获取 Field 对象
1
Field f = cls.getDeclaredField(属性名);
  1. 爆破:设置 Field 为可访问
1
f.setAccessible(true); // f 是 Field
  1. 访问
1
2
f.set(o, 值); // o 表示对象
System.out.println(f.get(o)); // o 表示对象
  1. 注意:如果是静态属性,则 setget 中的参数 o,可以写成 null

访问方法

  1. 根据方法名和参数列表获取 Method 方法对象
1
2
// 获取本类的所有方法
Method m = cls.getDeclaredMethod(方法名, 参数类型.class);
  1. 获取对象:创建类的实例
1
Object o = cls.newInstance();
  1. 爆破:设置 Method 为可访问
1
m.setAccessible(true);
  1. 访问:调用方法
1
Object returnValue = m.invoke(o, 实参列表); // o 就是对象
  1. 注意:如果是静态方法,则 invoke 的参数 o,可以写成 null
    补充:
  • cls.getDeclaredField(属性名)cls.getDeclaredMethod(方法名, 参数类型.class) 可能抛出 NoSuchFieldExceptionNoSuchMethodException 异常,因此在实际使用中需要进行异常处理。
  • cls.newInstance() 方法在Java 9之后已被弃用,推荐使用 cls.getDeclaredConstructor().newInstance() 来创建实例。
  • System.out.println(f.get(o)); 用于打印获取到的属性值。

反射与安全

攻击面与利用

  • 绕过访问控制(私有字段/方法):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SecretClass {
private String secret = "机密信息";
/** 私有方法:仅用于示例,正常情况下不应被外部调用 */
private void secretMethod() {
System.out.println("这是私有方法");
}
}
Class<?> cls = SecretClass.class;
SecretClass obj = new SecretClass();
Field f = cls.getDeclaredField("secret");
f.setAccessible(true);
String secret = (String) f.get(obj);
Method m = cls.getDeclaredMethod("secretMethod");
m.setAccessible(true);
m.invoke(obj);
  • 动态加载与插件滥用:
1
2
3
Class<?> c = Class.forName("com.example.Plugin");
Object o = c.getDeclaredConstructor().newInstance();
if (o instanceof Runnable) ((Runnable) o).run();
  • 反序列化利用(任意方法执行):
1
2
3
Method getRt = Class.forName("java.lang.Runtime").getMethod("getRuntime");
Object rt = getRt.invoke(null);
rt.getClass().getMethod("exec", String.class).invoke(rt, "calc.exe");

风险与影响

  • 访问控制绕过:破坏封装,读写 private 字段、调用私有方法
  • 类型安全绕过:运行期结构/类型变更导致 ClassCastException
  • 性能开销:反射比直接调用慢,频繁使用影响性能
  • 策略绕过:在弱隔离/沙箱中绕过限制

常见攻击链

  • Commons Collections:利用 InvokerTransformer/ChainedTransformer 触发链式反射到 Runtime.exec

Ysoserial - 最权威的 Java 反序列化利用工具
https://github.com/frohoff/ysoserial
包含 CommonsCollections1-7 等多条利用链的完整实现
查看 src/main/java/ysoserial/payloads/CommonsCollections*.java
Commons Collections 官方仓库

https://github.com/apache/commons-collections
可以查看源码和历史版本
Java 反序列化漏洞学习资源

https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet
包含 Commons Collections 利用链的详细说明
CommonsCollections 利用链分析和 PoC

https://github.com/Maskhe/javasec
中文 Java 安全学习资源,包含详细的 CC 链分析
Java 反序列化漏洞实验环境

  • Fastjson:反射调用 setter/构造器,结合自动类型与 gadget 链造成任意代码执行

Fastjson 官方仓库
https://github.com/alibaba/fastjson
官方仓库,可以查看历史漏洞修复记录
Fastjson 漏洞利用和 Gadget 链研究

https://github.com/LeadroyaL/fastjson-blacklist
收集了 Fastjson 的黑名单绕过技巧和 gadget 链
Fastjson 漏洞复现环境

https://github.com/vulhub/vulhub/tree/master/fastjson
Vulhub 提供的 Fastjson 漏洞复现环境
Fastjson 反序列化漏洞利用工具

https://github.com/c0ny1/FastjsonExploit
Fastjson 反序列化漏洞利用工具
Fastjson 漏洞分析和 PoC 集合

https://github.com/safe6Sec/Fastjson
包含多个版本的漏洞分析和 PoC
Ysoserial - Java 反序列化 Gadget 链工具

反射的应用场景

1. 插件系统

许多产品需要支持用户通过插件扩展功能,插件的具体实现在运行时由用户提供。程序在设计时并不确定会加载哪些插件类或调用哪些方法,而是在运行时动态加载插件类,并通过反射调用相关方法。
以IDEA插件为例:

  • 插件以JAR包形式存在,包含具体实现类和plugin.xml配置文件
  • IDEA使用反射配合类加载器动态加载插件入口类
  • 实例化插件对象并调用其方法

2. 框架开发

Spring、Hibernate等框架大量使用反射:

  • 依赖注入(DI):通过反射创建对象并注入依赖
  • AOP(面向切面编程):动态代理需要反射支持
  • ORM映射:通过反射将数据库记录映射为Java对象

3. 序列化与反序列化

JSON、XML等数据格式的序列化框架(如Jackson、Gson)使用反射:

  • 动态读取对象的字段
  • 将数据转换为对象实例

4. 单元测试

测试框架(如JUnit)使用反射:

  • 自动发现和执行测试方法
  • 访问私有方法进行测试

反射实战示例

示例1:基本概念演示

反射允许程序在运行时动态地访问类的结构和行为,包括类、方法、字段、构造函数等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void introduce() {
System.out.println("My name is " + name + " and I am " + age + " years old.");
}
// 获取年龄
public int getAge() {
return age;
}
}

示例2:获取Class对象的三种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws Exception {
// 方式1:通过类字面量
Class<?> clazz1 = Person.class;

// 方式2:通过Class.forName()
Class<?> clazz2 = Class.forName("com.test.Person");

// 方式3:通过对象的getClass()方法
Person person = new Person("John", 30);
Class<?> clazz3 = person.getClass();

// 三种方式获取的是同一个Class对象
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true
}

示例3:获取构造方法并创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;

// 获取public类型的所有构造方法
Constructor<?>[] constructors = clazz.getConstructors();

// 获取指定参数的Constructor
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);

// 通过反射创建对象
Object personInstance = constructor.newInstance("John", 30);
System.out.println(personInstance);
}

示例4:获取和操作字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) throws Exception {
Person person = new Person("erosion2020", 14);
Class<?> clazz = Person.class;

// 获取所有有访问权限的字段
Field[] pubFields = clazz.getFields();

// 获取所有字段,包括私有的
Field[] fields = clazz.getDeclaredFields();

// 获取私有字段name
Field name = clazz.getDeclaredField("name");

// 设置可访问(绕过访问控制)
name.setAccessible(true);

// 修改字段值
name.set(person, "AcidEtch");
System.out.println(name.get(person)); // 输出: AcidEtch
}

示例5:获取和调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;

// 获取所有包含了访问权限的方法
Method[] pubMethods = clazz.getMethods();

// 获取所有方法,包括私有的
Method[] methods = clazz.getDeclaredMethods();

// 动态调用String.substring(int)方法
String name = "AcidEtch";
Method substring = String.class.getMethod("substring", int.class);
System.out.println(substring.invoke(name, 3)); // 输出: dEtch

// 调用静态方法Integer.parseInt(String)
Method parseInt = Integer.class.getMethod("parseInt", String.class);
Integer n = (Integer) parseInt.invoke(null, "114514");
System.out.println(n); // 输出: 114514
}

示例6:创建实例并调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;

// 获取指定参数的构造方法
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);

// 通过反射来创建对象
Object personInstance = constructor.newInstance("John", 30);

// 获取introduce方法
Method introduceMethod = clazz.getMethod("introduce");

// 通过反射调用方法
introduceMethod.invoke(personInstance);
// 输出: My name is John and I am 30 years old.
}

示例7:反射执行系统命令

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws Exception {
// 通过反射执行Runtime.exec()命令
Class.forName("java.lang.Runtime")
.getMethod("exec", String.class)
.invoke(
Class.forName("java.lang.Runtime")
.getMethod("getRuntime")
.invoke(Class.forName("java.lang.Runtime")),
"cmd /c start"
);
}

示例8:修改final字段

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
class Example {
private final String message = "Initial Value";

public String getMessage() {
return message;
}
}

public class ModifyFinalField {
public static void main(String[] args) throws Exception {
Example example = new Example();

// 输出原始值
System.out.println("Before modification: " + example.getMessage());

// 获取message字段的Field对象
Field messageField = Example.class.getDeclaredField("message");

// 设置为可访问
messageField.setAccessible(true);

// 修改final字段的值
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);

// 清除final修饰符的影响
modifiersField.setInt(messageField,
messageField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);

// 修改字段值
messageField.set(example, "Modified Value");

// 输出修改后的值
System.out.println("After modification: " + example.getMessage());
// 输出: After modification: Modified Value
}
}

总结

反射是Java的强大特性,它提供了运行时动态操作类的能力,是框架开发的基础。但反射也带来了性能开销和安全风险,需要谨慎使用。在实际开发中,应该:

  • 理解反射的工作原理和应用场景
  • 注意性能影响,合理优化
  • 重视安全问题,做好防护措施
  • 遵循最佳实践,编写健壮的代码
  • 标题:
  • 作者: Parad0x7e
  • 创建于 : 2025-11-19 20:56:05
  • 更新于 : 2025-11-21 01:23:01
  • 链接: https://note.easyctf.top/2025/11/19/JavaSec/Java反射/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。