Strange behavior using braces in Java

2020-05-25 03:56发布

问题:

When I run the following code:

public class Test {

  Test(){
    System.out.println("1");
  }

  {
    System.out.println("2");
  }

  static {
    System.out.println("3");
  }

  public static void main(String args[]) {
    new Test();
  }
}

I expect to get the output in this order:

1
2
3

but what I got is in reverse order:

3
2
1

Can anyone explain why it is output in reverse order?

================

Also, when I create more than one instance of Test:

new Test();
new Test();
new Test();
new Test();

static block is executed only at first time.

回答1:

It all depends on the order of execution of initialization statements. Your test demonstrates that this order is:

  1. Static initialization blocks
  2. Instance initialization blocks
  3. Constructors

Edit

Thanks for the comments, now I can quote the appropriate part in the JVM specification. Here it is, the detailed initialization procedure.



回答2:

3 - is a static initializer, it runs once when the class is loaded, which happens first.

2 - is an initializer block, the java compiler will actually copy this into each constructor, so you can share some initialization between contructors if you like. Rarely used.

1 - will be executed when you construct the object, after (3) and (2)..

More information here



回答3:

Static blocks are executed first.

And then the instance instance intialization blocks

Please see JLS for instance intializers

{

// sop statement

}

you cannot have a return statment within the instance initialization block, just like constructors.



回答4:

Test(){System.out.println("1");}

    {System.out.println("2");}

    static{System.out.println("3");}

static things are executed first, {System.out.println("2");} isn't a part of a function, because of its scope it is called first, and Test(){System.out.println("1");} is called last because the other two are called first



回答5:

First, class is loaded into the JVM and class initialization happens. During this step static blocks are executed. "{...}" is just a syntactic equivalent of "static{...}". Since there is already a "static{...}" block in the code, "{...}" will be appended to it. That's why you have 3 printed before 2.

Next once the class is loaded, java.exe (which I assumed you executed from the command line) will find and run the main method. The main static method initializes the instance whose constructor is invoked, so you get "1" printed last.



回答6:

Because the static{} code is run when the class is first initialized within the JVM (i.e. even before main() is called), the instance {} is called when an instance is first initialized, before it's constructed, and then the constructor is called after all that is done.



回答7:

I‘ve get the bytecode-like code here by ASM .

I think this can answer your question , explaining what happened when a object is created in this occasion.

public class Test {
static <clinit>() : void
GETSTATIC System.out : PrintStream
LDC "3"
INVOKEVIRTUAL PrintStream.println(String) : void
RETURN


<init>() : void
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
GETSTATIC System.out : PrintStream
LDC "2"
INVOKEVIRTUAL PrintStream.println(String) : void
GETSTATIC System.out : PrintStream
LDC "1"
INVOKEVIRTUAL PrintStream.println(String) : void
RETURN

public static main(String[]) : void
NEW Test
INVOKESPECIAL Test.<init>() : void
RETURN
}

we can see LDC "3" is in the "clinit" , this is a class initializer .

The lifetime of a object usually is : loading class -> linking class -> class initialization -> object instantiation -> use -> GC . That's why 3 appears first. And as this is in the class level , not object level , it will appear once as class type will be loaded once . For details , referencing to inside the Java2 Virtual Machine : life time of a type

LDC "2" and `LDC "1" is in "init" , the constructor.

Reason why it's in this order is : Constructor will first execute some implict instruction such as super constructor and code in the {} of a class , then execute code which's in their construtor explicit.

That's what a compiler will do to the java file.



回答8:

It doesn't look like anyone stated why the 3 is only printed once explicitly. So I would add that this is related to why it is printed first.

Statically defined code is flagged as being separate from any particular instance of the class. In general, statically defined code can be considered to not be any class at all (of course there's some invalidity in that statement when scoping is considered). Thus that code gets run once the class is loaded, as stated above, as in, it isn't being called when an instance is constructed Test(), thus calling the constructor multiple times will not result in the static code being run anymore.

The bracketed code containing the 2 gets prepended to the construct, as started above, because it is sort of a precondition to all of the constructors in the class. You don't know what will happen in the constructors for Test, but you are guaranteed that they all start by printing 2. Thus this happens before anything in any specific constructor, and is called every time a(ny) constructor is called.



回答9:

Complete Explanation

The order of execution is like,

  1. static block
  2. instance block
  3. constructor

Explanation

Static block will always be called only once in the very beginning whenever the class is accessed by any means, in your case which is when you run the program. (That is what static block is meant for). It does not depend on instances therefore not called again when new instances are created.

Then the Instance initialization block will be called for each instance created and after that the constructor for each instance created. Because both of them can be used to instantiate the instance.

Is instance initialization block actually called before constructor?

After compilation the code will become,

public class Test {

  Test(){
    super();
    System.out.println("2");
    System.out.println("1");
  }


  static {
    System.out.println("3");
  }

  public static void main(String args[]) {
    new Test();
  }
}

So you can see, the statement written in instance block itself becomes part of the constructor. Therefore it is executed before the statements already written in the constructor.

From this documentation

The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.



标签: java braces