反射的概念
Java 反射(Reflection)是 Java 非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建 Java 类实例、调用任意的类方法、修改任意的类成员变量值等。Java 反射机制是 Java 语言的动态性的重要体现,也是 Java 的各种框架底层实现的灵魂。
反射技术,指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)
获取 Class 对象
反射的第一步:是将字节码加载到内存,我们需要获取到的字节码对象。
以下四种方法可以获取到一个类的字节码文件。
类名.Class
Class.forName("com.qwesec.xxx.Test")
ClassLoader.getSystemClassLoader().loadClass("com.qwesec.xxx.Test")
- 实例化对象通过
对象.getClass()也可以获得
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
| public class User { private String nickname; private String username; private String password;
public User() {
}
private User(String password) { this.password = password; }
public User(String nickname, String username, String password) { this.nickname = nickname; this.username = username; this.password = password; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ReflecTest1 { public static void main(String[] args) throws ClassNotFoundException { Class<User> userClass1 = User.class; System.out.println(userClass1); System.out.println(userClass1.getSimpleName()); System.out.println(userClass1.getName());
Class<?> userClass2 = Class.forName("com.qwesec.User"); System.out.println(userClass2);
Class<?> userClass3 = ClassLoader.getSystemClassLoader().loadClass("com.qwesec.User"); System.out.println(userClass3);
User user = new User(); Class<?> userClass4 = user.getClass(); System.out.println(userClass4); } }
|
运行结果:

获取类的构造器
获取类的构造器:

获取构造器的作用:依然是初始化对象返回

获取类的所有构造器
可以通过getDeclaredConstructors()方法获取类的所有构造器。
首先准备一个用于被获取构造器的 User 类。
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
| public class User { private String nickname; private String username; private String password;
public User() {
}
private User(String password) { this.password = password; }
public User(String nickname, String username, String password) { this.nickname = nickname; this.username = username; this.password = password; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; } }
|
写一个测试类,获取 User 类的所有构造器:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ReflectTest2 { public static void main(String[] args) throws ClassNotFoundException { Class aClass = Class.forName("com.qwesec.User"); Constructor[] constructors = aClass.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println("构造器名称: " + constructor.getName() + "参数个数: " + constructor.getParameterCount()); } } }
|
运行结果如下, 此时就将 User 类的无参构造和有参构造获取出来了。

获取类的单个构造器
可以通过 getConstructor()方法获取类的单个构造器(仅能获取 public 修饰的构造器)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ReflectTest3 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class aClass = Class.forName("com.qwesec.User"); Constructor constructor1 = aClass.getConstructor(); Constructor constructor2 = aClass.getConstructor(String.class, String.class, String.class); System.out.println(constructor1); System.out.println(constructor2); } }
|
输出结果如下:

获取类的私有构造器
可以通过getDeclaredConstructor()方法获取类的某个私有构造器。
1 2 3 4 5 6 7 8 9
| public class ReflectTest4 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class aClass = Class.forName("com.qwesec.User"); Constructor constructor1 = aClass.getDeclaredConstructor(String.class); System.out.println(constructor1); } }
|
反射获取类的构造器作用
通过反射获取类的构造器作用是为了初始化对象并返回。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ReflectTest5 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User"); Constructor constructor = aClass.getConstructor(String.class, String.class, String.class); User user = (User) constructor.newInstance("熊先生", "x1ongsec", "123456"); System.out.println("昵称: " + user.getNickname()); System.out.println("用户名: " + user.getUsername()); System.out.println("密码: " + user.getPassword()); } }
|
运行效果如下:

如果获取的是通过 private 修饰的构造器,则通过 newInstance() 函数实例化前,需要先讲 setAccessible(true) 设置为 true,表示禁止检查访问限制。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ReflectTest6 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User"); Constructor constructor = aClass.getDeclaredConstructor(String.class); constructor.setAccessible(true); User user = (User) constructor.newInstance("123456"); System.out.println("密码: " + user.getPassword()); } }
|
如果在调用私有构造方法之前没有关闭访问检查,则会出现如下错误:

获取和使用类的成员变量
Class 提供了从类中获取成员变量的方法。

获取到成员变量的作用:依然是赋值、取值。

这里为了演示我们在 User 类中添加几个公有的属性。
1 2 3 4 5 6 7 8 9
| public class User { private String nickname; private String username; private String password; public String age; public String gender;
}
|
获取所有公有成员变量
1 2 3 4 5 6 7 8 9 10 11
| public class ReflectTest7 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User"); Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println(field); } } }
|

获取所有成员变量
1 2 3 4 5 6 7 8 9 10 11
| public class ReflectTest7 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User"); Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } } }
|

获取和操作指定的成员变量
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
| public class ReflectTest8 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class aClass = Class.forName("com.qwesec.User");
Constructor constructor = aClass.getConstructor(); User user = (User) constructor.newInstance();
Field nickname = aClass.getDeclaredField("nickname"); nickname.setAccessible(true); nickname.set(user, "熊先生");
Field username = aClass.getDeclaredField("username"); username.setAccessible(true); username.set(user, "x1ongsec");
Field password = aClass.getDeclaredField("password"); password.setAccessible(true); password.set(user, "123456");
Field gender = aClass.getDeclaredField("gender"); gender.set(user, "男");
Field age = aClass.getDeclaredField("age"); age.set(user, "18");
for (Field declaredField : aClass.getDeclaredFields()) { declaredField.setAccessible(true); System.out.println(declaredField.get(user)); } } }
|
执行结果如下:

获取类的成员方法
Class 提供了从类中获取成员方法的 API。

成员方法的作用:依然是方法的执行。

在 User 中添加几个方法用于测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public void login() { System.out.println("用户登录了"); }
public void login(String username) { System.out.println("用户" + username + "登录了"); }
private void logout() { System.out.println("用户注销了"); }
private void logout(String username) { System.out.println("用户" + username + "注销了"); }
|
获取所有公有方法
1 2 3 4 5 6 7 8 9 10 11
| public class ReflectTest9 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User"); Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println(method); } } }
|
运行结果如下:

可以发现除了我们自己定义的方法以外,其中还有父类对象 Object 的公有方法,这是因为,在 Java 中默认的所有类都继承自父类 Object。
获取所有方法
getDeclaredMethods() 仅能获取自身的(public、private、protected)所有方法,不能获取父类的。
1 2 3 4 5 6 7 8 9 10 11
| public class ReflectTest10 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User"); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); } } }
|

获取和操作指定的成员方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class ReflectTest11 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class aClass = Class.forName("com.qwesec.User");
User user = (User) aClass.newInstance();
Method login1 = aClass.getMethod("login"); login1.invoke(user);
Method login2 = aClass.getMethod("login", String.class); login2.invoke(user, "x1ongsec");
Method logout1 = aClass.getDeclaredMethod("logout", String.class); logout1.setAccessible(true); logout1.invoke(user, "x1ongsec"); } }
|

命令执行
不使用反射命令执行
1 2 3 4 5
| public class ExecCmd { public static void main(String[] args) throws IOException { Runtime.getRuntime().exec("Open -a Calculator"); } }
|

使用反射调用 Runtime 执行 exec
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
| public class ReflectExecCmd { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, InterruptedException { Class<?> runtimeC = Class.forName("java.lang.Runtime"); Constructor<?> runtimeConstructor = runtimeC.getDeclaredConstructor(); runtimeConstructor.setAccessible(true); Runtime runimeInstance = (Runtime) runtimeConstructor.newInstance(); Method execCmd = runtimeC.getMethod("exec", String.class); Process p = (Process) execCmd.invoke(runimeInstance, "whoami"); InputStream inputStream = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { System.out.println("命令输出:" + line); }
p.waitFor(); reader.close(); inputStream.close();
} }
|

Java 反射机制总结
Java 反射机制是 Java 动态性中最为重要的体现,利用反射机制我们可以轻松实现 Java 类的动态调用。Java 的大部分框架均基于反射机制实现(如:Spring MVC、ORM框架 等),同时 Java 反射在漏洞利用代码编写、代码审计、绕过 RASP 方法限制等场景中也起到了至关重要的作用。
参考文章