这是问题的扩展:Order of the initialization in Java
所以在代码块中:
public class Point {
int y = getX();
int x = 42;
int getX() {
return x;
}
public static void main (String s[]) {
Point p = new Point();
System.out.println(p.x + "," + p.y);
}
}
输出42,0
虽然通过描述Java编译器和运行时的行为来回答上述问题,但仍然困扰我为什么编译器不将x(42)的初始值烘焙到字节码中? 我知道静态变量的值嵌入到字节码中,因为它们是类级变量,并且它们不占用对象内存中的任何空间,但是为类级非静态变量嵌入任何初始值也没有意义也进入字节码?这样,上面的代码将更符合预期的行为,并且我猜测对象的实例化会更快(因为分配给x的内存将立即包含42因此节省了解析类中初始化行的时间每次创建Point类的对象时)
我怀疑这可能与类字节码大小,对象初始化效率和编译时效率之间的权衡有关。
我希望对Java编译器/运行时有深入了解的人可以对此有所了解。 了解框架如何在内部工作总能帮助我们编写更好的代码: - )
答案 0 :(得分:6)
这不是效率问题;这是一个具有明智语义的问题。
Java设计人员希望初始化的行为是相同的,无论 坦率地说,我敢打赌编译器会将赋值x
被定义为42
还是getFortyTwo()
,因为如果该行为不同然后,这导致各种各样的方式无意中射击自己的脚。所以他们在JLS中指定了哪个顺序字段初始化发生,并且该顺序独立是x
是常量还是方法调用还是马铃薯。 (作为参考,它是您声明字段的顺序 - 因此,如果您颠倒了班级中x
和y
的顺序,y
将设置为42。)< / p>
y = 0
带入构造函数中,因为JLS的语义需要 y = 0
基于此代码。
答案 1 :(得分:3)
虽然通过描述Java编译器和运行时的行为来回答上述问题,但仍然困扰我为什么编译器不将x(42)的初始值烘焙到字节码中?
想象一下,如果你后来改变了这个:
int x = 42;
到此:
int x = getInitialValueForX();
你真的想要完全改变这种行为吗?
如果x
只是一个常量,那么将其设为static final
并将内联。实际上,如果变量不是最终且不是静态,那么我不明白为什么它应该被视为常量,即使初始值< em>发生目前是一个常数。
答案 2 :(得分:3)
我认为你误解了常数会发生什么,以及为什么。
编译时常量可以是内联的,但一般情况下,静态变量不是。编译时常量是defined in the specification,但是简单地说,它是在编译时已知的基本类型的值。有许多静态变量使用不是编译时常量的表达式进行初始化。
在这种情况下,Point
和getX()
不是最终的,因此getX()
的结果在编译时是未知的。当子类可以覆盖它并返回不同的值时,它怎么可能被内联?