作用域规则与变量覆盖面试题
Java Magazine上面有一个专门坑人的面试题系列: https://blogs.oracle.com/javamagazine/quiz-2。
这些问题的设计宗旨,主要是测试面试者对Java语言的了解程度,而不是为了用弯弯绕绕的手段把面试者搞蒙。
如果你看过往期的问题,就会发现每一个都不简单。
这些试题模拟了认证考试中的一些难题。 而 “中级(intermediate)” 和 “高级(advanced)” 指的是试题难度,而不是说这些知识本身很深。 一般来说,“高级”问题会稍微难一点。
下面哪些代码是正确的写法?
class C1 {
void foo(int a) {
for (int a = 0; a < 5; a++) { }
}
}
class C2 {
int a = 0;
{ int a = 1; }
}
class C3 {
{ int a = 0; }
{ int a = 1; }
}
class C4 {
{
int a = 0;
for (int a = 0; a < 5; a++) { }
}
}
class C5 {
{
for (int a = 0; a < 5; a++) { }
int a = 0;
}
}
到底是哪些选项正确呢? 请先思考,再看下面的解答。
这道题主要考察变量的作用域以及优先级:同一作用域下如果存在两个相同的变量名称(标识符),那么会简单会指向一个变量而忽略另一个。
一般来说,局部变量会覆盖同名的类属性和实例属性,但方法作用域内的局部变量则不允许覆盖。
在编写程序代码时,一般规范都会要求明确指定类名或者 this
来引用对应的字段。
下面是一个简单的使用示例:
public class MyClass {
static int x = 99;
int y = 100;
public static void showX() {
int x = 9;
System.out.println("x is " + x); // x is 9
System.out.println("MyClass.x is " + MyClass.x); // MyClass.x is 99
}
public void showY() {
int y = 10;
System.out.println("y is " + y); // y is 10
System.out.println("this.y is " + this.y); // this.y is 100
}
}
接下来依次解读试题中给出的选项。
先看选项 A
,
class C1 {
void foo(int a) {
for (int a = 0; a < 5; a++) { }
}
}
可以看到有一个变量 a
作为方法参数。
方法参数也是局部变量,其作用域范围从参数列表开始,一直到方法结束。
在方法内部的for
循环中,也声明了一个名为 a
的局部变量。
因此,根据规则,在某个作用域范围内,不允许存在多个同名的局部变量
, 所以这段代码无法编译, 选项A不正确
。
选项 B
,
class C2 {
int a = 0;
{ int a = 1; }
}
类中定义了实例变量 a
,在初始化语句块中又定义了一个局部变量a
,类似于方法代码中的局部变量声明。
所以在初始化块的作用域范围内, 局部变量覆盖了实例属性。
这段代码可以正确编译并运行, 所以 选项B正确
。
选项 C
的内容如下,
class C3 {
{ int a = 0; }
{ int a = 1; }
}
在两个单独的初始块中, 都声明了局部变量a
, 但各自的作用域范围都被限制为代码块之中, 所以不会发生重叠,也就没有变量冲突。
当然,这段代码没什么实际的作用,因为在初始化块中声明的变量,在其他地方都不可看,在语句块执行完成后会被丢弃。
可能有些同学会觉得编译器会报错,但实际上这些代码块都是会执行的,并没有不可达代码。
既然语法没问题, 那么 选项C正确
。
看选项 D
,
class C4 {
{
int a = 0;
for (int a = 0; a < 5; a++) { }
}
}
代码块中先是声明了一个局部变量, 然后for
循环中又声明了同名的变量。
因为for循环中声明的局部变量,作用域范围从声明处开始,直到循环结束。
也就是说, 在循环体范围内,存在两个同名的局部变量,这是违反语法规定的。
跟选项 A
中的情况有点类似,区别只在于选项 A
中声明的是方法参数,而选项D中声明的是普通局部变量。
由此可知, 选项D不正确
。
最后看选项 E
,
class C5 {
{
for (int a = 0; a < 5; a++) { }
int a = 0;
}
}
和选项D
看起来有点像, 但这次是循环先声明自己的局部变量,循环结束后,后面的代码接着声明了一个同名的普通局部变量。
这两个局部变量的作用域并不重叠,也不冲突,所以代码有效,选项E正确
。
在 《Java语言规范 - 6.3 变量声明与作用域》 一节中对变量声明的作用域范围做了详细说明。
有两个需要着重强调的点:
for
循环初始化语句中声明的局部变量, 作用域范围包括: 初始化部分、初始化后面的条件判断部分,递增更新语句,以及循环体中的语句。通过我们的分析可知,正确选项为: B
, C
, E
.
原文链接: https://blogs.oracle.com/javamagazine/quiz-yourself-variable-declaration-intermediate