最终在Java中做了什么? - 硬版本

时间:2012-05-25 14:29:59

标签: java inner-classes final

我从小就知道final是一个关键字,当应用于变量时,无法将变量重新分配给其他东西。 “如果一个变量是最终的那么它就是一个常数”总结了许多,虽然我不是那个定义的粉丝,但它可能是一个很好的方法来记住这个概念。我只想说you cannot change the value of the variable(无论“价值”是什么意思)。

我的生活很幸福,但有一天我深入了解method local inner classes ......

方法内定义的内部类不能访问方法本身定义的变量。为什么?因为当类存在于Heap中并且它可能在方法完成后保持存在(类的有效引用可能被传递并存储在其他地方),这些变量存在于堆栈中并且在方法返回时它们会死亡。我们不希望内部类在以后尝试访问不再存在的变量,因为那时世界将会结束。

完美。这说得通。优秀!然后:除非你声明那些变量最终.....然后你的类可以访问它们,编译器不会把你送到地狱......

WHY???我的意思是,这是什么样的巫术?最终究竟做了什么以及为什么我不得不等待谈论方法本地内部类来解决这个问题?假设最终变量存储在堆中,无论它们在何处被定义,除了使方法本地内部类快乐的概念之外还有其他任何应用吗?

5 个答案:

答案 0 :(得分:6)

引自“The Final word on the final keyword”:

  

这种限制的原因[本地类只能引用声明为final的局部变量和参数。]如果我们对如何实现本地类有所了解,这一点就变得很明显了。匿名本地类可以使用局部变量,因为编译器会自动为类提供一个私有实例字段来保存类使用的每个局部变量的副本。编译器还为每个构造函数添加隐藏参数,以初始化这些自动创建的私有字段。因此,本地类实际上并不访问本地变量,而只是它们自己的私有副本。这种方法可以正常工作的唯一方法是将局部变量声明为final,以确保它们不会改变。有了这种保证,本地类就可以确保其变量的内部副本能够准确地反映实际的局部变量。

答案 1 :(得分:4)

在你所指的情况下,你可能会陷入一种奇怪的境地。在方法中创建内部类时,该内部类的范围可以超过方法调用的生命周期。那么,如果你允许内部类引用一个仅在方法本身中定义的变量,那么当方法完成时会发生什么?

例如:

public void doSomethingLater()
{
  int value = 13;

  Thread t = new Thread(new Runnable() { 
    @Override public void run() {
      System.out.println("The value is " + value);
    }
  });
  t.start();

  value = 25;
}

假设这是允许的java代码。该程序在运行时会打印什么? 13? 25?为了实现对方法局部变量的内部类访问,Java设计者决定采用在内部类创建时复制变量值的简单路径。为了让Java程序员非常清楚该值被复制,它们会强制您使用最终引用,这样您的方法就无法在将来的某个时间点修改该变量(这可能会使预期新值会影响的开发人员感到困惑)内在的类。)

实际上,这就是幕后发生的事情:

public void doSomethingLater()
{
  int value = 13;

  Thread t = new Thread(new Runnable() { 
    private final _innerValue = value;
    @Override public void run() {
      System.out.println("The value is " + _innerValue);
    }
  });
  t.start();

  value = 25;
}

因此,您可以看到后面对值的修改不会影响内部类。

更新:

嗯,猜猜@pgras打败了我。无论如何,这是该答案中包含的引用的一个例子。

答案 2 :(得分:1)

在Java中,final字面意思是“如果一个变量是最终的,它就是一个常数”,但这并不意味着很多人认为它意味着什么。

对于原语,它真的 意味着。但是对于对象,所有final意味着您无法更改变量引用的对象实例。但是,完全允许可以根据需要更改该对象。

这就是为什么方法本地内部类需要它引用的变量声明为final。它们被声明为final,保证该方法不能改变这些变量引用的对象,这是方法内部类正常运行所需的保证。

当你谈论堆和堆栈时,你会混淆变量和对象。引用对象的方法本地变量存在于堆栈中,但对象它们引用堆上的实时。因此,即使方法超出范围,内部类也可以继续引用这些对象,因为对象实例存在于方法范围之外。

答案 3 :(得分:0)

实际上,它与以下内容重复:Cannot refer to a non-final variable inside an inner class defined in a different method

请参阅此答案:https://stackoverflow.com/a/1299889/277683

你似乎理解堆栈等,所以我会简短。总而言之,当您创建引用final时,编译器可以在编译时安全地确定其值 并对内部类使用相同的内容。

答案 4 :(得分:0)

答案要简单得多。

Java开发人员(制作java的人)认为如果你能访问一个内部类中不是最终的变量会让人感到困惑,所以他们添加了一个要求变量应该是最终的以避免混淆。

我不确定java是如何实现这一点的,但我猜他们只是在创建内部类时复制变量。也可以使用非最终变量轻松完成,但java开发人员认为这会让人感到困惑。