一文讲透 Java 中的反射

什么是反射

反射是 Java 中的一个特性,它允许程序在运行时获取自身的信息,并动态地操作类或对象的属性、方法和构造函数。通过反射,我们可以在事先不知道确切类名的情况下实例化对象、调用方法和设置属性。

反射机制的核心是Class对象,它代表一个类。Java 虚拟机(JVM)在加载类时会自动创建这个Class对象。

JVM 如何创建一个类

当我们编写一个类并进行编译时,编译器会将其转换为存储在.class文件中的字节码。在类加载过程中,JVM 使用ClassLoader读取.class文件,将字节码加载到内存中,并根据这些信息创建相应的Class对象。由于每个类在 JVM 中只加载一次,所以每个类都对应一个唯一的Class对象。

示例

 

arduino

代码解读

复制代码

public class User extends People {     public String name;     private int age;     private static int staticFiled = 10;     private final String sex;     protected String protectedFiled;     static {         System.out.println("静态方法执行");     }     public User(String name, String sex) {         this.name = name;         this.sex = sex;     }     private void privateMethod() {         System.out.println("我是私有方法");     }     public void publicMethod() {         System.out.println("我是公共方法");     } } public class People {     public String publicFiled;     private String privateFiled; }

获取Class对象的三种方式

  1. 第一种方法通过类名使用.class获取类对象。这是在编译时完成的,所以明确指定了类型User,不会导致任何错误。使用这种方法获取对象不会触发类初始化;只有在访问类的静态成员或实例时才会进行初始化。
 

ini

代码解读

复制代码

Class<User> userClass = User.class;

实例化一个对象:

 

arduino

代码解读

复制代码

User userInstance = userClass.getDeclaredConstructor(String.class, String.class).newInstance("张三", "男");

  1. 第二种方法通过对象的getClass()方法获取类对象。这种方法适用于从已实例化的类对象中获取类对象。请注意,类型不是User,而是通配符?,因为Class对象是从User的实例中获取的,实例的具体类型只能在运行时确定,而不是在编译时。
 

ini

代码解读

复制代码

User user = new User("张三", "男"); Class<?> userClass = user.getClass();

实例化一个对象:

 

ini

代码解读

复制代码

Constructor<?> constructor = userClass.getConstructor(String.class, String.class); User userInstance = (User) constructor.newInstance("张三", "男");

  1. 第三种方法使用静态方法Class.forName()通过全路径获取类对象。由于类型只能在运行时知道,所以类型是通配符?。通过这种方法获取类对象将立即触发类初始化。
 

ini

代码解读

复制代码

Class<?> userClass = Class.forName("org.example.reflect.entity.User");

创建一个实例:

 

ini

代码解读

复制代码

Constructor<?> constructor = userClass.getDeclaredConstructor(String.class, String.class); User userInstance = (User) constructor.newInstance("张三", "男");

在 Java 中访问对象字段

  1. 获取所有公共字段要获取所有公共字段,包括从父类继承的字段,使用getFields()
 

ini

代码解读

复制代码

Field[] fields = user.getFields(); for (Field field : fields) {     System.out.println(field); }

输出:

 

arduino

代码解读

复制代码

public java.lang.String org.example.reflect.entity.User.name public java.lang.String org.example.reflect.entity.People.publicField

  1. 获取所有声明的字段要获取类中所有声明的字段,无论其访问级别如何,使用getDeclaredFields()。这不包括从超类继承的字段:
 

ini

代码解读

复制代码

Field[] fields = user.getDeclaredFields(); for (Field field : fields) {     System.out.println(field); }

输出:

 

arduino

代码解读

复制代码

public java.lang.String org.example.reflect.entity.User.name private int org.example.reflect.entity.User.age private final java.lang.String org.example.reflect.entity.User.sex protected java.lang.String org.example.reflect.entity.User.protectedField

  1. 获取超类中的字段要获取超类中的字段,使用getSuperclass()
 

ini

代码解读

复制代码

Field[] fields = user.getSuperclass().getDeclaredFields(); for (Field field : fields) {     System.out.println(field); }

输出:

 

arduino

代码解读

复制代码

public java.lang.String org.example.reflect.entity.People.publicField private java.lang.String org.example.reflect.entity.People.privateField

  1. 获取特定字段要通过名称获取特定公共字段,使用getField(String name)。对于任何特定字段,无论其访问级别如何,使用getDeclaredField(String name)

  2. 处理不存在的字段尝试访问不存在的字段不会产生编译时错误,但会在运行时抛出异常:

 

ini

代码解读

复制代码

try {     Field nonExistentField = user.getDeclaredField("nonExistentField"); } catch (NoSuchFieldException e) {     e.printStackTrace(); }

输出:

 

makefile

代码解读

复制代码

java.lang.NoSuchFieldException: nonExistentField

  1. 设置字段值要设置私有静态字段的值,首先使其可访问:
 

ini

代码解读

复制代码

Class<?> userClass = Class.forName("org.example.reflect.entity.User"); Field staticField = userClass.getDeclaredField("staticField"); staticField.setAccessible(true); System.out.println(staticField.get(null));

如果字段是final的,仍然可以修改它:

 

ini

代码解读

复制代码

Field field = userClass.getDeclaredField("sex"); field.setAccessible(true); field.set(obj, "女生"); System.out.println(field.get(obj));

输出:

 

代码解读

复制代码

女生

访问方法

访问方法与访问字段类似:

  • getMethods()检索类及其超类中的所有公共方法。

  • getDeclaredMethods()检索类中所有声明的方法,无论访问级别如何。

  • getMethod(String name, Class<?>... parameterTypes)按名称和参数类型检索特定公共方法。

  • getDeclaredMethod(String name, Class<?>... parameterTypes)按名称和参数类型检索特定声明的方法,无论访问级别如何。

总结

从上面的示例中可以看出,以Declared为前缀的方法(如getDeclaredField)用于检索所有字段或方法,无论其访问级别如何。相比之下,没有Declared的方法(如getField)仅检索公共字段或方法。

反射允许绕过访问控制检查。用privatefinal修饰的字段和方法可以被访问和修改,这破坏了封装性。因此,应该谨慎使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值