最终实例是在构造函数之前启动和分配的吗?

is final instance initiated and assigned before constructor?

public static Properties prop;

private static UnionAuthProperties unionAuthProperties;

private UnionAuthProperties() {
    try {
        prop = PropertiesUtil.getPropertiesFromClassPath("unionauth.properties");
    } catch (Exception e) {
        LOG.error("{}",e);
    }
}

public static synchronized UnionAuthProperties getInstance() {
    if (unionAuthProperties == null) {
        unionAuthProperties = new UnionAuthProperties();  #27
    }
    return unionAuthProperties;
}

private final String URL_REQUEST = prop.getProperty("url.request");   #32

最后一条语句 URL_REQUEST 导致:

threw exception
java.lang.NullPointerException
    at UnionAuthProperties.<init>(UnionAuthProperties.java:32)
    at UnionAuthProperties.getInstance(UnionAuthProperties.java:27)
    at UnionAuthClient.<init>(UnionAuthClient.java:9)

据我所知,无论是final还是非final的实例都是在constructor [1] while the final ones have to be assigned before the end of constructor [2]之后启动的。那么为什么初始化URL_REQUEST时prop为NULL呢?

编辑: 如果在 super() 和 this(...) 完成之后,实例被初始化,那么最终实例 REDIRECT_URI 应该被初始化为 null或空白。但是,这会将 REDIRECT_URI 打印为 REDIRECT_URI:

public class Test {
        private static Properties prop;

        public static void main(String[] args) {
            Test a = new Test();
        }

        public Test() {
            // The following code should run after private final String REDIRECT_URI;
            try {
                System.out.println();
            } catch (Exception e) {}
            REDIRECT_URI = "REDIRECT_URI";
            System.out.println(REDIRECT_URI);
        }

        private final String REDIRECT_URI;
    }

而且我也试过像这样更改构造函数:

private UnionAuthProperties() {
     prop = new Properties();
}

仍然是 NPE。

http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5

第 1-3 项指定调用 super() 和 this() 的显式或隐式变体

第 4 项从上到下说 execute instance variable initializers。 这意味着所有不在构造函数内的最终和非最终字段,现在只初始化新实例的字段。

项目 5 说执行 rest of the body of this constructor,其中 rest 在您的案例中以 try { prop = ... 开头。 Rest 表示 "without super() or this()"

因此 prop.getProperty("url.request")prop 在构造函数主体的其余部分初始化之前执行。

我会将 prop 初始化移动到 getInstance() 函数。

这是您的代码执行的顺序(初始化和构造函数代码)

// 1. when class is loaded (because it is `static`) effectively same as public static Properties prop = null;
public static Properties prop; 

// 2. when class is loaded; effectively same as unionAuthProperties = null
private static UnionAuthProperties unionAuthProperties;

private UnionAuthProperties() {
    try {
        // 5. executing constructor code
        prop =     PropertiesUtil.getPropertiesFromClassPath("unionauth.properties");
    } catch (Exception e) {
        LOG.error("{}",e);
    }
}

public static synchronized UnionAuthProperties getInstance() {
    if (unionAuthProperties == null) {
        // 3. creating new instance (first only constructor called, assignment is later)
        unionAuthProperties = new UnionAuthProperties();  #27
    }
    return unionAuthProperties;
}

// 4. `execute instance variable initializers` in top to bottom order
// prop is null yet
private final String URL_REQUEST = prop.getProperty("url.request");