Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why an instance constant has a value when the object has all fields set to default?

Here is the code:

public class Main {
    public static void main(String[] args) {
        new B();
    }
}

class A {
    A() {
        System.out.println("A constructor before");
        action();
        System.out.println("A constructor after");
    }

    protected void action() {
        System.out.println("Never called");
    }
}

class B extends A {
    private final int finalField = 42;
    private int field = 99;

    B() {
        System.out.println("B constructor");
        action();
    }

    public void action() {
        System.out.println("B action, finalField=" + finalField + ", field=" + field);
    }
}

And the result is:

A constructor before
B action, finalField=42, field=0
A constructor after
B constructor
B action, finalField=42, field=99

I confused by this line :

B action, finalField=42, field=0

Object B is not completely initialized and when we call method "action" from super class constructor - variable "field" has a default value, but the final variable "finalField" already has value 42.

When was the "finalField" initialized?

like image 749
Roman Frolov Avatar asked Sep 13 '25 03:09

Roman Frolov


1 Answers

When a final field is initialized with a constant expression (15.29), it's called a constant variable (4.12.4):

private final int finalField = 42;

This means that the string

"B action, finalField=" + finalField + ", field="

is a constant expression itself and its value is determined at compile time. If you inspect the compiled class file you will in fact find the string B action, finalField=42, field= in the constant pool section.

In general, when a field that is a constant variable is used, it has to be replaced by its value at compile time. It is not allowed (13.1) to reference the field at run time:

  1. A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.

    If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field's value to V during instance creation (§12.5).

The field initializer still runs when you would expect: after A constructor has returned and before B constructor starts. Observing the uninitialized value is tricky because the compiler inlines the uses of the variable, but you can access the value of the field through reflection:

public void action() {
    try {
        System.out.println("B action, finalField="
            + getClass().getDeclaredField("finalField").get(this)
            + ", field=" + field);
    } catch (IllegalAccessException | NoSuchFieldException e) {
        e.printStackTrace();
    }
}

Output:

A constructor before
B action, finalField=0, field=0
A constructor after
B constructor
B action, finalField=42, field=99
like image 57
Joni Avatar answered Sep 15 '25 19:09

Joni