盡管普遍認知是,實際可以通過反射通路其他類的私有屬性和方法。它甚至并不難。這個特性在單元測試中特别有用。本文将告訴你如何做。
注意:這隻能在單體Java應用程式中運作,比如單元測試和正常程式。如果你嘗試在Java Applet程式中使用,可能需要注意SecurityManager問題。但是,你并不常做這些操作,并且到目前為止已偏離了本文主題。
注意:已經有很多關于Java9禁用反射通路類私有屬性的讨論。根據我的經曆,在Java9依舊是可以的(通路類私有屬性),但這可能會在Java未來版本中改變。
通路私有屬性
通路私有屬性需要調用Class.getDeclaredField(String name)或者Class.getDeclaredFields()方法。方法Class.getField(String name)和Class.getFields()隻傳回類的public方法,是以不能使用它們。下面的示例代碼是一個有私有屬性的類,再下一個是通過反射通路私有屬性。
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Field privateStringField = PrivateObject.class.
getDeclaredField("privateString");
privateStringField.setAccessible(true);
String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue);
示例代碼會輸出“fieldValue = The Private Value”,它是PrivateObject執行個體的privateString屬性的值。
使用方法privateObject.class.getDeclaredField("privateString")時需要注意。這個方法傳回私有屬性。這個方法隻傳回特定類定義的屬性,不包括任何超類的屬性。
另外需要注意代碼privateStringField.setAccessible(true);。調用這個方法将為特定的Field執行個體關閉通路權限檢查,僅用于反射。現在你可以通路屬性,不管它是private, protected 或者package scope,即使調用方不在通路scope内。你依舊不能使用普通代碼通路私有屬性,Java編譯通不過。
通路私有方法
通路類私有方法需要調用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods()。方法Class.getMethod(String name, Class[] parameterTypes)和Class.getMethods()隻傳回類的public方法,是以不能使用。下面是一個有私有方法的類和如何通過反射調用該類方法的私有代碼:
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
private String getPrivateString(){
return this.privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Method privateStringMethod = PrivateObject.class.
getDeclaredMethod("getPrivateString", null);
privateStringMethod.setAccessible(true);
String returnValue = (String)
privateStringMethod.invoke(privateObject, null);
System.out.println("returnValue = " + returnValue);
示例代碼會輸出"returnValue = The Private Value",這是執行個體PrivateObject中的方法getPrivateString()的傳回值。
注意,代碼PrivateObject.class.getDeclaredMethod("privateString")。這個方法調用将傳回類的私有方法。這個方法隻傳回類直接定義的方法,而不傳回任何超類的方法。
另外需要注意代碼privateStringMethod.setAccessible(true);。調用Method.setAcessible(true)将關閉Method執行個體的通路權限檢查,僅用于反射。現在你可以調用方法,不論它是private, protected 或 package scope,即使調用者不在這些scopes内。你依舊不能通過普通方法通路類的私有方法,這會編譯不通過。