001 自增运算
输出以下代码的结果:_____
public static void main(String[] args) {
int i = 1;
i = i++; // 1
int j = i++; // 2
int k = i + (++i) * (i++); // 3
System.out.println("i=" + i);
System.out.println("j=" + j);
System.out.println("k=" + k);
}
答案:i=4;j=1;k=11
解释:先明确两个Java虚拟机栈中的概念【操作数栈、局部变量表】
提到Java虚拟机栈又不得不说栈帧,它是Java虚拟机栈中用来存储数据和部分过程结果的数据结构,每一个栈帧都有自己的局部变量表(Local Variables)、操作数栈(OperandStack)
i++:是先把i压入操作数栈中,然后i自增
++i:i先自增,然后压入操作数栈
对于1处:
- ①先把
i存入操作数栈中,此时操作数栈中i的值为1 - ②然后把操作数栈中
i的值赋值给左边的i。
对于2处:
- ①先把
i的值压入操作数栈 - ②
i变量自增1 - ③把操作数栈中
i的值1赋值给j
因为之前已经自增两次了,但第一次被赋值了1,所以此时i的值为2
对于3处:
- 从
=运算符的右边将变量从左往右依次压栈,i先压栈,因为是++i,所以先自增i,然后把i(i==3)压入栈中,随后又把i(i==3)压入栈中,i自增; - 最后操作数弹出栈,先把栈顶操作数弹出栈,先把第一个
i(i==3)弹出,随后又把i(i==3)弹出,之后两数相乘,得到结果9,因为=右边的运算还没有结束,故9被压入栈中; - 然后继续将临时结果9与栈中最后的
i(i==2)弹出栈相加,把结果11赋值给k
3 (模拟栈)
3
2
总结:
=右边的操作数从左到右加载值,依次压入操作数栈- 自增、自减操作都是直接修改变量的值,不经过操作数栈
- 比如:
i=i+1,就要先将i压栈,然后将1压入栈中,最后取出两个运算数相加后,赋值给i。
- 比如:
- 在赋值之前,临时结果是被存储在操作数栈中的
002 判断输出结果
一、考点
- 类初始化过程
- 实例初始化过程
- 方法的重写
二、题目
public class Father{
private int i = test();
private static int j = method();
static{ System.out.print("(1)"); }
Father(){ System.out.print("(2)"); }
{ System.out.print("(3)"); }
public int test(){
System.out.print("(4)");
return 1;
}
public static int method(){
System.out.print("(5)");
return 1;
}
}
public class Son extends Father{
private int i = test();
private static int j = method();
static{ System.out.print("(6)"); }
Son() { // super(); System.out.print("(7)"); }
{ System.out.print("(8)"); }
public int test(){
System.out.print("(9)");
return 1;
}
public static int method(){
System.out.print("(10)");
return 1;
}
public static void main(String[] args) {
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
}
}
类初始化过程
① 一个类要创建实例需要先加载并初始化该类
- 先加载和初始化
main方法所在的类
② 先初始化父类,然后初始化子类
③ 一个类的初始化就是执行<clinit>()方法
<clinit>()方法由静态类变量显示赋值代码和静态代码块组成- 类变量显式赋值代码和静态代码块代码从上到下顺序执行
<clinit>()方法只执行一次
当执行new Son()时,首先加载并初始化该类的父类father,也会执行父类的<clinit>()方法,而该方法由静态类变量赋值代码与静态代码块组成,所以也就是执行
private static int j = method();,输出(5)static{ System.out.print("(1)"); },输出(1)
随后执行子类的<clinit>()方法
private static int j = method();,输出(10)static { System.out.print("(6)"); },输出(6)
顺序:(5)(1)(10)(6)
实例初始化
(1)先执行父类的<init>()方法
实例初始化实际上是执行<init>()方法,该方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成。构造方法最后被执行.
需要注意的是:每一个子类的<init>()方法执行之前先要执行父类的<init>()方法
① 当执行new Son()时,先执行父类的private int i = test();
② 然后顺序执行{ System.out.print("(3)"); }
③ 最后执行构造方法
Father() { System.out.print("(2)"); }
得到的顺序:(4)(3)(2)
问题一: 这里的输出顺序还不对,因为还有text()方法的重写问题
- 子类如果重写了父类的方法(
text()),通过子类对象调用的一定是子类重写过的代码,所以这里输出的是(9),而不是(4)
顺序:(9)(3)(2)
问题二: 那method()方法也是被重写了的啊,在类初始化之前为什么没有执行子类的method()两次?
- 因为在面向对象机制中就设定了静态方法不会被重写
(2)后执行子类的<init>()方法
① private int i = test();,输出(9)
② { System.out.print("(8)"); }
③ Son(){ System.out.print("(7)"); }
顺序:(9)(8)(7)
最后的答案:
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)