Search code examples
javaarraysfield.class-file

Where is the initial value of a variable stored in a class file


I am still trying to learn the java class file format and i understand that constant variables values are stored under the ConstantValue attribute, however i don't understand where the non constant field values are stored in the class file. Such as

public Robot robot = new Robot();

I looked through oracles class file specification and i couldn't find any attributes like this.


Solution

  • The only attribute for storing initial values is the ConstantValue. However, this is only used for static variables. In Java compiled code, it's only used for static final variables, though it can be used for any static variable in handwritten bytecode. And of course, the value has to be constant.

    For the example you posted, you have what is known in Java as an initializer. These have no equivalent in the classfile. Instead, the Java compiler will paste this code after every superclass constructor call returns. Note that this means that if you can view them before this point, such as by calling a virtual method in the superclass constructor, they will not have been initialized yet.

    Here's an example.

    public class initializers {
    
        static final float f = 4;
        static final int i = int.class.hashCode();
        static int i2 = 4;
    
        public final Object x = null;
        public Object x2 = null;
    
    
        public initializers() {}
        public initializers(int x) {this();}
        public initializers(float x) {}
    }
    

    Compiling and dissembling the class results in

    .version 49 0
    .class super public initializers
    .super java/lang/Object
    
    .field static final f F = 4.0F
    .field static final i I
    .field static i2 I
    .field public final x Ljava/lang/Object;
    .field public x2 Ljava/lang/Object;
    
    .method public <init> : ()V
        .limit stack 2
        .limit locals 1
        aload_0
        invokespecial java/lang/Object <init> ()V
        aload_0
        aconst_null
        putfield initializers x Ljava/lang/Object;
        aload_0
        aconst_null
        putfield initializers x2 Ljava/lang/Object;
        return
    .end method
    
    .method public <init> : (I)V
        .limit stack 1
        .limit locals 2
        aload_0
        invokespecial initializers <init> ()V
        return
    .end method
    
    .method public <init> : (F)V
        .limit stack 2
        .limit locals 2
        aload_0
        invokespecial java/lang/Object <init> ()V
        aload_0
        aconst_null
        putfield initializers x Ljava/lang/Object;
        aload_0
        aconst_null
        putfield initializers x2 Ljava/lang/Object;
        return
    .end method
    
    .method static <clinit> : ()V
        .limit stack 1
        .limit locals 0
        getstatic java/lang/Integer TYPE Ljava/lang/Class;
        invokevirtual java/lang/Object hashCode ()I
        putstatic initializers i I
        iconst_4
        putstatic initializers i2 I
        return
    .end method
    

    As you can see, f is the only one that used a ConstantValue attribute. i did not because it wasn't a constant expression, while i2 did not because it's not final. The code to initialize i and i2 was placed in the static initializer (the 'class constructor'). The code to initialize x and x2 was pasted after both superclass constructor calls, but not in the second constructor, which just calls a constructor in the same class.