Java 中使用大括号的奇怪行为
当我运行以下代码时:
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();
}
}
我希望按此顺序获得输出:
1
2
3
但我得到的是相反的顺序:
3
2
1
谁能解释为什么它以相反的顺序输出?
================
另外,当我创建多个 Test
实例时:
new Test();
new Test();
new Test();
new Test();
第一次仅执行静态块。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这完全取决于初始化语句的执行顺序。您的测试表明这个顺序是:
编辑
感谢您的评论,现在我可以引用 JVM 规范中的相应部分。 这里详细的初始化过程。
It all depends on the order of execution of initialization statements. Your test demonstrates that this order is:
Edit
Thanks for the comments, now I can quote the appropriate part in the JVM specification. Here it is, the detailed initialization procedure.
3 - 是一个静态初始化程序,它在类加载时运行一次,这首先发生。
2 - 是一个初始化块,java编译器实际上会将其复制到每个构造函数中,因此如果您愿意,您可以在构造函数之间共享一些初始化。很少使用。
1 - 将在构造对象时执行,在 (3) 和 (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
首先执行静态块。
然后是实例实例初始化块,
请参阅 JLS 了解实例初始化程序
{
// sop statements
}
实例初始化块中不能有 return 语句,就像构造函数一样。
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.
静态事物首先执行,
{System.out.println("2");}
不是函数的一部分,因为它的作用域首先调用它,而Test( ){System.out.println("1");}
最后调用,因为另外两个先调用static things are executed first,
{System.out.println("2");}
isn't a part of a function, because of its scope it is called first, andTest(){System.out.println("1");}
is called last because the other two are called first首先,类被加载到 JVM 中并进行类初始化。在此步骤中执行静态块。 “{...}”只是“static{...}”的语法等价物。由于代码中已经有一个“static{...}”块,因此“{...}”将被附加到它的后面。这就是为什么你在 2 之前打印了 3。
接下来,一旦类被加载,java.exe(我假设你从命令行执行)将找到并运行 main 方法。 main 静态方法初始化其构造函数被调用的实例,因此最后打印出“1”。
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.
由于
static{}
代码在 JVM 中首次初始化类时运行(即甚至在调用main()
之前),因此实例{}< /code> 在实例首次初始化、构造之前调用,然后在完成所有操作后调用构造函数。
Because the
static{}
code is run when the class is first initialized within the JVM (i.e. even beforemain()
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.我在这里通过 ASM 获得了类似字节码的代码。
我认为这可以回答您的问题,解释在这种情况下创建对象时发生的情况。
我们可以看到
LDC“3”
在“clinit”中,这是一个类初始值设定项。对象的生命周期通常是:加载类 -> 加载类 -> 加载类 -> 加载类 -> 加载类 ->链接类->类初始化->对象实例化->使用->气相色谱仪。这就是为什么 3 首先出现。由于这是在类级别,而不是对象级别,因此它将出现一次,因为类类型将被加载一次。详情请参考Java2虚拟机内部:类型的生命周期
< code>LDC "2" 和
`LDC "1"
位于构造函数“init”中。这样做的原因是:构造函数将首先执行一些隐式指令,例如超级构造函数和类的 {} 中的代码,然后执行其构造函数中显式的代码。
这就是编译器将对 java 文件执行的操作。
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.
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.
看起来没有人说明为什么 3 只明确打印一次。所以我想补充一点,这与为什么首先打印它有关。
静态定义的代码被标记为与类的任何特定实例分开。一般来说,静态定义的代码可以被认为根本不是任何类(当然,当考虑范围时,该语句存在一些无效性)。因此,如上所述,一旦加载了类,该代码就会运行,就像在构造实例
Test()
时不会调用它一样,从而调用多次构造函数不会导致静态代码再运行。正如上面开始的,包含 2 的括号内的代码被添加到构造函数的前面,因为它是类中所有构造函数的先决条件。您不知道 Test 的构造函数中会发生什么,但可以保证它们都以打印 2 开头。因此,这发生在任何特定构造函数中的任何内容之前,并且每次调用构造函数时都会调用。
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.
完整的解释
执行的顺序是这样的,
函数解释
静态块将始终在开始时仅被调用一次,无论何时以任何方式访问该类,在您的这是当你运行程序时的情况。 (这就是静态块的用途)。它不依赖于实例,因此在创建新实例时不会再次调用。
然后,将为创建的每个实例调用实例初始化块,然后为创建的每个实例调用构造函数。因为两者都可以用来实例化实例。
实例初始化块实际上是否在构造函数之前调用?
编译后代码将变成,
所以你可以看到,实例块中编写的语句本身就成为构造函数的一部分。因此它在构造函数中已编写的语句之前执行。
摘自本文档
Complete Explanation
The order of execution is like,
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,
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