7

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

 3 years ago
source link: https://renfufei.blog.csdn.net/article/details/105962238
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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 变量声明与作用域》 一节中对变量声明的作用域范围做了详细说明。

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK