简体   繁体   English

在Java中访问未初始化的静态字段

[英]Accessing an uninitialised static field in Java

I'm indebted to this answer for the idea. 我很感激这个想法的答案

The following code compiles, but certainly shouldn't. 以下代码编译,但肯定不应该编译。 It uses the value of x before it's been initialised. 它在初始化之前使用x的值。 If you remove the StaticAssign. 如果删除StaticAssign. qualifier then it no longer compiles. 限定符然后它不再编译。

public class StaticAssign {

    public static final int x;

    static {
        System.out.println(StaticAssign.x);
        x = 5;
    }

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

}

On my machine, this consistently prints 0 . 在我的机器上,这始终打印0 If I change the type of x to String , it consistently prints null . 如果我将x的类型更改为String ,它将始终打印null

The question is: will it definitely print 0 or null , or might it access uninitialised memory and print out something else? 问题是:它肯定会打印0null ,还是可以访问未初始化的内存并打印出其他内容?

And even if this code gets through the compiler, why isn't it picked up by the JVM? 即使这段代码通过编译器,为什么它不被JVM选中?

Is there a way to do nasty things based on this? 有没有办法根据这个做出令人讨厌的事情?

It actually has been initialized. 它实际上已经初始化了。 Variables in the global scope are initialized automatically. 全局范围中的变量会自动初始化。 Variables of Object type will be initialized to null primitive like int will be initialized to 0. A variable declared not in the global scope must be initialized ie. 对象类型的变量将初始化为null原语,如int将初始化为0.必须初始化未在全局范围内声明的变量,即。 declared in a method. 在方法中声明。 Another problem is declaring it as final this is telling the compiler it must be explicitly initialized. 另一个问题是将其声明为final这告诉编译器必须显式初始化它。 So by adding the x=5 you are bypassing the compiler error saying it must be explicitly initialized. 因此,通过添加x=5您可以绕过编译器错误,说它必须显式初始化。 When you access it before this line at run-time it is initialized to 0 like any other primitive int type variable in the global scope. 在运行时在此行之前访问它时,它会像全局范围中的任何其他原始int类型变量一样初始化为0。

This is due to the way in which the classes are loaded. 这是由于加载类的方式。

First the class StaticAssign definition is loaded and the fields are initialized to default values: 首先加载类StaticAssign定义,并将字段初始化为默认值:

StaticAssign.x = 0;

Then the initalization block is executed. 然后执行初始化块。

System.out.println(StaticAssign.x);
x = 5;

There is a reference to StaticAssign.x , the current class. 有一个对当前类的StaticAssign.x的引用。 As recursive initialization attempts are simply ignored, value of x is still 0 . 由于简单地忽略了递归初始化尝试,因此x值仍为0

This means that: 这意味着:

System.out.println(StaticAssign.x)

is valid because StaticAssign.x is a reference to field of an allready loaded class. 是有效的,因为StaticAssign.x是一个媒体链接 加载的类的字段的引用。

But if yo do: 但如果你这样做:

System.out.println(x)   

then x is a reference to a final uninitalized field. 然后x是对最终未初始化字段的引用。

On the other hand, you never will access uninitialised memory. 另一方面,您永远不会访问未初始化的内存。 When the class definition is loaded variables are initialized to default values, before the initalization block is executed. 加载类定义时,在执行初始化块之前,将变量初始化为默认值。

EDIT: There is a nice video "Elvis Lives Again" from Java Puzzlers that shows this much more better than I can explain 编辑:Java Puzzlers有一个很好的视频“Elvis Lives Again” ,它比我能解释的要好得多

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM