【问题标题】:Re-declaring variables inside loops in Java在 Java 中的循环内重新声明变量
【发布时间】:2015-08-31 16:35:48
【问题描述】:

在Java中,我们不能声明一个变量与另一个同名的变量在同一范围内:

int someInteger = 3;

...

int someInteger = 13;

语法错误,无法编译。但是,如果我们把它放在一个循环中:

for (int i = 0; i < 10; i++) {
   int someInteger = 3;
}

不会产生错误,效果很好。我们基本上是在声明相同的变量。是什么原因?我不知道/理解这背后的逻辑是什么?

【问题讨论】:

  • 我们基本上声明了同一个变量。你是吗?您如何访问其先前的值?
  • 垃圾回收。循环中的变量不再在程序执行的范围内。
  • @MalikBrahimi:循环内的变量确实不在循环外的范围内,但原因是不是“垃圾收集”。垃圾回收是指对分配有new的对象进行处理。
  • Java 变量是块范围的。其余的,谷歌“Java shadowing”。

标签: java loops variables


【解决方案1】:

这样想,每次循环之后,作用域都被“销毁”了,变量也就消失了。在下一个循环中,会创建一个新范围,并且可以在该范围内再次声明变量。

出于类似的原因,您也可以这样做

{
   int someInteger = 3;
}
{
   int someInteger = 13;
}

顺便说一句,Java 不允许局部变量遮蔽,这可能很不方便

int x = 3;
{
   int x = 13; // error!
}

Consumer<Integer> consumer = (x)->print(x);  // ERROR! 
// lambda parameter is treated like local variable

Runnable r = ()->{ int x; ... } // ERROR
// lambda body is treated like local block

【讨论】:

    【解决方案2】:

    Java 具有所谓的“块作用域”,这意味着在其中声明变量的任何代码块(由封闭的花括号定义),(并且只有在那里)是它存在的地方。这也意味着每个变量只能在任何给定块中声明一次。

    当你说

    for (int i = 0; i < 10; i++) {
      int someInteger = 3;
    }
    

    每次迭代都会创建一个新块。类似于说

    { 
      int someInteger = 3;
    }
    { 
      int someInteger = 3;
    }
    ...
    

    在这种情况下,每个块中只有一个名为 someInteger 的变量。

    当你说

    { 
      int someInteger = 3;
      ...
      int someInteger = 3;
    }
    

    编译器正确地抱怨您在同一代码块(或范围)中声明了多个具有相同名称的变量。

    【讨论】:

      【解决方案3】:

      您可以将变量推送到数组中。

      Stack myarr = new Stack();
      for (int i = 0; i < 10; i++) {
         int someInteger = 3;
      myarr.push(someInteger);
      }
      

      或者创建一个变量everyloop

      for (int i = 0; i < 10; i++) {
         int someInteger+i = 3;
      }
      

      【讨论】:

        【解决方案4】:

        每个变量都有一个作用域。范围嵌套在方括号 {} 内。当你离开这个范围时,这个上下文就消失了,所以你可以定义其他同名的变量。否则,你不能。

        我给你举两个例子:

        // define new scope named "methodA"
        public void methodA() {
           int a = 3; 
           // define new scope named "loop"
           for (int i = 0; i < 10; i++) {
              int a = 6; // ERROR
           }
        }
        

        在上述情况下,您将遇到错误。因为“loop”作用域在“methodA”作用域内,所以当你进入“loop”作用域时,a的第一个定义仍然存在。

        第二种情况:

        // define new scope named "methodA"
        public void methodA() {
           // define new scope inside methodA scope
           {
               int a = 3;
           } 
           // define new scope named "loop"
           for (int i = 0; i < 10; i++) {
              int a = 6; // NO ERROR
           }
        }
        

        上面的代码会编译成功,因为第一个定义的 a 和第二个定义的 a 的作用域不同,而且这些作用域没有嵌套。

        【讨论】:

        • 虽然可以被本地/匿名类隐藏...但不能被 lambda
        【解决方案5】:

        如果与第一次使用的作用域不同,您可以在类或方法中重复使用相同的变量名。

        在您的第一个示例中,两个变量具有相同的范围,因此名称冲突并且编译器报告错误。在您的第二个示例中,它们具有不同的范围 - 一个在方法中,另一个在 for 循环中 - 因此没有冲突并且代码可以编译。

        【讨论】:

          【解决方案6】:

          在块的范围内,创建了一个新的someInteger,它遮蔽任何其他someInteger 的词法范围。 Variable shadowing 上的维基百科条目说(部分)

          变量遮蔽发生在某个范围(决策块、方法或内部类)内声明的变量与外部范围内声明的变量同名时。在标识符(名称,而不是变量)级别,这称为name masking

          【讨论】:

            【解决方案7】:

            不同的作用域;

            int variables = 3;
            ....
            int variables = 13;
            

            范围一致,肯定编译错误; 但是

            for (;;;) {
                 int variables = 13;
            }
            

            每一个循环相当于重新建立变量variables之前的变量variables随着作用域的变化而消失; JVM栈帧就是这样一种机制(局部变量随着赋值过程的开始,随着过程的结束而消除);

            【讨论】:

              猜你喜欢
              • 2018-05-13
              • 1970-01-01
              • 1970-01-01
              • 2012-02-02
              • 1970-01-01
              • 2015-04-12
              • 1970-01-01
              • 2014-06-15
              • 2011-05-28
              相关资源
              最近更新 更多