前言
在平常測試中,咱們每每都有若是能獲取被測對象的某個屬性就方便多了的感慨,不幸的是大多數時候該屬性都是private的,讓咱們望屬性而興嘆。
不修改源代碼而突破private成了不少qa的願望,本文正是拋磚引玉的解決了這個問題。
JAVA篇
JAVA語言中有個很是有名的特性:Reflection。這個機制容許程序在運行時透過Reflection APIs取得任何一個已知名稱的class的內部信息,固然包括類的私有成員變量和方法。
附件中的PrivateOperator.java對reflect進行了封裝,能夠方便地get、set私有成員方法 & 調用私有成員方法,甚至是父類的私有成員變量和方法!
get私有成員變量
這個需求可使用PrivateOperator.getObjectProperty()。
方法聲明:
/**
* @param owner: target object
* @param classLevel: 0 means itself, 1 means it's father, and so on...
* @param fieldName: name of the target field
* @return value of the target field
*/
public static Object getObjectProperty(Object owner, int classLevel, String fieldName)html
用法舉例:
Test t = new Test();
// get value of itself
System.out.println(PrivateOperator.getObjectProperty(t, 0, "privateString"));
// test get Parent private field
System.out.println(PrivateOperator.getObjectProperty(t, 1, "parentPrivate"));java
set私有成員變量
這個需求可使用PrivateOperator.setObjectProperty()。
方法聲明:
/**
* @param owner: target object
* @param classLevel: 0 means itself, 1 means it's father, and so on...
* @param fieldName: name of the target field
* @param value: new value of the target field
*/
public static void setObjectProperty(Object owner, int classLevel, String fieldName, Object value)數組
用法舉例:
Test t = new Test();
PrivateOperator.setObjectProperty(t, 0, "privateString", "I have been set");
t.printPrivateString();安全
調用私有成員方法
這個需求可使用PrivateOperator.invokeObjectFunction()。
方法聲明:
/**
* @param owner: target object
* @param classLevel: 0 means itself, 1 means it's father, and so on...
* @param methodName: name of the target method
* @param parameterTypes: types of the target method's parameters
* @param parameters: parameters of the target method
* @return result of invoked method
*/
public static Object invokeObjectMethod(Object owner, int classLevel, String methodName, Class[] parameterTypes, Object[] parameters)ide
用法舉例:
Test t = new Test();
Class[] parameterTypes = {String.class, int.class};
Object[] parameters = {"from PrivateOperator", 1};
Object ret = PrivateOperator.invokeObjectMethod(t, 0, "testParameter", parameterTypes, parameters);
System.out.println(ret);函數
C++篇
看過了前面的解決方案,讓人以爲突破private在JAVA簡直如探囊取物,不過因爲C++不支持反射,因此就沒那麼簡單了。
繫好安全帶,讓咱們開始這段對象內存結構之旅吧。
解決方案0:編譯選項
最容易想到的方法莫過於在目標類的首部加上#define private public,可是這種修改了源代碼的方法是不提倡的。
在編譯的時候加上-Dprivate=public,編譯器會把全部private的成員的訪問權限設爲public。
修改編譯選項這種方法不失爲一種簡便的突破private的方法,不過代價是要從新編譯目標類。
若是不修改目標類、甚至是編譯選項,那麼還有其餘方法能夠突破private訪問權限的限制嗎?答案是有的
解決方案1:指針偏移
某種程度上,類的一個對象能夠看做包含不一樣類型元素的數組,其數據成員的地址偏移由數據成員在類定義中的順序決定. 其中類對象的地址指向類中第一個被定義的數據成員的地址;第二個被定義的數據成員的地址取決於第一個數據成員的類型,若第一個爲 int 型,則再偏移 4 個字節( sizeof(int) )即獲得第二個數據成員的地址(有時也不必定是這樣,以下例中,因爲類型對齊的緣故,實際偏移 8 個字節( sizeof(double) )才獲得第二個數據成員的地址)。
測試
細心的同窗已經注意到示例代碼中有一個被註釋掉的虛函數聲明,這是由於當類中有虛函數時對象的首地址會被插入虛函數表指針,即首個元素的地址會偏移sizeof(ptr),這個偏移量和類的成員變量類型對齊,即若是成員中都是4字節的變量,那麼首地址偏移4字節;若是成員中有8字節的變量,同理首地址偏移8字節。以下圖所示。
spa
就像上面討論的同樣,這種方法雖然能突破private的限制,可是要考慮實際對象的內存結構,好比虛函數、字節對齊甚至是編譯器對對象結構產生的影響,實際使用甚是不方便。 C++標準要求,在同一個access section(也就是private、public、protected等區段)中,members的排列只需符合「較晚出現的members在class object中有較高的地址」這一條便可(請看C++ Standard 9.2節)。也就是說各個members並不必定得連續排列(具體實現看編譯器的喜愛,實驗顯示VC6 & g++4.4都是不連續的),members中間可能被編譯器因爲類型對齊的緣由補充一些bytes。
解決方案2:同構類
構造一個和目標類同構的類,這樣它們的內存結構就是一致的,惟一不一樣的是這個輔助的類的成員都是public的,這樣將目標對象轉換爲同構類型再讀取私有成員,就能夠躲開編譯器的類型檢查。
具體作法:將目標類代碼粘貼到struct中(非虛函數可省),去除訪問限制符號。訪問目標對象時將其轉換爲同構類型便可
示例代碼以下:
3d
使用這種簡單的方法,甚至能夠訪問目標對象的私有成員方法!
侷限性:因爲子類不能訪問父類的私有成員變量和函數,因此這種構造同構類的方法也不能訪問父類的私有成員變量和函數。指針