用Java調用私有方法
- java
1.概述
雖然在Java中將方法private
,以防止從擁有類的外部調用它們,但出於某些原因,我們可能仍需要調用它們。
為此,我們需要解決Java的訪問控制問題。這可以幫助我們到達庫的某個角落,或者允許我們測試一些通常應保密的代碼。
在這個簡短的教程中,我們將研究如何驗證方法的功能,而不考慮其可見性。我們將考慮兩種不同的方法:Java Reflection API和Spring的ReflectionTestUtils
。
2.可見性超出我們的控制
對於我們的示例,讓我們使用對long
數組進行LongArrayUtil
我們的類有兩個indexOf
方法:
public static int indexOf(long[] array, long target) {
return indexOf(array, target, 0, array.length);
}
private static int indexOf(long[] array, long target, int start, int end) {
for (int i = start; i < end; i++) {
if (array[i] == target) {
return i;
}
}
return -1;
}
假設這些方法的可見性無法更改,但是我們要調用私有indexOf
方法。
3. Java反射API
3.1。用反思找到方法
雖然編譯器阻止我們調用類中不可見的函數,但我們可以通過反射來調用函數。首先,我們需要訪問描述我們要調用的函數Method
Method indexOfMethod = LongArrayUtil.class.getDeclaredMethod(
"indexOf", long[].class, long.class, int.class, int.class);
我們必須使用getDeclaredMethod
才能訪問非私有方法。我們在具有函數的類型(在本例中為LongArrayUtil
上調用它,並傳入參數的類型以標識正確的方法。
如果該方法不存在,則該函數可能會失敗並引發異常。
3.2。允許訪問方法
現在,我們需要暫時提高方法的可見性:
indexOfMethod.setAccessible(true);
此更改將持續到JVM停止或accessible
屬性設置回false.
3.3。反射調用方法
最後,我們Method
對像上invoke
:
int value = (int) indexOfMethod.invoke(
LongArrayUtil.class, someLongArray, 2L, 0, someLongArray.length);
現在,我們已經成功訪問了私有方法。
invoke
的第一個參數是目標對象,其餘參數需要匹配我們方法的簽名。在這種情況下,我們的方法是static
,目標對LongArrayUtil
父類– LongArrayUtil。對於調用實例方法,我們將傳遞要調用其方法的對象。
我們還應注意, invoke
返回Object
,對於void
函數,該參數為null
,並且需要轉換為正確的類型才能使用它。
4. Spring ReflectionTestUtils
到達類的內部是測試中的常見問題。 Spring的測試庫提供了一些捷徑,以幫助單元測試達到類。這通常可以解決特定於單元測試的問題,在單元測試中,測試需要訪問一個私有字段,Spring可能會在運行時實例化該私有字段。
首先,我們需要在pom.xml中spring-test
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.4</version>
<scope>test</scope>
</dependency>
現在,我們可以在ReflectionTestUtils
invokeMethod
函數,該函數使用與上述相同的算法,從而節省了編寫大量代碼的時間:
int value = ReflectionTestUtils.invokeMethod(
LongArrayUtil.class, "indexOf", someLongArray, 1L, 1, someLongArray.length);
由於這是一個測試庫,因此我們不希望在測試代碼之外使用它。
5.注意事項
使用反射繞過功能可見性會帶來一些風險,甚至可能無法實現。我們應該考慮:
- Java安全管理器是否將在我們的運行時中允許
- 在沒有編譯時檢查的情況下,我們正在調用的函數在將來是否會繼續存在
- 重構我們自己的代碼,使事物更清晰可見
六,結論
在本文中,我們研究瞭如何使用Java Reflection API和Spring的ReflectionTestUtils
訪問私有方法。