Strange behavior using braces in Java

2020-05-25 04:21发布

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.

标签: java braces
9条回答
乱世女痞
2楼-- · 2020-05-25 04:36

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.

查看更多
萌系小妹纸
3楼-- · 2020-05-25 04:39

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

查看更多
ら.Afraid
4楼-- · 2020-05-25 04:40

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.

查看更多
成全新的幸福
5楼-- · 2020-05-25 04:41

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.

查看更多
等我变得足够好
6楼-- · 2020-05-25 04:44

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楼-- · 2020-05-25 04:50

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.

查看更多
登录 后发表回答