直接分配最终变量和在构造函数中分配最终变量之间有区别吗?

时间:2011-12-02 09:16:11

标签: java initialization final

最终变量value的这两个初始化之间是否存在差异?

class Test {
    final int value = 7;
    Test() {}
}

class Test {
    final int value;
    Test() {
        value = 7;
    }
}

-

编辑:一个更复杂的例子,涉及子类。在这种情况下,“0”打印到stdout,但如果我直接指定值,则打印7。

import javax.swing.*;
import java.beans.PropertyChangeListener;

class TestBox extends JCheckBox {

    final int value;

    public TestBox() {
        value = 7;
    }

    public void addPropertyChangeListener(PropertyChangeListener l) {
        System.out.println(value);
        super.addPropertyChangeListener(l); 
    }

    public static void main(String... args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        frame.setContentPane(panel);
        panel.add(new TestBox());
        frame.pack();
        frame.setVisible(true);
    }
}

7 个答案:

答案 0 :(得分:4)

字节码级别存在差异:

源代码:

  final int value;

  public TestBox() {
      value = 7;
  }

addPropertyChangeListener生成以下代码:

   0:   getstatic       #3; 
   3:   aload_0
   4:   getfield        #2; 
   7:   invokevirtual   #4; 

源代码:

final int value = 7;

public TestBox() {      
}

addPropertyChangeListener生成以下代码:

   0:   getstatic       #3; 
   3:   bipush  7
   5:   invokevirtual   #4; 

因此存在细微差别。但不实用。

似乎编译器可以将最终变量作为常量处理,如果它在定义语句中初始化的话。当然,不同的编译器可能采用不同的方式。

答案 1 :(得分:2)

尝试了一个非常简单的例子,是的,当在父的构造函数中访问值时,它是单元化的(应该是),除非它的最终初始化时声明。这个过程是由EJP描述的,但是#0步骤:使用指定的值(如果有的话)初始化决赛。

答案 2 :(得分:1)

对最终变量的常见误解是它无法改变其值。最终修饰符(JLS 4.5.4)的实际含义是“最终变量只能分配给一次”。

您遇到了可以评估所谓的“空白最终”(声明但尚未分配)变量的情况之一,以便它评估指定数据类型的默认值,即使它后来分配了不同的值。

答案 3 :(得分:0)

其他在第二种情况下,您可以根据调用的构造函数或传递给它的参数指定不同的值, NO。

答案 4 :(得分:0)

不,没有。唯一的区别是用于初始化字段的顺序:直接初始化的字段在构造函数中初始化之前初始化。

答案 5 :(得分:0)

我认为没有任何区别。但是你需要的价值可能会帮助你决定使用哪个。

  1. 如果变量是final并且在构造函数中赋值为固定值,则无需在构造函数中赋值。
  2. 如果变量是final并且赋值不同,作为参数传递,则在构造函数中,则需要在构造函数中赋值。
  3. 大多数情况下使用第一种情况。因为据我所知,最终的变量只是常量。

答案 6 :(得分:0)

构造函数按以下顺序执行:

  1. 调用super()。
  2. 初始化局部变量并调用匿名初始化程序块{}。
  3. 调用构造函数本身的代码。请注意,如果构造函数显式调用super()本身,则在#1下进行处理。
  4. 因此,您的问题的答案是初始化将从initial-in-initializer版本中的#2移动到initialize-in-constructor版本中的#3。但是,除非您使用匿名初始化程序块{}或可能使用其他字段的先前初始化初始化的字段,否则无法区分它们。

相关问题