问题:
// 直接按类字面量获取 Class<?> myClass = ClassTestA.class; // 全类名反射获取 Class<?> myClass = Class.forName("com.cambrianwenjie.demo.ClassTestA"); // 获取私有字段 Field privateField = myClass.getDeclaredField("name"); // 设置私有字段可访问 privateField.setAccessible(true); // 获取并修改私有字段的值 String fieldValue = (String) privateField.get(myClass);
以上代码在运行到获取并修改私有字段的值时,会提示
Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.String field com.cambrianwenjie.demo.ClassTestA.name to java.lang.Class at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java: 167 ) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java: 171 ) at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java: 58 ) at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java: 36 ) at java.lang.reflect.Field.get(Field.java: 393 )
疑惑:
forName获取到的就是类的全部信息,包括字段(Field)的信息。通过Class对象可以调用Field类中的get方法来获取字段的值,那么为什么会报错?
查看Field的get()方法的JavaDoc
大概翻译一下:
- 返回由该Field对象表示的字段在指定对象上的值。如果字段具有原始类型,则该值会自动包装为对象。
- 获取底层字段的值的方式如下:
- 如果底层字段是静态字段,则忽略obj参数;它可以为null。
- 否则,底层字段是实例字段。如果指定的obj参数为null,则该方法抛出NullPointerException异常。如果指定的对象不是声明底层字段的类或接口的实例,则该方法抛出IllegalArgumentException异常。
- 如果Field对象正在强制执行Java语言的访问控制,并且底层字段不可访问,则该方法抛出IllegalAccessException异常。如果底层字段是静态字段,并且尚未初始化声明该字段的类,则会初始化该类。
- 否则,从底层实例或静态字段中检索值。如果字段具有原始类型,则在返回之前将该值包装在对象中,否则按原样返回。
- 如果字段在obj的类型中被隐藏,则根据前面的规则获取字段的值。
问题原因:
关键就在于上面加粗斜体的部分,更关键的是加粗斜体部分的红字
类的字面量和全类名反射获取的Class都是类对象而非类的实例对象
此处解释下类对象和类的实例对象:
定义:
- 类对象(Class Object):表示整个类的定义,包括类的结构、方法、字段等信息。在Java中,类对象是在运行时由Java虚拟机(JVM)动态创建和管理的。
- 类的实例对象(Instance Object):表示类的一个具体实例,是类对象的一个实例化对象。在Java中,类的实例对象是通过使用new关键字或其他创建对象的方式创建的。
内容:
- 类对象:包含了类的定义和结构信息,例如类的方法、字段、构造函数、静态代码块等。类对象在内存中只有一份,对于一个类而言,类对象是唯一的。
- 类的实例对象:表示类的一个具体实例,每个实例都有自己的独特状态和数据。每次创建类的实例对象时,都会在内存中分配一块新的内存空间来存储实例的数据。
访问:
- 类对象:可以通过类字面量(例如ClassName.class)或Class.forName()方法来获取类对象。通过类对象可以获取类的各种信息,如方法、字段、注解等。
- 类的实例对象:通过创建对象的方式,使用new关键字或其他创建对象的方式来获取类的实例对象。通过实例对象可以访问和操作实例的成员变量和方法。
功能:
- 类对象:用于获取类的结构信息,进行反射操作,如动态创建对象、调用方法、访问字段等。
- 类的实例对象:用于表示类的一个具体实例,可以通过实例对象来访问和操作实例的成员变量和方法。
总的来说,类对象是类的定义和结构信息的表示,而类的实例对象是类的具体实例,每个实例有自己的状态和数据。类对象用于操作和获取类的结构信息,而类的实例对象用于访问和操作实例的成员变量和方法。
因此如果要调用Field的get()方法,需要传入一个实例化对象才可以。
比如将上述代码最后一个行改为:
// 获取并修改私有字段的值 String fieldValue = (String) privateField.get(myClass.newInstance());
标签:Field,反射,java,获取,私有,对象,字段,实例,小记 From: https://www.cnblogs.com/onejay/p/17934706.html