zoukankan      html  css  js  c++  java
  • 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][][]
    
  • 相关阅读:
    JavaScript replace() 方法
    vs2010注册码 激活方法
    Javascript 返回上一页
    错误:该行已经属于另一个表
    Oracle10g中如何分析响应时间
    Oracle Purge和drop的区别
    Oracle 的 Sql*Plus 常用命令介绍【转】
    如何实现oracle不同版本间数据的导入导出
    如何绑定变量使用
    Oracle PGA 介绍
  • 原文地址:https://www.cnblogs.com/huaranmeng/p/12790752.html
Copyright © 2011-2022 走看看