目录
1. 什么是反射?
2.反射的核心操作
2.1 反射组成相关的类
2.2 反射常见使用的方法
2.3 如何获取Class对象?
2.4 获取成员变量Field
2.5 获取成员方法Method
2.6 获取构造函数Constructor
2.7 反射创建类对象及调用方法
2.7.1 创建类对象
2.7.2 调用实例方法
2.7.3 调用静态方法
2.8 利用反射构造Runtime类执行
2.9 设置setAccessible(true)暴力访问权限
1. 什么是反射?
反射是一种动态机制,能够在程序运行时:
- 动态获取类信息(属性、方法、构造器)
- 突破封装性限制(访问私有成员)
- 动态创建对象与调用方法
想象你在餐厅点餐,服务员递给你一本菜单。传统方式是根据菜单上的菜品直接点单,但反射就像你拿到菜单后,不仅能看到菜品名称,还能知道每道菜的原料、烹饪方法,甚至可以要求厨师按照你的方式调整口味。Java 反射机制赋予程序在运行时动态解析类结构的能力,让代码具备了类似 "透视" 的魔法。
2.反射的核心操作
2.1 反射组成相关的类
反射机制相关操作一般位于java.lang.reflect包中。
而java反射机制组成需要重点注意以下的类:
java.lang.Class:类对象;
java.lang.reflect.Constructor:类的构造器对象;
java.lang.reflect.Field:类的属性对象;
java.lang.reflect.Method:类的方法对象;
2.2 反射常见使用的方法
获取类的方法:forname
实例化类对象的方法:newInstance
获取函数的方法:getMethod
执行函数的方法:invoke
2.3 如何获取Class对象?
在反射中,我们想获取一个类或调用一个类的方法,需要先获取到该类的Class对象。而Class类构造器是私有的,所以只有JVM能够创建Class对象(了解),所以是无法通过创建对象的方式来获取class对象的,一般我们获取class对象就有以下三种方法:
// 三种获取Class对象的方式
Class<?> clazz1 = String.class; // 通过类名的属性class获取。
Class<?> clazz2 = "test".getClass(); // 实例化一个对象,之后在调用getClass()方法。
Class<?> clazz3 = Class.forName("java.util.Date"); // 调用Class类中的forName方法,将字节码文件加载进内存,返回Class对象。动态加载
而这三种获取Class类方式中,一般使用第三种通过Class.forName方法去动态加载类。且使用forName就不需要import导入其他类,可以加载我们任意的类。
getClass()方法,需要本身创建一个对象,就没有了使用反射机制意义。
类.class属性,需要导入类的包,依赖性太强,在大型项目中容易抛出编译错误;
2.4 获取成员变量Field
获取成员变量Field位于java.lang.reflect.Field包中
Field[] getFields() :获取所有public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field getDeclaredField(String name) 获取指定的成员变量
package com.reflect;import java.lang.reflect.Field;public class FieldTest {public String name;public String profession;protected int age;private String number;char sex;public static void main(String[] args){try{Class c1 = Class.forName("com.reflect.FieldTest"); // 创建Class对象Field[] fieldArray1 = c1.getDeclaredFields(); //获取全部成员变量Field[] fieldArray2 = c1.getFields();// 获取全部public成员变量System.out.println("全部成员变量");for (Field field : fieldArray1){System.out.println(field.getName());}System.out.println("-------分割线---------");System.out.println("全部public成员变量");for (Field field : fieldArray2){System.out.println(field.getName());}System.out.println("-------分割线---------");System.out.println("获取指定名称的public修饰的成员变量");Field fieldArray3 = c1.getField("name"); // 获取指定名称的public修饰的成员变量System.out.println(fieldArray3.getName());System.out.println("-------分割线---------");System.out.println("获取指定的成员变量");Field fieldArray4 = c1.getDeclaredField("number"); // 获取指定的成员变量System.out.println(fieldArray4.getName());} catch (Exception e) {e.printStackTrace();}}
}
2.5 获取成员方法Method
Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法
Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法
//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型
Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法
Method[] getDeclaredMethods() // 获取该类中的所有方法
package com.reflect;import java.lang.reflect.Method;public class MethodTest {public void study(String s) {System.out.println("学习中..." + s);}protected void run() {System.out.println("跑步中...");}void eat() {System.out.println("吃饭中...");}private String sleep(int age) {System.out.println("睡眠中..." + age);return "sleep";}public static void main(String[] args) {try {Class c = Class.forName("com.reflect.MethodTest"); // 创建Class对象Method[] methods1 = c.getDeclaredMethods(); // 获取所有该类中的所有方法Method[] methods2 = c.getMethods(); // 获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法System.out.println("获取所有该类中的所有方法");for (Method m:methods1) {System.out.println(m);}System.out.println("-------分割线---------");System.out.println("获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法");for (Method m:methods2) {System.out.println(m);}System.out.println("-------分割线---------");System.out.println("获取study方法");Method methods3 = c.getMethod("study", String.class); // 获取study方法System.out.println(methods3);System.out.println("-------分割线---------");System.out.println("获取sleep方法");Method method4 = c.getDeclaredMethod("sleep", int.class); // 获取sleep方法System.out.println(method4);} catch (Exception e) {e.printStackTrace();}}
}
2.6 获取构造函数Constructor
Constructor<?>[] getConstructors() :只返回public构造函数
Constructor<?>[] getDeclaredConstructors() :返回所有构造函数
Constructor<> getConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的public构造函数
Constructor<> getDeclaredConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的构造函数
package com.reflect;import java.lang.reflect.Constructor;
public class ConstructorTest {public ConstructorTest() {System.out.println("无参构造函数");}public ConstructorTest(String name) {System.out.println("有参构造函数" + name);}private ConstructorTest(boolean n) {System.out.println("私有构造函数");}public static void main(String[] args) {try {Class c1 = Class.forName("com.reflect.ConstructorTest");Constructor[] constructors1 = c1.getDeclaredConstructors();Constructor[] constructors2 = c1.getConstructors();System.out.println("只返回public构造函数");for (Constructor c : constructors1) {System.out.println(c);}System.out.println("-------分割线---------");System.out.println("返回所有构造函数");for (Constructor c : constructors2) {System.out.println(c);}System.out.println("-------分割线---------");System.out.println(" 匹配和参数配型相符的public构造函数");Constructor constructors3 = c1.getConstructor(String.class);System.out.println(constructors3);System.out.println("-------分割线---------");System.out.println(" 匹配和参数配型相符的构造函数");Constructor constructors4 = c1.getDeclaredConstructor(boolean.class);System.out.println(constructors4);} catch (Exception e) {e.printStackTrace();}}
}
2.7 反射的应用
在Java的反射机制中,java.lang.reflect.Method
类的invoke()
方法扮演着核心角色。它允许我们在运行时动态调用对象的方法,无论是实例方法还是静态方法。一般会和getMethod方法配合进行调用。
public Object invoke(Object obj, Object... args)
2.7 反射创建类对象及调用方法
2.7.1 创建类对象
一般我们使用Class对象的newInstance()方法来进行创建类对象。只需要通过forname方法获取到的class对象中进行newInstance方法创建即可。
Class c = Class.forName("com.reflect.MethodTest"); // 创建Class对象
Object m1 = c.newInstance(); // 创建类对象
2.7.2 调用实例方法
当需要调用一个对象的实例方法时,invoke()
的第一个参数应传入该对象的实例。
package com.reflect;import java.lang.reflect.Method;public class InvokeExample {public void printMessage(String message) {System.out.println("实例方法输出: " + message);}public static void main(String[] args) throws Exception {// 使用完整的类名(包含包路径)Class<?> clazz = Class.forName("com.reflect.InvokeExample");// 创建类的实例Object instance = clazz.newInstance();// 获取printMessage方法对象Method method = clazz.getMethod("printMessage", String.class);// 调用实例方法method.invoke(instance, "Hello, World!");}
}
2.7.3 调用静态方法
对于静态方法,invoke()
的第一个参数应传入null
,因为静态方法属于类本身,而非类的实例。
package com.reflect;import java.lang.reflect.Method;public class InvokeExample {public static int add(int a, int b) {return a + b;}public static void main(String[] args) throws Exception {// 获取MathUtils的Class对象Class<?> clazz = Class.forName("com.reflect.InvokeExample");// 获取add方法对象Method method = clazz.getMethod("add", int.class, int.class);// 调用静态方法(第一个参数为null)Object result = method.invoke(null, 5, 3);System.out.println("静态方法结果: " + result);}
}
2.8 利用反射构造Runtime类执行
平时漏洞测试中,如何使用java反射中如何获取Runtime类来命令执行测试弹出计算器。
尝试通过反射机制调用Runtime.getRuntime().exec()
方法,执行系统命令打开系统的计算器应用。
package com.reflect;import java.lang.reflect.Method;public class RuntimeTest {public static void main(String[] args) throws Exception {Class c1 = Class.forName("java.lang.Runtime");Object m = c1.newInstance();}
}
在Java反射机制中,当尝试通过Class.newInstance()
直接实例化一个类时,若该类的构造函数被声明为private
,则会抛出IllegalAccessException
。原因是因为在Java标准库中,java.lang.Runtime
类采用了经典的单例模式设计,其通过私有构造函数+静态工厂方法的组合,完美实现了对单例的管控。
1.私有构造函数:
Runtime
类的构造函数被显式声明为private
,禁止外部直接通过new
关键字或反射创建实例。2.静态工厂方法:提供
public static Runtime getRuntime()
方法作为唯一获取实例的途径,确保全局唯一性。
所以通过反射获取getRuntime
静态方法并调用,绕过了私有构造函数的限制。
package com.reflect;import java.lang.reflect.Method;public class RuntimeDemo {public static void main(String[] args) throws Exception {Class<?> clazz = Class.forName("java.lang.Runtime");// 获取getRuntime方法(静态方法)Method getRuntime = clazz.getMethod("getRuntime");Object runtime = getRuntime.invoke(null); // 静态方法传null// 调用exec方法Method exec = clazz.getMethod("exec", String.class);exec.invoke(runtime, "calc.exe"); // 弹出计算器(Windows)}
}
2.9 设置setAccessible(true)暴力访问权限
在Java反射体系中,java.lang.reflect.AccessibleObject
类扮演着关键角色。作为Field
、Method
和Constructor
类的共同基类,它提供了setAccessible()
方法这一"特权通道",使得反射机制能够突破访问控制限制。
AccessibleObject
├─ Field(字段反射类)
├─ Method(方法反射类)
└─ Constructor(构造器反射类)
所有反射对象都继承自AccessibleObject
,因此都拥有setAccessible()
方法。
所有我们最开始的代码再修改简略下,就能成功命令执行了。
package com.reflect;import java.lang.reflect.Constructor;public class RuntimeTest {public static void main(String[] args) throws Exception {Class c1= Class.forName("java.lang.Runtime");Constructor m = c1.getDeclaredConstructor();m.setAccessible(true);c1.getMethod("exec", String.class).invoke(m.newInstance(), "calc");}
}