在 Java 开关中声明和初始化变量

Posted

技术标签:

【中文标题】在 Java 开关中声明和初始化变量【英文标题】:Declaring and initializing variables within Java switches 【发布时间】:2012-06-04 08:42:05 【问题描述】:

我有一个关于 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 - 当我要在 case 2: 中评论 value = 2 时,它会大声说 局部变量值可能尚未初始化

问题: 场景一:如果执行流程不去case 1:(当key = 2),那么它怎么知道值变量的类型为int? 场景 2:如果编译器知道 value 变量的类型为 int,那么它一定已经访问了 case 1: 中的 int value = 1; 表达式。(声明和初始化)。那为什么它会发出嘶嘶声当我要在case 2: 中评论value = 2 时,说局部变量值可能尚未初始化

【问题讨论】:

这不是一个疯狂的问题,这是一个非常好的问题。 Variable's scope in a switch case的可能重复 @PhilippeCarriere 实际上,我认为它应该是相反的 - 这里的答案更好(即使帖子更新),因为直接引用了 JLS,并且很好地总结了不同的问题在那篇文章中的答案。 See also. @Tunaki 重复的描述以“这个问题以前被问过”开头。我正在阅读,因为后一个应该被标记为前一个的副本。但我确实同意这个有很好的元素。也许他们应该以某种方式合并? 还有很多关于 SO 的问题都被标记为我原来的问题的重复,所以如果你决定最好把这个标记为新的原始问题,请修复所有链接以引用这个问题而不是我的。 【参考方案1】:

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

声明在编译时处理,不依赖于 代码的执行流程。由于value 在本地声明 switch 块的范围,它可以在该块中的任何位置使用 它的声明点。

【讨论】:

为什么这个答案被赞成?它没有回答问题,不像保罗或飞碟的回答...... 确实如此。所以,+1,一分钱,我这边也是。【参考方案2】:

变量已被声明(作为 int),但尚未初始化(分配了初始值)。想一想:

int value = 1;

作为:

int value;
value = 1;

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

【讨论】:

+1 很好地解释了编译时和运行时的声明和初始化。【参考方案3】:

基本上,Switch 语句的作用域很奇怪。来自section 6.3 of the JLS:

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

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

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

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

case 1: 
    int value = 1;
    ...
    break;

case 2: 
    int value = 2;
    ...
    break;

我相信这更清楚。

【讨论】:

+1 表示“虽然初始化是,但声明并不是真正的“可执行”。”。也感谢你的建议。 集成 JEP-325 后,picture of the scope of local variables has changed 和一个可以跨案例使用相同的名称,而不是切换块。尽管它也依赖于类似的块编码。此外,使用 switch 表达式为每个 switch case 分配给变量的值会非常方便。 用大括号添加新范围的要点。甚至不知道你能做到这一点。【参考方案4】:

在 JDK-12 早期访问版本中集成了 JEP 325: Switch Expressions (Preview)。从Jon's answer 可以看到某些变化 -

    局部变量范围 - 开关案例中的局部变量现在可以是案例本身的局部变量而不是整个开关块。一个考虑Day枚举类的示例(类似于Jon在语法上也尝试过的)以进行进一步解释:

    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);
        
    
    

    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'
    
    

【讨论】:

【参考方案5】:

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

    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
        
    

【讨论】:

【参考方案6】:

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

标签声明:

标签声明: 标识符:声明

LabeledStatementNoShortIf: 标识符:StatementNoShortIf

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

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

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

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

【讨论】:

以上是关于在 Java 开关中声明和初始化变量的主要内容,如果未能解决你的问题,请参考以下文章

如何在Java中声明和初始化数组?

java声明变量

java基本语法day01_09变量的声明与使用

Java成员变量和局部变量

java变量类型

java变量