Java 为什么没有为最终实例变量指定默认值?

Java 为什么没有为最终实例变量指定默认值?,java,final,Java,Final,我在准备Java OCA认证时遇到了一个类似的问题,如以下问题: public class TestClass { final int i; public static void main(String[] args) { TestClass t = new TestClass(); System.out.println(t.i); } } 根据Java,实例和静态变量都是默认值。令人惊讶的是,这是我的控制台中显示的错误: error:

我在准备Java OCA认证时遇到了一个类似的问题,如以下问题:

public class TestClass {
    final int i;

    public static void main(String[] args) {
        TestClass t = new TestClass();
        System.out.println(t.i);
    }
}
根据Java,实例和静态变量都是默认值。令人惊讶的是,这是我的控制台中显示的错误:

error: variable i not initialized in the default constructor final int i;

为什么我没有在上面指定一个默认值?

标准整数默认为零,那么为0文本整数创建一个常量有什么意义呢?它没有任何用途,因此您必须在初始化期间创建一个带有值的常量。

标准整数默认为零,那么为0文本整数创建常量有什么意义呢?它没有任何用途,因此您必须在初始化期间创建一个具有值的常量

在上述情况下,为什么不指定默认值?谁能 解释

如果要提供默认值,则无法更改其值。当final关键字与变量一起使用时,意味着一旦分配了变量,就不能重新分配

最终变量只能初始化一次,可以通过 初始值设定项或赋值语句。不需要这样做 在声明点初始化:这称为空白 最后一个变量。类的空最终实例变量必须为 在它所在的类的每个构造函数中都有明确的赋值 宣布;同样,一个空白的最终静态变量必须是 在声明它的类的静态初始值设定项中赋值; 否则,在这两种情况下都会发生编译时错误。注意:如果 变量是引用,这意味着该变量不能 重新绑定以引用另一个对象。但它所代表的对象 如果引用最初是可变的,那么它仍然是可变的

进一步阅读:

在上述情况下,为什么不指定默认值?谁能 解释

如果要提供默认值,则无法更改其值。当final关键字与变量一起使用时,意味着一旦分配了变量,就不能重新分配

最终变量只能初始化一次,可以通过 初始值设定项或赋值语句。不需要这样做 在声明点初始化:这称为空白 最后一个变量。类的空最终实例变量必须为 在它所在的类的每个构造函数中都有明确的赋值 宣布;同样,一个空白的最终静态变量必须是 在声明它的类的静态初始值设定项中赋值; 否则,在这两种情况下都会发生编译时错误。注意:如果 变量是引用,这意味着该变量不能 重新绑定以引用另一个对象。但它所代表的对象 如果引用最初是可变的,那么它仍然是可变的

进一步阅读:


将默认值指定给最终变量将首先破坏使变量成为最终变量的整个目的。 final意味着您不能在指定值后更改该值。
如果为最终变量指定了默认值,则即使是第一次,您也无法将变量的值设置为其他值。

将默认值指定给最终变量将无法实现将变量设置为最终变量的全部目的。 final意味着您不能在指定值后更改该值。
如果为最终变量指定了默认值,则即使是第一次,也无法将该变量的值设置为其他值。

它的定义如下:

第十六章。明确分配 每个局部变量和每个空白的最终字段在访问其值时必须有一个明确的赋值

[……]

对于每次访问局部变量或空白最终字段x,必须在访问之前明确指定x,否则会发生编译时错误

其中,术语“空白最终字段”是指没有值或初始值设定项的最终字段,并且明确指定意味着无论发生什么情况,该字段都将被指定:

明确赋值背后的思想是,对局部变量或空白final字段的赋值必须发生在访问的每个可能执行路径上。类似地,明确的未赋值背后的思想是,在指定的任何可能的执行路径上,不允许对空白最终变量进行其他赋值。 除了按规范抛出错误外,决策背后还有逻辑推理。没有必要为空白的最终字段设置默认值。在你的例子中,空白的最终结果是一个整数,它只需给出0,你就不能改变它。变量的用途是什么

另外,如果最终变量没有显式地给出默认值,为什么不首先初始化它呢?由于默认值为v,以后无法重新分配给它
值已经给定了,为什么不现在初始化它呢?

它的定义如下:

第十六章。明确分配 每个局部变量和每个空白的最终字段在访问其值时必须有一个明确的赋值

[……]

对于每次访问局部变量或空白最终字段x,必须在访问之前明确指定x,否则会发生编译时错误

其中,术语“空白最终字段”是指没有值或初始值设定项的最终字段,并且明确指定意味着无论发生什么情况,该字段都将被指定:

明确赋值背后的思想是,对局部变量或空白final字段的赋值必须发生在访问的每个可能执行路径上。类似地,明确的未赋值背后的思想是,在指定的任何可能的执行路径上,不允许对空白最终变量进行其他赋值。 除了按规范抛出错误外,决策背后还有逻辑推理。没有必要为空白的最终字段设置默认值。在你的例子中,空白的最终结果是一个整数,它只需给出0,你就不能改变它。变量的用途是什么


另外,如果最终变量没有显式地给出默认值,为什么不首先初始化它呢?您以后不能重新分配给它,因为已经给出了默认值,所以为什么不现在初始化它?

错误消息具有误导性,因为它可以用默认值初始化:

class FinalTest{
    final int x = printXAndInitializeIt();

    private int printXAndInitializeIt(){
        System.out.println("x before initialization = "+x);
        return 1;
    }

    public static void main(String[] args) {
        FinalTest ft = new FinalTest();
        System.out.println("x after initialization = "+ft.x);
    }
}
输出:

x before initialization = 0
x after initialization = 1

但即使有默认值,final变量也需要显式初始化,因为在大多数情况下,缺少初始化是由错误引起的。此错误可以更改为警告,但我猜Java设计人员认为这比抱歉更安全。

错误消息具有误导性,因为它可以用默认值初始化:

class FinalTest{
    final int x = printXAndInitializeIt();

    private int printXAndInitializeIt(){
        System.out.println("x before initialization = "+x);
        return 1;
    }

    public static void main(String[] args) {
        FinalTest ft = new FinalTest();
        System.out.println("x after initialization = "+ft.x);
    }
}
输出:

x before initialization = 0
x after initialization = 1

但即使有默认值,final变量也需要显式初始化,因为在大多数情况下,缺少初始化是由错误引起的。这个错误可以改为警告,但我猜Java设计人员认为这比抱歉更安全。

不是final字段没有初始化为默认值,事实上它们有点像,尽管只有在做一些愚蠢的事情时,您才会观察到这一事实,但是您需要在声明中、初始化程序块中或在每个构造函数中明确地初始化它们一次

为了理解这一动机,请考虑下面的代码:

public class Foo {
    private final int mValue;

    public Foo(final boolean shouldSet) {
        if (shouldSet) {
            mValue = 1;
        }
    }

    // ...
}

如果字段不是final,那么编译器将在其声明末尾推断出一个隐式=0;但最后一个字段并不总是有效的。在上面的例子中,编译器甚至不能预先判断它是否有效。因此,编译器通过从不推断隐式=0并要求显式初始化来避免这个问题。

并不是最终字段没有初始化为默认值,事实上它们有点像,尽管只有在做一些愚蠢的事情时,您才会观察到这一事实,但是您需要在声明中、初始化程序块中或在每个构造函数中明确地初始化它们一次

为了理解这一动机,请考虑下面的代码:

public class Foo {
    private final int mValue;

    public Foo(final boolean shouldSet) {
        if (shouldSet) {
            mValue = 1;
        }
    }

    // ...
}

如果字段不是final,那么编译器将在其声明末尾推断出一个隐式=0;但最后一个字段并不总是有效的。在上面的例子中,编译器甚至不能预先判断它是否有效。因此,编译器通过从不推断隐式=0并要求显式初始化来避免这个问题。

它会得到什么值?如果一个类有一个最终成员,它必须在构造函数中显式初始化。我认为它会显示0,因为int类型变量的默认值是0@Arthurcinander希望它得到什么价值?如果一个类有一个最终成员,它必须在构造函数中显式初始化。我认为它会显示0,因为int类型变量的默认值是0@ArthurCinaderI想检查它是否显示int的默认值。但我明白了,你明白我的意思吗?零的常数完全违背了常数的目的。我们使用常数来表示字段宽度、网格中游戏项目之间的间距等。零完全没有意义。常数没有默认值,我认为你忽略了这一点。因为常量不能改变值,所以它们需要同时定义和声明。这就是为什么没有默认值的原因。因为0或etc不符合常数的用途,因此编译器如何知道默认值是什么?零的常数完全不符合常数的用途。真正地我想检查它是否显示int的默认值va
卢。但我明白了,你明白我的意思吗?零的常数完全违背了常数的目的。我们使用常数来表示字段宽度、网格中游戏项目之间的间距等。零完全没有意义。常数没有默认值,我认为你忽略了这一点。因为常量不能改变值,所以它们需要同时定义和声明。这就是为什么没有默认值的原因。因为0或etc不符合常数的用途,因此编译器如何知道默认值是什么?零的常数完全不符合常数的用途。真正地请参阅JLS以了解明确[un]分配的详细信息。请参阅JLS以了解明确[un]分配的详细信息。初始化的有趣之处在于,即使是非最终成员变量,如果显式初始化,也会初始化两次,一次是默认值,然后是显式值。所以私有int指数=0;将变量初始化为零两次。JVM被禁止将其优化为一次。初始化的一个有趣的怪癖是,如果显式初始化,即使是非最终成员变量也会被初始化两次,一次初始化为默认值,然后再初始化为显式值。所以私有int指数=0;将变量初始化为零两次。JVM禁止将其优化到一次。