Java 为什么在调用构造函数之前初始化实例变量?

Java 为什么在调用构造函数之前初始化实例变量?,java,constructor,Java,Constructor,我有以下代码: public abstract class UCMService{ private String service; protected DataMap dataMap = new DataMap(); protected class DataMap extends HashMap<String,String> { private static final long serialVersionUID = 4014

我有以下代码:

public abstract class UCMService{
    private String service;     

    protected DataMap dataMap = new DataMap(); 

    protected class DataMap extends HashMap<String,String> {

        private static final long serialVersionUID = 4014308857539190977L;

        public DataMap(){
            System.out.println("11111");
            put("IdcService",service);
        }
    }

    public UCMService(String service){
        System.out.println("2222");
        this.service = service;
    }
}
公共抽象类UCMService{
专用字符串服务;
受保护的DataMap DataMap=新DataMap();
受保护的类DataMap扩展了HashMap{
私有静态最终长serialVersionUID=4014308857539190977L;
公共数据映射(){
系统输出打印项次(“11111”);
put(“IdcService”,service);
}
}
公共UCMService(字符串服务){
系统输出打印号(“2222”);
服务=服务;
}
}
现在在控制台中,
DataMap
构造函数的
System.out.println
UCMService
的构造函数之前执行


我想知道为什么会这样。

简短回答:
因为说明书上这么说

长答案
构造函数无法使用内联初始化字段是非常奇怪的

你想能够写作吗

SomeService myService = new SomeService();
public MyConstructor() {
    someService.doSomething();
}

这是因为在编译时,编译器会将您在声明位置完成的每个初始化移动到类的每个构造函数。因此,
UCMService
类的构造函数被有效地编译为:

public UCMService(String service){
    super();  // First compiler adds a super() to chain to super class constructor
    dataMap = new DataMap();   // Compiler moves the initialization here (right after `super()`)
    System.out.println("2222");
    this.service = service;
}
因此,显然
DataMap()
构造函数是在
UCMService
类的
print
语句之前执行的。类似地,如果在
UCMService
类中还有更多构造函数,则初始化将移动到所有构造函数


让我们看看一个简单类的字节码:

class Demo {
    private String str = "rohit";

    Demo() {
        System.out.println("Hello");
    }
}
编译这个类,并执行命令-
javap-cdemo
。您将看到构造函数的以下字节码:

Demo();
    Code:
       0: aload_0
       1: invokespecial #1   // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #2   // String rohit
       7: putfield      #3   // Field str:Ljava/lang/String;
      10: getstatic     #4   // Field java/lang/System.out:Ljava/io/PrintStream;
      13: ldc           #5   // String Hello
      15: invokevirtual #6   // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      18: return
Demo();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:aload_0
5:ldc#2//String rohit
7:putfield#3//fieldstr:Ljava/lang/String;
10:getstatic#4//fieldjava/lang/System.out:Ljava/io/PrintStream;
13:ldc#5//String你好
15:invokevirtual#6//方法java/io/PrintStream.println:(Ljava/lang/String;)V
18:返回

您可以在第7行看到
putfield
指令,将field
str
初始化为
“rohit”
,它位于
print
语句之前(第
15行的指令)

等等,所以有些人告诉我不要在构造函数中工作。我认为这意味着,只在构造函数中分配变量。someService.doSomething()算工作吗?如果doSomething()确实做了什么,那么是-那就是工作。@否,如果
doSomething()
没有做什么,那么它应该被重命名为更具描述性的东西。@InspiredOne:构造函数应该做任何必要的事情来生成有效的对象实例。虽然有时让对象以一种模式启动是合理的,在这种模式下,人们唯一能做的就是调用一个初始化方法,但设计一个类型使其不存在“尚未使用”的模式通常[并非总是]更有帮助。话虽如此,使用字段初始值设定项来创建需要清理的内容是危险的,因为如果构造函数失败,就无法清理它们。真是精彩的解释。顺便说一句,我还没有尝试过,但每次遇到初始化顺序问题时,我可能想使用一些工具查看编译器生成的代码。