Java坑人面试题系列: 变量声明(中级难度)

作用域规则与变量覆盖面试题

Java Magazine上面有一个专门坑人的面试题系列: https://blogs.oracle.com/javamagazine/quiz-2。

这些问题的设计宗旨,主要是测试面试者对Java语言的了解程度,而不是为了用弯弯绕绕的手段把面试者搞蒙。

如果你看过往期的问题,就会发现每一个都不简单。

这些试题模拟了认证考试中的一些难题。 而 “中级(intermediate)” 和 “高级(advanced)” 指的是试题难度,而不是说这些知识本身很深。 一般来说,“高级”问题会稍微难一点。

问题(中级难度)

下面哪些代码是正确的写法?

  • A.
class C1 {
    void foo(int a) {
       for (int a = 0; a < 5; a++) { }
    }
}
  • B.
class C2 {
    int a = 0;
    { int a = 1; }
}
  • C.
class C3 {
    { int a = 0; }
    { int a = 1; }
}
  • D.
class C4 {
    {
        int a = 0;
        for (int a = 0; a < 5; a++) { }
    }
}
  • E.
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 变量声明与作用域》 一节中对变量声明的作用域范围做了详细说明。

有两个需要着重强调的点:

  1. 方法参数的作用域范围: 形参的作用域范围,是方法,构造函数或lambda表达式对应的body。
  2. for循环初始化语句中声明的局部变量, 作用域范围包括: 初始化部分、初始化后面的条件判断部分,递增更新语句,以及循环体中的语句。

通过我们的分析可知,正确选项为: B, C, E.

相关链接

  • Java坑人面试题系列: 包装类(中级难度)
  • Java坑人面试题系列: 比对while与for循环(中级)
  • Java坑人面试题系列: 集合(高级)
  • Java坑人面试题系列: 线程/线程池(高级)
  • Java坑人面试题系列: 变量声明(中级)

原文链接: https://blogs.oracle.com/javamagazine/quiz-yourself-variable-declaration-intermediate

你可能感兴趣的:(面试笔试)