簡單易懂Java反射的setAccessible()方法
前言:在使用Field類的對象訪問我自定義的Employee類對象的name域時,拋出異常illegalAccessException。查詢原因為:在訪問name域時,Java進行了訪問檢查,發(fā)現(xiàn)該域是private修飾的,不能直接訪問,因此拋出異常。
概要
本文首先詳細介紹訪問檢查的概念,然后介紹關于反射的運行時訪問檢查,并說明可能存在的問題。最后介紹可以通過setAccessible方法屏蔽或者說禁用運行時訪問檢查。讓我們開始把~~
一、 什么是Java的訪問檢查
訪問檢查,就是查看成員屬性、成員方法的使用是否符合訪問權限(public、protected、default、private)。
有點太理論化了,簡單來說,如果一個類的成員(屬性或者方法)的訪問權限是private,那么該成員只能在當前類中使用;如果一個類的成員的訪問權限是public,那么該成員可以在任意類中使用;如果一個類的成員的訪問權限是default,那么該成員只能在同一個包下面的類中使用;如果一個類的成員的訪問權限是protected,那么該成員可以在同一個包下面的類中和其他包下面的該類的子類中使用。
如果,類的成員的訪問權限是default,你卻在另一個包中使用了該成員,當編譯時,編譯器會進行訪問檢查,發(fā)現(xiàn)成員的使用與給定的訪問權限不一致,因此會報錯。
舉個例子,在com.example包下創(chuàng)建People類,有四個成員變量。在com.example.app包下(它是不同于com.example的包)下,使用People類的四個成員變量。
package com.example; package com.example; public class People { private int privateVar = 1; int defaultVar = 2; protected int protectedVar = 3; public int publicVar = 4; } package com.example.app; import com.example.People; public class TestMain { public static void main(String[] args) { People p = new People(); System.out.println(p.privateVar); System.out.println(p.defaultVar); System.out.println(p.protectedVar); System.out.println(p.publicVar); } }
編譯后提示,publicVar的使用符合public的訪問權限,所以沒有出錯。
相信大家都理解了訪問檢查是什么,那么,反射對象的訪問檢查是怎么的呢?
一個類的成員屬性、成員方法、構造函數(shù),在反射中分別被抽象為Field、Method、Counstructor類。
我們可以使用Field訪問對象的成員屬性,成員屬性的訪問權限,編譯器是不知道的,只有運行時才知道。因此對于反射對象(例如Field)訪問權限的檢查只能交給虛擬機。
如果,虛擬機在運行時,發(fā)現(xiàn)成員的使用與給定的訪問權限不一致,如下代碼
package com.example.app; import com.example.People; import java.lang.reflect.Field; public class TestMain { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { People p = new People(); Class cl = p.getClass(); // 利用反射訪問private修飾的成員變量 Field f = cl.getDeclaredField("privateVar") System.out.println(f.get(p)); } }
運行時,拋出異常:java.lang.IllegalAccessException
綜上,訪問檢查可以時編譯器在編譯時進行或者虛擬機在運行時進行(主要是針對反射)
二、 setAccessible() 方法介紹
setAccessible(boolean flag)
方法是AccessibleObject類中的一個方法,它是Field、 Method、Constructor的公共父類。當Field、Method或Constructor (三者都是反射對象)分別用于設置字段(set(Object obj, Object value)
)或獲取字段(get(Object obj)
)、調用方法(invoke(Object obj, Object... args)
)或創(chuàng)建和初始化類的新實例(newInstance(Object... initargs)
)時,將執(zhí)行運行時訪問檢查。
引用自《Java核心技術 第十版》
注意:方法名setAccessible很容易讓人產生誤解,給人的感覺是設置了成員的可訪問性,例如,覺得public修飾的成員是任意類都可以訪問的,所以可訪問標志是true;覺得private修飾的成員只有本類可以訪問,所以可訪問標志是false。其實不然,不管是什么訪問權限,其可訪問標志的值都為false。
測試代碼如下:
package com.example.app; import com.example.People; import java.lang.reflect.Field; public class TestMain { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { People p = new People(); Class cl = p.getClass(); // 打印輸出所有成員變量的名字及可訪問標志 for (Field f : cl.getDeclaredFields()) { System.out.println(f.getName() + ": " + f.isAccessible()); } } }
輸入結果:
上面中的API說得很清楚,這個可訪問標志表示是否屏蔽Java語言的訪問檢查,默認值是false,(上面已經測試)
可以通過setAccessible(true)
修改默認值,如此會屏蔽Java語言的(運行時)訪問檢查,使得對象的私有成員可以訪問,而不報錯。
package com.example.app; import com.example.People; import java.lang.reflect.Field; public class TestMain { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { People p = new People(); Class cl = p.getClass(); for (Field f : cl.getDeclaredFields()) { //屏蔽對象的訪問檢查 f.setAccessible(true); // 訪問不符合訪問權限的成員屬性 System.out.println(f.getName() + " = " + f.get(p)); } } }
輸入結果:
到此這篇關于簡單易懂Java反射的setAccessible()方法的文章就介紹到這了,更多相關Java反射setAccessible()內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java多線程CountDownLatch的實現(xiàn)
本文主要介紹了Java多線程CountDownLatch的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter)
本文主要介紹了Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter),就像文章標題所說的,SpringSecurity已經廢棄了繼承WebSecurityConfigurerAdapter的配置方式,下面就來詳細的介紹一下,感興趣的可以了解一下2023-12-12Java 垃圾回收機制詳解(動力節(jié)點Java學院整理)
在系統(tǒng)運行過程中,會產生一些無用的對象,這些對象占據(jù)著一定的內存,如果不對這些對象清理回收無用對象的內存,可能會導致內存的耗盡,所以垃圾回收機制回收的是內存。下面通過本文給大家詳細介紹java垃圾回收機制,一起學習吧2017-02-02SpringCloud及Nacos服務注冊IP選擇問題解決方法
這篇文章主要介紹了SpringCloud及Nacos服務注冊IP選擇問題,為什么注冊的IP和真實IP不符合呢,原因是Nacos客戶端在注冊服務時會從機器網卡中選擇一個IP來注冊,所以,當注冊了的是非真實IP后,另一臺機器調用時是不可能調通的,知道問題原因就是解決方法,一起看看吧2024-01-01