内联初始化的最终成员变量以某种方式为null

时间:2017-06-29 10:49:12

标签: java android member final

我在Conversation类中有以下代码:

private ConversationListener conversationListener = null;
private final Integer conversationListenerLock = 0;

public ConversationListener getConversationListener() {
    synchronized (conversationListenerLock) {
        return conversationListener;
    }
}

我现在有时会遇到以下异常:

java.lang.NullPointerException: Null reference used for synchronization (monitor-enter)
    at com.mobileoct.shared.data.Conversation.getConversationListener(Conversation.java:283)
    at com.mobileoct.android.colposcope.main.ConversationManager.loadConversationsForUser(ConversationManager.java:260)
    at com.mobileoct.android.colposcope.main.ConversationManager$1.run(ConversationManager.java:305)
    at android.os.Handler.handleCallback(Handler.java:739)                                    
    at android.os.Handler.dispatchMessage(Handler.java:95)                                    
    at android.os.Looper.loop(Looper.java:148)                                                
    at android.os.HandlerThread.run(HandlerThread.java:61)                                    

似乎以某种方式conversationListenerLocknull。怎么可能呢?如何在不初始化conversationListenerLock值的情况下进入Conversation类的公共非静态方法? 我怎样才能修复它以免发生这种情况?

(从堆栈跟踪中可以清楚地看到,它在Android上运行,因此看起来是Java的一些不确定版本,非常接近Java 7.something。)

编辑:回复评论中的一些问题:

我在三星Galaxy J5(SM-J500H)上运行,运行Android 6.0.1。 (我还没有在其他设备上运行它,但这是主要的目标设备,所以这并不重要。)

此类的唯一构造函数是:

public Conversation(String sessionId, String name, String ownerId, String userId) {
    dataType = DATA_TYPE;
    this.setId(UUID.randomUUID().toString());
    this.setSessionId(sessionId);
    this.setName(name);
    this.setOwnerId(ownerId);
    this.setUserId(userId);
}

然而,这个类有时会被Gson实例化,如:

Conversation conversation = new GsonBuilder().create().fromJson(jsonString, Conversation.class);

new GsonBuilder().create()之间有更多内容,但我很确定它不相关。)

我不太了解不安全的出版物,知道这是否适合该法案,但它似乎可能是罪魁祸首。

编辑#2 - 这里是“从ctor直接和间接调用的所有代码,以及所有其他字段的初始值设定项”(从父类复制的一些代码):

public static final String DATA_TYPE = "conversation";
private static final Class<?> s_postServlet = AddConversation.class;

protected String id;
private String sessionId;
private String name;
private String ownerId; // the user that created the conversation
private Boolean deleted = false;
private Date lastUpdated = new Date();
private User user;
private User owner;
private Session session;
private SortedSet<Collaborator> collaborators = new TreeSet<>();
private SortedSet<Message> messages = new TreeSet<>();
private Set<Notification> notifications = new HashSet<>();
private ConversationListener conversationListener = null;
private final Object conversationListenerLock = new Object();

// From parent class
protected String dataType;
protected Date timestamp = new Date();
protected String userId;


public Conversation(String sessionId, String name, String ownerId, String userId) {
    dataType = DATA_TYPE;
    this.setId(UUID.randomUUID().toString());
    this.setSessionId(sessionId);
    this.setName(name);
    this.setOwnerId(ownerId);
    this.setUserId(userId);
}

public void setId(String id) {
    this.id = id;
}

public void setSessionId(String sessionId) {
    this.sessionId = sessionId;
}

public void setName(String name) {
    this.name = name;
}

public void setOwnerId(String ownerId) {
    this.ownerId = ownerId;
}

// From parent class
public void setUserId(String userId) {
    this.userId = userId;
}

1 个答案:

答案 0 :(得分:2)

您永远不应在Java中使用Integer进行同步。

由于您已将Integer设置为0,conversationListenerLock 必须引用其中一个缓存的Integer,因为它是&#39; s在-128到+127的范围内。试图获得其显示器是一个特别糟糕的主意。该对象不属于您,只有引用

(我建议您的Java平台正在显示错误的诊断,以尝试处理您提供的问题,我认为这相当于您的JVM中的错误。)< / p>

根据经验,不要尝试synchronize在Java中的Integer:JVM保留缓存任何内容的权利 Integer值;不仅仅是我已经陈述过的最小范围。

最简单的解决方法是编写

private final Object conversationListenerLock = new java.lang.Object();