在Java基礎中,private是一個訪問權限最嚴格的修飾符。但是在我們工作當中,使用第三方jar包的時候甚至使用JDK里面的工具類的時候,經常會遇到一些private修飾變量,我們想訪問甚至修改這個變量的時候就顯得比較麻煩。
這個時候我們需要通過Java反射方案來實現我們訪問和修改private修飾的變量。
核心API
在java.lang.reflect.Field
類中有一個java.lang.reflect.AccessibleObject#setAccessible(boolean)
方法可以設置反射訪問變量的時候跳過權限檢查。
這個API不僅可以訪問對象變量,也可以訪問靜態變量。
封裝類
這個是Groovy寫的,對JDK的反射相關API進行了封裝,其中有些異常並沒有處理。
package com.funtester.utils
import com.funtester.base.exception.FailException
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
/**
* 私有變量訪問工具類,可用於final修飾的變量
*/
class PriUtil {
/***
* 獲取私有成員變量的值
*
*/
static <F> F get(Object instance, String name, Class<F> f) {
try {
Field field = instance.getClass().getDeclaredField(name);
field.setAccessible(true); // 參數值為true,禁止訪問控制檢查
return (F) field.get(instance);
} catch (NoSuchFieldException | IllegalAccessException e) {
FailException.fail("獲取${instance.toString()}私有變量$name 失敗 ${e.getMessage()}");
}
}
/***
* 設置私有成員變量的值
*
*/
static void set(Object instance, String fileName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field field = instance.getClass().getDeclaredField(fileName);
field.setAccessible(true);
field.set(instance, value);
}
/***
* 訪問私有方法
*
*/
static <F> F call(Object instance, String name, Class<F> r, Class[] parameterTypes, Object[] params) {
Method method = instance.getClass().getDeclaredMethod(name, parameterTypes);
method.setAccessible(true);
return (F) method.invoke(instance, params);
}
/**
* 獲取static變量
* @param c
* @param name
* @param f
* @return
*/
static <F> F get(Class c, String name, Class<F> f) {
Field[] fields = c.getDeclaredFields();
try {
for (Field field : fields) {
field.setAccessible(true);
if (field.getType() == f && field.getName().equals(name))
return (F) field.get(c);
}
} catch (Exception e) {
FailException.fail("獲取${c.name}私有變量$name 失敗 ${e.getMessage()}");
}
}
/**
* 設置static變量
* @param c
* @param name
* @param f
*/
static void set(Class c, String name, Object f) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals(name))
field.set(c, f)
}
}
/**
* 調用私有static方法
* @param c
* @param name
* @param r
* @param parameterTypes
* @param params
* @return
*/
static <F> Object call(Class c, String name, Class<F> r, Class[] parameterTypes, Object[] params) {
try {
Method method = c.getMethod(name, parameterTypes);
method.setAccessible(true);
return (F) method.invoke(null, params);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
FailException.fail("執行${c.name}私有方法$name 失敗,參數 ${e.getMessage()}")
}
}
}
測試類
這里我簡單寫了一個測試類,一個成員變量,一個類變量。
/**
* 反射訪問private測試類
*/
public class PriBase {
private String name = "FunTester";
private static String cname = "CFunTester";
}
測試腳本
首先我測試一下非靜態變量,測試腳本如下:
import com.funtester.frame.SourceCode
import com.funtester.utils.PriUtil
class PriTest extends SourceCode{
public static void main(String[] args) {
PriBase base = new PriBase()
PriUtil.set(base,"name","修改后name")
String get = PriUtil.get(base, "name", String.class)
output(get)
PriBase base1 = new PriBase()
String get1 = PriUtil.get(base1, "name", String.class)
output(get1)
}
}
控制台輸出:
INFO-> main 當前用戶:oker,工作目錄:/Users/oker/IdeaProjects/funtester/,系統編碼格式:UTF-8,系統Mac OS X版本:10.16
INFO-> main
###### # # # # ####### ###### ##### ####### ###### #####
# # # ## # # # # # # # #
#### # # # # # # #### ##### # #### #####
# # # # # # # # # # # # #
# ##### # # # ###### ##### # ###### # #
INFO-> main 修改后name
INFO-> main FunTester
Process finished with exit code 0
其次我們測試一下靜態變量,測試腳本如下:
import com.funtester.frame.SourceCode
import com.funtester.utils.PriUtil
class PriTest extends SourceCode{
public static void main(String[] args) {
PriUtil.set(PriBase.class,"cname","修改后name")
String get = PriUtil.get(PriBase.class, "cname", String.class)
output(get)
}
}
控制台輸出:
INFO-> main 當前用戶:oker,工作目錄:/Users/oker/IdeaProjects/funtester/,系統編碼格式:UTF-8,系統Mac OS X版本:10.16
INFO-> main
###### # # # # ####### ###### ##### ####### ###### #####
# # # ## # # # # # # # #
#### # # # # # # #### ##### # #### #####
# # # # # # # # # # # # #
# ##### # # # ###### ##### # ###### # #
INFO-> main 修改后name
Process finished with exit code 0
完美實現我們的需求,以后再也不用管什么訪問權限了,哈哈哈~~~