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