[英]Behaviour Difference: 'null' initialized final static member, and 'null' initialized final local variable
[英]Inline-initialized final member variable is somehow null
我在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)
似乎不知何故 , conversationListenerLock
為null
。 怎么可能呢? 如何在不初始化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;
}
您永遠不應該在Java中的Integer上進行同步。
由於您已將Integer
設置為0,因此conversationListenerLock
必須引用其中一個緩存的Integer
,因為它在-128到+127的范圍內。 試圖獲得其顯示器是一個特別糟糕的主意。 該對象不屬於您,僅屬於您的引用 。
(我建議您的Java平台正在顯示錯誤的診斷,以嘗試處理您提供的問題,我認為這相當於您的JVM中的錯誤。)
因此,根據經驗, 不要嘗試在Java中對Integer
進行synchronize
:JVM保留對任何 Integer
值進行緩存的權限; 不僅僅是我已經陳述過的最小范圍。
最簡單的解決方法是編寫
private final Object conversationListenerLock = new java.lang.Object();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.