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方法分析

setAccessibleAccessibleObject 类中的一个方法,是它是 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][][]
posted @ 2020-04-27 22:37  花染梦  阅读(3110)  评论(0编辑  收藏  举报