在循环内创建最终变量

时间:2009-03-04 07:48:31

标签: java final

在java中允许这样做:

for(int i=0;i<5;i++){
  final int myFinalVariable = i;
}

我的问题的关键字是final。是否允许执行随循环的每次运行而变化的最终变量?我想知道这是因为final说你不能改变变量的值(只调用myFinalVariable = i),但是我用final int重新定义了整个变量。

它们是两个完全不同的变量,只是具有相同的名称 - 上一次循环运行的变量已经在通往垃圾收集器的路上了吗?

5 个答案:

答案 0 :(得分:82)

是的,这是允许的。 final关键字表示您无法在其范围中更改变量的值。对于循环示例,您可以将变量超出循环底部的范围,然后返回到范围内,并在循环顶部使用新值。分配给循环中的变量将不起作用。

答案 1 :(得分:12)

你是对的,对于循环中的每次迭代,你都在创建一个新变量。变量确实共享相同的名称,但这很好,因为它们不在同一范围内。下一个例子工作:

final int myFinalVariable = 0;
for(int i=0;i<5;i++){
  myFinalVariable = i;
}

答案 2 :(得分:9)

变量只是堆栈上的一个位置。尝试使用尽可能小的范围保持变量,并尝试使它们成为最终变量。但是范围和最终只是源代码的东西......从代码生成/ VM的角度来看,它们根本不重要。

在您的具体示例中,使用“int”不会创建垃圾。但是,如果它是被创建的对象,那么对于这两种情况,垃圾量以及垃圾何时符合清理条件都是相同的。

请使用以下代码:

public class X
{
    public static void main(final String[] argv)
    {
        foo();
        bar();
    }

    private static void foo()
    {
        for(int i=0;i<5;i++)
        {
            final int myFinalVariable = i;
        }
    }

    private static void bar()
    {
        for(int i=0;i<5;i++)
        {
            int myFinalVariable = i;
        }
    }
}

编译器为每种方法生成相同的字节码:

public class X extends java.lang.Object{
public X();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method foo:()V
   3:   invokestatic    #3; //Method bar:()V
   6:   return

private static void foo();
  Code:
   0:   iconst_0
   1:   istore_0
   2:   iload_0
   3:   iconst_5
   4:   if_icmpge       15
   7:   iload_0
   8:   istore_1
   9:   iinc    0, 1
   12:  goto    2
   15:  return

private static void bar();
  Code:
   0:   iconst_0
   1:   istore_0
   2:   iload_0
   3:   iconst_5
   4:   if_icmpge       15
   7:   iload_0
   8:   istore_1
   9:   iinc    0, 1
   12:  goto    2
   15:  return

}

添加另一个在循环外声明变量的方法会因为声明变量的顺序而给出稍微不同的字节码。请注意,此版本的变量无法成为最终版本。最后一个版本不是最好的方法(如果可以的话,循环内的最终变量是最好的):

private static void car()
{
    int myFinalVariable;

    for(int i=0;i<5;i++)
    {
        myFinalVariable = i;
    }
}

private static void car();
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_5
   4:   if_icmpge       15
   7:   iload_1
   8:   istore_0
   9:   iinc    1, 1
   12:  goto    2
   15:  return

}

答案 3 :(得分:2)

如上所述,是的,您可能确实将循环中的变量标记为“最终”#。这是这样做的结果(Java 7,Eclipse Indigo,Mac OS X Lion)。

for ( int i = 0; i < 5; i++ ) {

  // With 'final' you cannot assign a new value.
  final int myFinalVariable = i;  // Gets 0, 1, 2, 3, or 4 on each iteration.
  myFinalVariable = 7; // Compiler error: The final local variable myFinalVariable cannot be assigned.

  // Without 'final' you can assign a new value.
  int myNotFinalVariable = i;  // Gets 0, 1, 2, 3, or 4 on each iteration.
  myNotFinalVariable = 7; // Compiler is OK with re-assignment of variable's value.

}

答案 4 :(得分:2)

在循环内声明的变量只有在执行循环之前才有范围。

在循环中将变量声明为final是对循环中的变量没有区别,但如果我们使用final修饰符声明循环外的变量,那么分配给基本类型的值或分配给引用变量的Object不能改变了。

在下面的示例中,前两个循环没有问题,两个循环都给出相同的输出,但第三个循环给出了编译时错误。

公共课堂考试{

public static void main(String[] args) {
    for (int i = 0; i < 5; i++) {
        final int j= i;
        System.out.println(j);
    }
    for (int i = 0; i < 5; i++) {
        int j= i;
        System.out.println(j);
    }

    final int j;
    for (int i = 0; i < 5; i++) {
        j= i;
        System.out.println(j);
    }
}

}

如果我错了,请纠正我。