顾名思义
反射允许对成员变量,成员方法和构造方法的信息进行编程访问
获取class对象
获取字段(成员变量)-》获取修饰符-》获取名字-》获取类型-》获取赋值/获取值
获取构造方法-》获取修饰符-》获取名字-》获取形参-》创建对象
获取成员方法。-》获取修饰符-》获取名字-》获取形参-》获取返回值-》抛出的异常-》获取注解-》运行方法
1.Class.forName("全类名");
2.类名.class
3.对象.getClass();
.java-.class(源代码阶段)-》是这个1.Class.forName("全类名");
A.class-》 内存加载阶段-》是这个2.类名.class
A a=new A();运行阶段-》是这个3.对象.getClass();
首先写好javaben配置环境
然后封装一下student类
public class student {
private String name;
private int age ;
public student(){
}
public student(String name) {
this.name = name;
}
protected student(int age){
this.age = age;
}
private student(String name,int age){
this.name =name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName(){return name;}
/**
*
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
*
*
* @return age
*/
public int getAge() {
return age;
}
}
第一种方式
特点:最为常用的
全类名 :包名加类名

第二种方式
特点:一般更多的是当做参数进行传递

照样可以输出student类
第三种方式
特点:当我们已经有了这个类的对象是,才可以使用

在Java当中,万物皆对象
class中用于获取构造方法的方法
1.返回所有公共构造方法对象的数组
Constructor<?>[] getConstructors()
2.返回所有构造方法对象的数组
Constructor<?>[] getDeclaredConstructors()
3.返回单个公共构造方法对象
ConstructorgetConstructor(Class<?>... parameterTypes)
4.返回单个构造方法对象
ConstructorgetDeclaredConstructor(Class<?>... parameterTypes)
constructor类中用于创建对象的方法
newInstance(Object... initargs)
根据指定的构造方法创建对象
setAccessible(boolean flag)
设置为true,表示取消访问控制
1.获取class字节码文件对象
Class<?> clazz = Class.reflect("com.itheima.myreflect2.Student");
2.获取构造方法
Constructor[] cons = clazz.getConstructors();
for (Constructor con : cons){
System.out.println(con);
}
控制台打印
public student() //反射空参数
public student(java.lang.String)//反射字符串
可以发现都获取到了public的构造方法

Class clazz =Class.forName("student");
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(cons2);
}
运行,发现四个构造方法全部被反射到了

进行对比,发现确实是这样

不指定类型,获取空参构造
Constructor con1 = clazz.getDeclaredConstructor();
System.out.println(con1);

指定类型,获取指定类型构造,当然getDeclaredConstructor方法是获取所有构造方法的,所有这里能够获取
到public方法
Constructor con2 = clazz.getDeclaredConstructor(String.class);
System.out.println(con2);

如果尝试不用getDeclaredConstructor方法,我们来试一试获取一下Protected属性
Constructor con3 = clazz.getConstructor(int.class);
System.out.println(con3);

可以看到程序报错了,在33行
我们使用getDeclaredConstructor()方法,发现就可以成功获取了

获取指定的两个参数a
Constructor con4 = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(con4);

既然能够反射出对象的修饰符和类型,那么就能用一些其他方法来进行修饰
这样就能以整数的形式打印出
int modifiers = con4.getModifiers();
System.out.println(modifiers);

还可以获取参数,参数个数,参数类型

我们创建一个参数数组,将其打印出来
Parameter[] parameters = con4.getParameters();
for (Parameter parameter : parameters){
System.out.println(parameter);

student stu =(student) con4.newInstance("张三", 20);
System.out.println(stu);

发现报错了,原来是修饰符是Private
发现getDeclaredConstructor()方法只能查看方法的权限,达不到修改方法的权限
在上面添加一个
setAccessible(true);//临时取消权限的校验

然后发现程序是不报错了,但是应该是版本兼容的问题导致不能显示出来
这种方式也称作暴力反射
class类中用于获取成员变量的方法
Field[] getFields()-》返回所有公共成员变量对象的数组
Field[] getDeclaredFields()-》返回所有成员变量对象的数组
Field getField(String name)-》返回单个公共成员变量对象
Field getDeclaredField(String name)-》返回单个成员变量对象
filed类中用于创建对象的方法
void set(Object obj, Object value--》赋值
Object get(Object obj)-》获取值
Class clazz =Class.forName("Student");//获取class字节码文件的对象
Field[] fields = clazz.getFields();//获取公共成员变量
尝试用数组进行打印
for (Field field : fields){
System.out.println(field);
}

发现只包含了gender
查看student发现确实这样

将getFields改为getDeclaredFields
Class clazz =Class.forName("Student");//获取class字节码文件的对象
Field[] fields = clazz.getDeclaredFields();//获取所有成员变量
for (Field field : fields){
System.out.println(field);
}
打印输出,发现全部都输出来了

下列语句
Field gender = clazz.getField("gender");
System.out.println(gender);
打印结果

尝试获取name

发现代码报错了,因为name是私有的,这里的方法获取不到,没有这个权限
改一下
Field gender = clazz.getDeclaredField("name");
System.out.println(gender);

Field name = clazz.getDeclaredField("name");
System.out.println(name);
// 获取权限修饰符
int modifiers = name.getModifiers();
System.out.println(modifiers);

发现这是私有的2
String n = name.getName();
System.out.println(n);
获取成员变量名

获取成员变量的数据类型
Class<?> type = name.getType();
System.out.println(type);

获取成员变量记录的值
Student s = new Student("zhangsan",23,"男");
name.setAccessible(true);
Object value = name.get(s);
System.out.println(value);
运行

修改对象里面记录的值
name.set(s,"lisi");
System.out.println(s);

Class 类中用于获取成员方法的方法
Method[] getMethods()-》返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods()-》返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>... parameterTypes)-》返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>... parameterTypes)-》返回单个成员方法对象
Method 类中用于创建对象的方法

示例代码
public class Student {
private int age;
private String name;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void sleep(){
System.out.println("睡觉");
}
private void eat(String something){
System.out.println("在吃"+something);
}
public String toString() {
return "Student{name='" + name + "', age=" + age + '}';
}
}
Class clazz =Class.forName("Student");//获取class字节码文件的对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
打印输出,可以看到很多方法,不过都是public修饰的,但是很多方法都没有我定义的

改为DeclareMethods,可以看到方法少了很多
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);

然后发现报错

发现这里的方法是private的,私有的,所以我们需要用获取所有成员方法
//获取指定的单一方法
Method m = clazz.getDeclaredMethod("eat", String.class);
System.out.println(m);

int modifiers = m.getModifiers();
System.out.println(modifiers);

打印结果发现是2,那么就证明是私有
String name = m.getName();
System.out.println(name);

可以发现种类很多

Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
可以发现是string类型的

在eat这里我设置抛出了IO和NULL两个异常

Class[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);

发现成功抛出两个设置的异常
其中s表示方法的调用者。参数“汉堡包”表示在调用方法的时候传递的实际参数
Student s = new Student();
m.setAccessible(true);
m.invoke(s,"汉堡包");


Student s = new Student();
m.setAccessible(true);
Object result=m.invoke(s,"汉堡包");
System.out.println(result);
运行发现

反射的作用


私有的学生类
public class Student {
private String name;
private int age;
private char gender;
private double height;
private String hobby;
public Student(String name, int age, char gender, double height, String hobby) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
this.hobby = hobby;
}
// Getters for all fields
public String getName() { return name; }
public int getAge() { return age; }
public char getGender() { return gender; }
public double getHeight() { return height; }
public String getHobby() { return hobby; }
}
私有的老师类
public class Teacher {
private String name;
private double salary;
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
// Getters for all fields
public String getName() { return name; }
public double getSalary() { return salary; }
}
通过反射可以做到增删改查
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
// 创建 Student 对象并打印属性值
Object studentObj = createAndPrintObject(Student.class, "小A", 23, '女', 167.5, "睡觉");
// 创建 Teacher 对象并打印属性值
Object teacherObj = createAndPrintObject(Teacher.class, "播妞", 10000.0);
}
public static Object createAndPrintObject(Class<?> clazz, Object... params) throws Exception {
// 确定参数类型
Class<?>[] paramTypes = new Class<?>[params.length];
for (int i = 0; i < params.length; i++) {
if (params[i] instanceof Integer) {
paramTypes[i] = int.class;
} else if (params[i] instanceof Character) {
paramTypes[i] = char.class;
} else if (params[i] instanceof Double) {
paramTypes[i] = double.class;
} else {
paramTypes[i] = params[i].getClass();
}
}
// 获取构造方法并创建对象
Constructor<?> constructor = clazz.getConstructor(paramTypes);
Object obj = constructor.newInstance(params);
// 打印字段
printFields(obj);
return obj;
}
public static void printFields(Object obj) throws Exception {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + "=" + field.get(obj));
}
}
}

反射这个java机制为啥不安全???
Java 的核心设计原则之一是封装性(Encapsulation),即类的内部实现细节应该对外隐藏。通过反射,可以访问类的私有字段和方法,绕过访问控制检查。
示例:
class Person {
private String name;
}
// 使用反射访问私有字段
Field field = Person.class.getDeclaredField("name");
field.setAccessible(true); // 绕过访问限制
Person p = new Person();
field.set(p, "Alice"); // 修改私有字段
影响:
• 破坏了类的设计意图。
• 可能导致对象状态被非法修改,引发不可预测的行为。
Java 语言本身具有访问修饰符(如 private、protected、public)来控制代码的可访问性。但反射可以通过 setAccessible(true) 直接绕过这些限制。
示例:
Method method = MyClass.class.getDeclaredMethod("secretMethod");
method.setAccessible(true);
method.invoke(obj);
影响:
• 恶意代码可以调用本应受保护的方法。
• 安全敏感操作可能被非法执行。
反射调用方法或访问字段比直接调用慢很多,因为 JVM 需要进行额外的安全检查和动态解析。
性能对比(大致):
操作 耗时
普通方法调用 1 ns
反射调用 100 - 500 ns
虽然现代 JVM 已经对反射进行了优化,但在高频调用场景下仍然存在显著性能损耗。
从 Java 9 开始引入了模块系统(JPMS),旨在限制外部代码访问 JDK 内部 API。然而,反射仍然可以通过 --add-opens 或 --add-exports 参数绕过这些限制。
示例:
java --add-opens java.base/java.lang=ALL-UNNAMED ...
影响:
• 导致 JDK 内部实现暴露给应用程序。
• 增加了维护和兼容性风险。
如果应用程序使用反射加载并执行任意类中的方法,可能会成为攻击入口。例如:
• 动态加载恶意类。
• 执行非预期的私有方法。
• 修改关键数据结构。
特别是在 Web 应用、插件系统、远程调用等场景中,反射如果不加以限制,容易成为攻击目标。
反射操作是在运行时进行的,因此许多错误(如方法名拼写错误、参数类型不匹配)只有在运行时才会暴露出来。
示例:
Method method = MyClass.class.getMethod("nonExistentMethod"); // 编译通过
Object result = method.invoke(obj); // 运行时报错
影响:
• 增加调试难度。
• 降低代码健壮性。
安全问题 描述
破坏封装 可以访问私有成员,绕过访问控制
安全漏洞 恶意代码可通过反射执行危险操作
性能问题 反射调用效率低,影响性能
编译无报错 错误只能在运行时发现
模块限制失效 可绕过 JPMS 的模块隔离机制