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

时间:2017-01-06 07:41:41

标签: java-7

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都是在构造函数[1]之后启动的,而最后的实例必须在构造函数[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。

1 个答案:

答案 0 :(得分:1)

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

第1-3项指定调用super()和this()

的显式或隐式变体

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

第5项说执行rest of the body of this constructor,其中rest在您的案例中以try { prop = ...开头。 Rest表示“没有super()或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");