Java重写方法与初始化的隐患
作者:网络转载 发布时间:[ 2015/11/27 11:18:30 ] 推荐标签:编程语言
问题
简单还原一下问题, 我们有一个类SuperClass
public class SuperClass {
private int mSuperX;
public SuperClass() {
setX(99);
}
public void setX(int x) {
mSuperX = x;
}
}
现在我们想随时知道mSuperX是什么值, 不用反射, 因为父类从不直接修改mSuperX的值, 总是通过setX来改, 那么简单的方法是继承SuperClass, 重写setX方法, 监听它的改变好.下面是我们的子类SubClass:
public class SubClass extends SuperClass {
private int mSubX = 1;
public SubClass() {}
@Override
public void setX(int x) {
super.setX(x);
mSubX = x;
System.out.println("SubX is assigned " + x);
}
public void printX() {
System.out.println("SubX = " + mSubX);
}
}
我使用mSubX来跟踪mSuperX
因为在ViewGroup中, clipToPadding默认值是true(为了简化问题, 把它当成boolean, 实际并不是), 而ViewGroup初始化有可能不调用setClipToPadding, 此时是默认值, 为了模拟这种情况, 将mSubX初始化为1.
后在main里调用:
public class Main {
public static void main(String[] args) {
SubClass sc = new SubClass();
sc.printX();
}
}
很多人, 包括我, 认为终端输出的结果应该是:
SubX is assigned 99
SubX = 99
然而真正运行后输出的是:
SubX is assigned 99
SubX = 1
实际分析
要想知道发生了什么, 简单的方法是看看到底程序到底是怎么执行的, 比如单步调试, 或者直接一点, 看看Java字节码.
下面是Main的字节码
Compiled from "Main.java"
public class bugme.Main {
......
public static void main(java.lang.String[]);
Code:
0: new #2 // class bugme/SubClass
3: dup
4: invokespecial #3 // Method bugme/SubClass."<init>":()V
......
}
这是直接用javap反编译.class文件得到的. 虽说同样是Java写的, 用apktool反编译APK文件(其中的dex文件)得到的smali代码和Java Bytecode明显长得不一样.
字节码乍一看怪怪的, 只要知道它隐含了一个栈和局部变量表好懂了.
这段代码首先new一个SubClass实例, 把引用入栈, dup是把栈顶复制一份入栈, invokespecial #3将栈顶元素出栈并调用它的某个方法, 这个方法具体是什么要看常量池里第3个条目是什么, 但是javap生成的字节码直接给我们写在旁边了, 即SubClass.<init>.
接下来看SubClass.<init>,
public class bugme.SubClass extends bugme.SuperClass {
public bugme.SubClass();
Code:
0: aload_0
1: invokespecial #1 // Method bugme/SuperClass."<init>":()V
......
这里面并没有方法叫<init>, 是因为javap为了方便我们阅读, 直接把它改成类名bugme.SubClass, 顺便一提, bugme是包名. <init>方法并非通常意义上的构造方法, 这是Java帮我们合成的一个方法, 里面的指令会帮我们按顺序进行普通成员变量初始化, 也包括初始化块里的代码, 注意是按顺序执行, 这些都执行完了之后才轮到构造方法里代码生成的指令执行. 这里aload_0将局部变量表中下标为0的元素入栈, 其实是Java中的this, 结合invokespecial #1, 是在调用父类的构造函数, 也是我们常见的super().
所以我们再看SuperClass.<init>
public class bugme.SuperClass {
public bugme.SuperClass();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 99
7: invokevirtual #2 // Method setX:(I)V
10: return
......
}
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11