103

我有一个关于 Java 开关的疯狂问题。

int key = 2;

switch (key) {
    case 1:
        int value = 1;
        break;
    case 2:
        value = 2;
        System.out.println(value);
        break;
    default:
        break;
}

场景 1 - 当它key是 2 时,它成功地将值打印为 2。
场景 2 - 当我要评论它value = 2case 2:,尖叫声说The local variable value may not have been initialized

问题:

场景1:如果执行流程不去case 1:(当key = 2),那么它如何知道值变量的类型为int

场景 2:如果编译器知道 value 变量的类型为int,那么它必须访问int value = 1;过 .(声明和初始化)中的表达式case 1:。那为什么它会吱吱作响,当我要评论value = 2case 2:,说局部变量值可能尚未初始化

4

6 回答 6

118

基本上,switch 语句在作用域方面很奇怪。从JLS 的第 6.3 节

块中局部变量声明的范围(第 14.4 节)是声明出现的块的其余部分,从它自己的初始化程序开始,并在局部变量声明语句的右侧包括任何进一步的声明符。

在您的情况下,它与它case 2位于同一块case 1并出现在它之后,即使case 1永远不会执行......所以尽管您在逻辑上从未“执行”声明,但局部变量在范围内并且可以写入。(尽管初始化是,但声明并不是真正的“可执行”。)

如果您注释掉value = 2;分配,编译器仍然知道您指的是哪个变量,但是您不会经历任何为其分配值的执行路径,这就是为什么您会在尝试时遇到错误的原因读取任何其他未明确分配的局部变量。

我强烈建议您不要使用在其他情况下声明的局部变量——正如您所见,这会导致代码高度混乱。当我在 switch 语句中引入局部变量时(我很少尝试这样做 - 理想情况下,案例应该非常短)我通常更喜欢引入一个新的范围:

case 1: {
    int value = 1;
    ...
    break;
}
case 2: {
    int value = 2;
    ...
    break;
}

我相信这更清楚。

于 2012-05-30T06:12:04.680 回答
22

变量已声明(作为 int),但未初始化(分配了初始值)。想想这条线:

int value = 1;

作为:

int value;
value = 1;

int value部分在编译时告诉编译器您有一个名为 value 的变量,它是一个 int。该value = 1部分对其进行了初始化,但这发生在运行时,并且如果未输入开关的该分支,则根本不会发生。

于 2012-05-30T06:10:53.703 回答
18

来自 http://www.coderanch.com/t/447381/java-programmer-SCJP/certification/variable-initialization-within-case-block

声明在编译时处理,不依赖于代码的执行流程。由于value是在 switch 块的本地范围内声明的,因此它可以在该块中从其声明点开始的任何地方使用。

于 2012-05-30T06:10:13.153 回答
4

在 JDK-12 早期访问版本中集成了JEP 325: Switch Expressions (Preview) 。从乔恩的回答中可以看出一些变化 ——

  1. 局部变量作用域——switch case 中的局部变量现在可以是 case 本身的局部变量,而不是整个 switch 块。一个示例(类似于 Jon 在语法上也尝试过的)考虑Dayenum 类以进行进一步解释:

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    
    // some another method implementation
    Day day = Day.valueOf(scanner.next());
    switch (day) {
        case MONDAY,TUESDAY -> {
            var temp = "mon-tue";
            System.out.println(temp);
        }
        case WEDNESDAY,THURSDAY -> {
            var temp = Date.from(Instant.now()); // same variable name 'temp'
            System.out.println(temp);
        }
        default ->{
            var temp = 0.04; // different types as well (not mandatory ofcourse)
            System.out.println(temp);
        }
    }
    
  2. Switch Expressions - 如果打算给变量赋值然后使用它,一次可以使用 switch 表达式。例如

    private static void useSwitchExpression() {
        int key = 2;
        int value = switch (key) {
            case 1 ->  1;
            case 2 -> 2;
            default -> {break 0;}
        };
        System.out.println("value = " + value); // prints 'value = 2'
    }
    
于 2018-09-08T18:55:04.720 回答
0

这个解释可能会有所帮助。

    int id=1;

    switch(id){
        default: 
            boolean b= false; // all switch scope going down, because there is no scope tag

        case 1:
            b = false;
        case 2:{
            //String b= "test"; you can't declare scope here. because it's in the scope @top
            b=true; // b is still accessible
        }
        case 3:{
            boolean c= true; // case c scope only
            b=true; // case 3 scope is whole switch
        }
        case 4:{
            boolean c= false; // case 4 scope only
        }
    }
于 2018-10-20T00:42:02.107 回答
0

Java规范:

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.11

由于标签中断而突然完成的情况由标签语句的一般规则(第 14.7 节)处理。

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.7

标记语句:

LabeledStatement:标识符:语句

LabeledStatementNoShortIf:标识符:StatementNoShortIf

与 C 和 C++ 不同,Java 编程语言没有 goto 语句;标识符语句标签与出现在标记语句中的任何位置的 break (§14.15) 或 continue (§14.16) 语句一起使用。

带标签语句的标签范围是立即包含的语句。

换句话说,case 1、case 2 是 switch 语句中的标签。break 和 continue 语句可以应用于标签。

因为标签共享语句的范围,所以标签中定义的所有变量共享 switch 语句的范围。

于 2019-07-05T20:07:26.240 回答