zoukankan      html  css  js  c++  java
  • 突破private

    前言
    在日常测试中,我们往往都有如果能获取被测对象的某个属性就方便多了的感慨,不幸的是大多数时候该属性都是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)

    用法举例: 
    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"));

    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)

    用法举例: 
    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字节。如下图所示。 

    就像上面讨论的一样,这种方法虽然能突破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中(非虚函数可省),去除访问限制符号。访问目标对象时将其转换为同构类型即可
    示例代码如下: 
     
    使用这种简单的方法,甚至可以访问目标对象的私有成员方法! 

    局限性:由于子类不能访问父类的私有成员变量和函数,所以这种构造同构类的方法也不能访问父类的私有成员变量和函数。

  • 相关阅读:
    gulp备忘
    好文收藏
    妙味H5交互篇备忘
    [CSS3备忘] transform animation 等
    css选择器总结
    flexbox备忘
    函数
    继承2
    在 Swift 中实现单例方法
    浅谈 Swift 中的 Optionals
  • 原文地址:https://www.cnblogs.com/baochun968/p/2163408.html
Copyright © 2011-2022 走看看