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][][]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM