field.setAccessible(true)

Accessable屬性是繼承自AccessibleObject 類. 功能是啓用或禁用安全檢查
在反射對象中設置 accessible 標誌允許具有足夠特權的複雜應用程序(比如 Java Object Serialization 或其他持久性機制)以某種通常禁止使用的方式來操作對象。
setAccessible
public void setAccessible(boolean flag)
throws SecurityException

將此對象的 accessible 標誌設置爲指示的布爾值。值爲 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。值爲 false 則指示反射的對象應該實施 Java 語言訪問檢查。

setAccessible方法是幹什麼用的呢讓我們來看一段代碼吧

public class User {
	private String name;
	
	public User(String name) {
		this.name = name;
	}


	public String getName() {
		return name;
	}


	public void setName(String name) {
		this.name = name;
	}
}

測試:

import java.lang.reflect.Field;

public class Demo {
public static void main(String[] args) throws Exception {  
   Class clazz = User.class;
       User u = new User("小明");
       for (Field f : clazz.getDeclaredFields()) {  
           System.out.println(f.isAccessible());//這裏的結果是false
           f.setAccessible(true);
           System.out.println(f.getName()+":"+f.get(u));
       }  
 
   }  
}

通過運行以上代碼,我們發現 System.out.println(f.isAccessible())這一句打印結果是"false",從字面上理解是說該字段不能被訪問,但是爲了保險起見我們還是去看一下源代碼(Java有的時候就是比較坑,不一定能夠見名知意,我們還是保險點比較好,雖然不算是做學問,但是學習的態度還是一絲不苟比較好),首先我們去掉f.setAccessible(true);

然後在 System.out.println(f.getName()+":"+f.get(u));這行打斷點,但是發現不了問題,經過檢查發現getName()這個方法並不拋異常,也就是是就算沒有加f.setAccessible(true);

也可以獲得name,拋異常的是f.get(u);這句話,我找到Field中的get(Object obj)方法,發現有這麼一句

if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
checkAccess(Reflection.getCallerClass(), clazz, obj, modifiers);
}
}

這裏的override就是我們用setAccessible設置的值,但是接下來就坑爹了Reflection以及其他幾個類的源碼不在jdk自帶的源碼中,找了半天才找到sun的源代碼,導入源代碼(最好將sun的源代碼和java的放在一個文件夾裏,不然一次只能導入一個源代碼,要來回切換,比較麻煩)

然後我們debug定位到了Reflection類的ensureMemberAccess方法如圖:
在這裏插入圖片描述

圖中有一個verifyMemberAccess方法,這就是拋出異常的方法,進入該方法直接到了 boolean successSoFar = false;然後就return false了,因此拋出了IllegalAccessException異常,也就是說我們得出結論當isAccessible()的結果是false時不允許通過反射訪問該字段

結論:當isAccessible()的結果是false時不允許通過反射訪問該字段

實際上setAccessible是啓用和禁用訪問安全檢查的開關,並不是爲true就能訪問爲false就不能訪問,一般情況下,我們並不能對類的私有字段進行操作,利用反射也不例外,但有的時候,例如要序列化的時候,我們又必須有能力去處理這些字段,這時候,我們就需要調用AccessibleObject上的setAccessible()方法來允許這種訪問,而由於反射類中的Field,Method和Constructor繼承自AccessibleObject,因此,通過在這些類上調用setAccessible()方法,我們可以實現對這些字段的操作。但有的時候這將會成爲一個安全隱患,爲此,我們可以啓用java.security.manager來判斷程序是否具有調用setAccessible()的權限。默認情況下,內核API和擴展目錄的代碼具有該權限,而類路徑或通過URLClassLoader加載的應用程序不擁有此權限。例如:當我們以這種方式來執行上述程序時將會拋出異常

java.lang.IllegalAccessException: Class com.example.xj.myapplication.TestField can not access a member of class com.example.xj.myapplication.Student with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Field.get(Field.java:390)
	at com.example.xj.myapplication.TestField.main(TestField.java:20)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

所以 f.setAccessible(true);得作用就是讓我們在用反射時訪問私有變量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章