首先补充2个专业单词:生命周期 lifetime 作用域 scope
关于变量生命周期问题,稍不留神就容易犯错。下面举几道练习题来巩固这个知识点,意在让自己做注意这个小问题。
1. 程序执行的结果是:
1 public class Test{ 2 int x = 12; 3 public void method(int x){ 4 x +=x; 5 System.out.println(x); 6 } 7 public static void main(String[] args){ 8 Test t = new Test(); 9 t.method(5); 10 } 11 }
A. 5 B. 10 C. 12 D. 17
分析:t.method(5)调用method(int x)方法,传入参数是x = 5。int x = 12这个x成员变量的生命周期就是整个类的生命周期。不幸的是Java规定,当局部变量与成员变量重名时,在方法内的局部变量具有更高的优先级,此时外部变量不在方法内起作用(C/C++也是这么规定的)。因此方法中运算是这样的:
1 public void method(int x){ // 传入 x = 5 2 x +=x; // x = x + x = 5 + 5 = 10 3 System.out.println(x); // 输出 10 4 }
当method(int x)方法调用结束,这个方法的栈帧就从Java栈中退出了,此时局部变量x的生命周期也就到头了。实际上成员变量x并没有起到任何作用,只是声明了一下而已。
2. 下列程序编译或运行的结果是:
1 public static void main(String[] args) { 2 for(int i=0;i<=100;i++) { 3 int num = 100; 4 } // 用完直接销毁 5 for(int j=0;j<=100;j++) { 6 int num = 200; 7 } // 用完直接销毁 8 int num = 300; // 重新声明变量 9 System.out.println(num); 10 }
A.输出:100 B.输出:200 C.输出:300 D.编译出错
分析:变量的作用域就是"{}"内,当"{}"执行结束,变量的生命周期也就结束了。本题中,每次for循环都声明int num,当for循环结束后相应的num局部变量被销毁。最后又声明int num = 300,它的生命周期就是此时开始到main()方法结束。下面简单分析一下这段代码在内存中做了啥事。
首先public static void main(String[] args) 方法被加载到Java栈中(一个方法对于一个栈帧),这个main栈帧包括了局部变量(其余的操作数等不讨论)num。第一个for循环时num = 100和i被压入栈,for循环结束i和num退栈,生命周期结束,被销毁。第二个for循环同第一个效果。到第三个num时必须声明类型,否则报错,因为之前声明的num已经被销毁。int num = 300的生命周期到main方法结束为止,因此System.out.println(num)输出的是300。输出完毕后栈底的main栈帧退栈,此时num生命周期结束。
3. 下列程序编译或运行的结果是:
1 public static void main(String[] args) { 2 int num = 100; 3 for(int i=0;i<=100;i++) { 4 int num = 10; 5 } 6 System.out.println(num); 7 }
A.输出:10 B.输出:100 C.输出:201 D.编译出错
分析:这道题乍一看,貌似和上面那题考查知识点相同。其实不然,比较题1,发现橙色的那部分是多出来的。Java规定,相同变量名只能声明一次,即使声明的类型不同也只能声明一次,否则编译错误(不符合语法规范)。int num = 100生命周期直到main方法结束,而在for循环内又声明了一次num变量,因此出错。其实这两个num放的位置是不同的,for循环内的num不会覆盖原来的num,由于作用域不同,其实是同名的两个变量。用一个比喻来说明这点,比如一个学校有两个叫"赵四"的同学,一个是1年级1班的,一个是1年级2班的,这两个其实是两个个体,存在的班级不同,只不过是名字相同而已。
总结:
1)在Java中,不严格区分变量的声明与定义。也就是说Java语言,变量的声明就是定义。这与C/C++存在区别,C语言在运行程序时对声明是不会给变量分配内存空间,只有定义变量时才会给变量分配内存空间。
2)Java变量只能定义一次,所在的块为其作用域,也就是说变量的生命周期和块同步。