Java 最终静态字段在第一次使用时为空

Java final static field is null at first usage

有一个 class 应该将日志存储在本地文件中,该文件被硬编码到程序中。

初始化逻辑如下:

import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.IOException;

public class Util {
    private static final Logger LOGGER = Logger.getLogger(Util.class.getName());
    private static final FileHandler filehandler = createFileHandler();

    private static FileHandler createFileHandler(){
        FileHandler handler;
        try {
            handler = new FileHandler("security.log", 0, 1, true);
            LOGGER.addHandler(filehandler);
        } catch (IOException | SecurityException e) {
            handler = null;
            LOGGER.log(Level.SEVERE, "Unable to create Security log!");
        }
        return handler;
    }
    
    public static String use_util(){
        LOGGER.log(Level.INFO, "Utils are used!");
        return "It's just I don't know.. a string, man.";
    }
    
    public static void main(String[] args){
        System.out.println(Util.use_util());
    }
}

然而,在第一次调用 use_util 时出现空指针异常,指出 LOGGER 为空:

$javac Util.java
$java -Xmx128M -Xms16M Util
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
    at java.util.logging.Logger.addHandler(Logger.java:1748)
    at Util.createFileHandler(Util.java:14)
    at Util.<clinit>(Util.java:8)

根据 Java 规范 section 12.4.2 :

Then, initialize the final class variables and fields of interfaces whose values are compile-time >constant expressions (§8.3.2.1, §9.3.1, §13.4.9, §15.28). ...

Next, execute either the class variable initializers and static initializers of the class, or the field >initializers of the interface, in textual order, as though they were a single block.

所以基于此,LOGGER 应该在 fileHandler 之前初始化。尽管如此, LOGGER 在初始化 fileHandler 时似乎为空...

这是为什么? 怎么强制初始化顺序?

Caused by: java.lang.NullPointerException

at java.util.logging.Logger.addHandler(Logger.java:1748)

从我的阅读方式来看,class 字段在这里不是问题。 LOGGER 不为空,执行了对 LOGGER.addHandler() 的调用,但在内部失败。

这是因为您将 filehandler 传递给调用,而不是您刚刚在之前的行中初始化的处理程序 handler。 由于此时您仍在 filehandler 的初始化执行中,因此它仍然是 null.