有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

具有继承调用的构造函数中的java最终变量

我刚刚发现了一些很奇怪的事情。如果使用重写方法从隐式超级构造函数调用最终变量,则在调用时永远不会初始化元素:

public static abstract class A {

    public A() 
    {
        doSomething();
    }

    public abstract void doSomething();

}

public static class B extends A {

    private final Object s = new Object(); 

    public B()
    {
    }

    public void doSomething() {
        System.out.println(s);
    }
}

public static void main( String[] args )
{
    new B();// prints 'null'
}

如果未重写该方法,则将正确实例化最终变量:

public static class B  {

    private final Object s = new Object(); 

    public B()
    {
        doSomething();
    }

    public void doSomething() {
        System.out.println(s);
    }
}

public static void main( String[] args )
{
    new B(); // prints the object correctly
}

最后,对我来说更奇怪的是(我认为这与字符串#intern机制有关)

public static abstract class A {

    public A() 
    {
        doSomething();
    }

    public abstract void doSomething();

}

public static class B extends A {

    private final String s = "Hello"; 

    public B()
    {
    }

    public void doSomething() {
        System.out.println(s);
    }
}

public static void main( String[] args )
{
    new B(); // will print "Hello"
}

我的问题是,在第一种情况下,我能做些什么来解决这个问题,我应该使用确保非空值的getter吗

我有点理解为什么会出现第一种情况(构造函数在初始化任何实例变量之前隐式调用“super”构造函数),但是,如果我是正确的,在这种情况下,为什么第三种情况会正确打印“Hello”


共 (1) 个答案

  1. # 1 楼答案

    理解基类的构造函数是在子类的构造函数之前执行的,这一点很重要。这意味着,在基类的构造过程中,子类的字段可能尚未初始化。(但是,它们在子类的构造过程中初始化。)

    My question is what can i do in the first case to fix this, should i use a getter that ensures non-null value ?

    您发现的问题是永远不要从构造函数中调用可重写方法的原因之一

    getter可能同样糟糕,因为getter也是可重写的

    而不是

    Object s = new Object();
    
    ...
    
    public void doSomething() {
        System.out.println(s);
    }
    

    B中,可以将要在A的构造中使用的变量作为参数传递给A的构造函数:

    public B() {
        super(new Object());
    }
    

    这将传递与构造B对象相关的数据,以便B的构造函数是“自包含的”。这是相当混乱的,我建议你重新考虑你的课程结构


    关于第三个案件:

    private final String s = "Hello";
    

    由于"Hello"是一个编译时常量表达式,并且s是最终的,因此Java编译器可以自由地内联使用s,也就是说,可以自行决定将s替换为"Hello"