Java setAccessible() 方法
1.直接通过 Field 访问 private 对象会报错
测试代码如下:
public void accessPrivateObjects(){
Employee employee = new Employee("hrm",500,2000,11,11);
for(Field f : employee.getClass().getDeclaredFields()){
try{
System.out.println(f.get(employee));
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
}
public class Employee extends People{
private double salary;
private LocalDate hireDay;
...
}
报错:
java.lang.IllegalAccessException: class fiveChapter.FiveChapter cannot access a member of class fiveChapter.Employee with modifiers "private"
原因:
由于
salary
是一个私有域, 所以 get 方法将会抛出一个IllegalAccessException
。只有利用get
方法才能得到可访问域的值。除非拥有访问权限,否则Java 安全机制只允许査看任意对象有哪些域, 而不允许读取它们的值。反射机制的默认行为受限于 Java 的访问控制。然而, 如果一个 Java 程序没有受到安全管理器的控制, 就可以覆盖访问控制。 为了达到这个目的, 需要调用 Field、 Method 或Constructor 对象的
setAccessible
方法。--《Java核心技术 卷1 基础知识 原书第10版》P199
2.利用 setAccessible 访问私有对象的值
name
属性为 Employee
类的超类 People
类的成员,getDeclaredFields()
返回值不包括超类成员
getField()
仅能获取类(及其父类可以自己测试) public属性成员
/**
* 利用反射获取 private 域的值,不包含超类域
* 输出:500.0
* 2000-11-11
*/
public static void accessPrivateObjects(){
Employee employee = new Employee("hrm",500,2000,11,11);
for(Field f : employee.getClass().getDeclaredFields()){
f.setAccessible(true);
try{
System.out.println(f.get(employee));
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
}
3.setAccessible方法分析
setAccessible
为AccessibleObject
类中的一个方法,是它是 Field、 Method 和 Constructor 类的公共超类。这个特性是为调试、 持久存储和相似机制提供的。
因而想访问 Field、 Method 和 Constructor 的私有对象,均需 setAccessible
。
- public void setAccessible(boolean flag)
- public static void setAccessible(AccessibleObject[] array, boolean flag)
4.可供任意类使用的通用 toString方法
其中使用getDeclaredFileds
获得所有的数据域, 然后使用 setAccessible
将所有的域设置为可访问的。 对于每个域,获得了名字和值。递归调用 toString
方法, 将每个值转换成字符串。
public class ObjectAnalyzer {
private ArrayList<Object> visited = new ArrayList<>();
/**
* 将对象转换为列出所有字段的字符串表示形式。
* @param object
* @return 包含对象类名以及所有字段名和值的字符串
*/
public String toString(Object object){
if(object == null) return "null";
if(visited.contains(object)) return "...";
visited.add(object);
Class cl = object.getClass();//返回一个Class类型的实例
//如果是 String 类直接返回对象
if(cl == String.class) return (String) object;
//如果该实例是数组
if(cl.isArray()){
//返回表示数组的组件类型的类 。 如果此类不表示数组类,则此方法返回null。
String r = cl.getComponentType() + "[]{";
for(int i = 0; i < Array.getLength(object); i++){
if(i > 0) r+= ",";
//返回指定数组对象中索引组件的值,即返回 object 数组对象索引 i 的值
Object val = Array.get(object,i);
//isPrimitive() 确定指定的类对象是否表示基本类型
if(cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
do{
r += "[";
Field[] fields = cl.getDeclaredFields();
//设置可访问 private 对象的值
AccessibleObject.setAccessible(fields,true);
for(Field f : fields){
if(!Modifier.isStatic(f.getModifiers())){
if(!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try{
//返回域所属类型的 Class 对象
Class t = f.getType();
Object val = f.get(object);
if(t.isPrimitive()) r += val;
else r += toString(val);
}catch (Exception e){
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
}while (cl != null);
return r;
}
}
测试:
public void objectAnalyzerTest(){
ArrayList<Integer> squares = new ArrayList<>();
for(int i = 1; i <= 5; i++){
squares.add(i * i);
}
System.out.println(new ObjectAnalyzer().toString(squares));
}
输出结果为:
java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]