记一次类加载引起的Bug

1.起因

程序莫名其妙出了
error:java.lang.NullPointerException: Null reference used for synchronization (monitor-enter)
就是在使用同步锁的时候,是锁的对象是空的。很是奇怪,看代码没觉得啊。打印一下,确实是空的,Why?

2. 代码,看出问题?

代码逻辑简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SynTest {
public static void main(String[] args) {
System.out.println(LockInstance.string);
}
}

class LockInstance {
public static String string = getString();
private static byte[] sLock = new byte[0];
public static LockInstance getInstance() {
synchronized (sLock){
return new LockInstance();
}
}
private static String getString(){
return LockInstance.getInstance().getApplicationContext();
}

public String getApplicationContext() {
return "string";
}
}

没问题啊?

猜一下结果输出?出错拉!!

1
2
3
4
5
6
7
Exception in thread "main" java.lang.ExceptionInInitializerError
at SynTest.main(SynTest.java:3)
Caused by: java.lang.NullPointerException
at LockInstance.getInstance(SynTest.java:11)
at LockInstance.getString(SynTest.java:16)
at LockInstance.<clinit>(SynTest.java:8)
... 1 more

3 为什么?

本质上是没有理解类加载的过程导致出错的。来理一下思路
首先在第一次调用 LockInstane 的静态方法的时候,类会进行初始化。那么类的初始化是有一个过程的。按声明的静态变量进行初始化,再是类的静态块,最后再是执行调用的静态方法。按上面的代码,问题错在 类初始化的时候,由于静态的成员变量调用了静态方法进行初始化,但是在里面用的变量却还没来得及初始化。

4 解决办法

如果类的成员变量的初始化需要用到类的成员变量,请保证在这之前已经用到的变量已经初始化了。

修改方法,放到前面就可以了。

1
2
private static byte[] sLock = new byte[0];
public static String string = getString();

测试运行成功。