[英]Behaviour Difference: 'null' initialized final static member, and 'null' initialized final local variable
在后續代碼中,我遇到了一個我之前不知道的行為。
考慮第一種情況:
public static void main(String[] args) {
final String str = null;
System.out.println(str.length()); // Compiler Warning: NullPointerAccess
}
正如預期的那樣,編譯器在str
為null時顯示以下警告 - 空指針訪問:變量str在此位置只能為null。
現在,當我將該變量移動一個初始化為null的靜態final字段時 :
class Demo {
static final String str = null;
public static void main(String[] args) {
System.out.println(str.length()); // No Compiler Warning
}
}
現在,編譯器沒有顯示任何警告。 AFAIK,編譯器應該知道str
是最終的,不會在代碼的任何一點改變它的值。 並且假設它為null
,肯定會在以后導致NullPointerException
,它會這樣做。
雖然編譯器在第一種情況下成功警告我,為什么它在第二種情況下無法識別。 為什么這種行為改變? 如果我將static
字段更改為instance
字段,並使用Demo
實例訪問它,則行為相同。
我認為這種行為可能已在JLS中指定,因此我瀏覽了主題定義分配 ,但沒有找到與此問題相關的任何內容。 任何人都可以解釋行為的變化嗎? 如果可能的話,我正在尋找一些與JLS有關聯的強點嗎?
除此之外,為什么編譯器首先只向我顯示警告 ,因為我認為出於與上述相同的原因,方法調用肯定會在運行時拋出NPE ,因為字段無法更改? 為什么不向我顯示編譯器錯誤? 我是否期望編譯器太多,因為似乎很明顯, str.length()
的運行時結果不能比NPE
?
很抱歉錯過了之前:
我正在使用Eclipse Juno ,在Ubuntu 12.04上使用OpenJDK 7 。
我不確定在100%,但在第二種情況下,你有最終字段對抗局部變量,有可能為這個最終字段分配一些直接在靜態的值(或實例塊取決於變量是否為靜態)初始化塊:
class Demo {
...
static {
str = "some text";
}
...
}
所以編譯器不會給你警告。
哇! 原來,這是日食的具體問題。 當我使用以下代碼編譯代碼時:
javac -Xlint:all Demo.java
它沒有對任何案件發出任何警告。 所以,我回到eclipse檢查為這種情況啟用的任何設置,並找到了一個。
在Windows - > 首選項 - > Java - > 編譯器 - > 錯誤/警告中 ,在Null Analysis下 ,我可以更改Eclipse編譯器應該處理Null指針訪問的方式。 我可以將其設置為 - 忽略 , 錯誤或警告 。
現在看來這是一個完全愚蠢的問題。 對我感到羞恥。 :(
public static void main(String[] args) {
final String str = null;
System.out.println(str.length()); // Compiler Warning: NullPointerAccess
}
在這種情況下,str是一個局部變量,如果在初始化之前嘗試對其執行任何操作,編譯器將無法編譯。 流分析完全不同,它將檢查代碼的流程,在代碼流中,它檢測到在執行length()操作時,局部變量str只能為null。
class Demo {
static final String str = null;
public static void main(String[] args) {
System.out.println(str.length()); // No Compiler Warning
}
}
在這種情況下,str是一個實例變量,即使您沒有明確指定,它也將為null。
為什么還沒有警告?
您可以在構造函數中初始化實例變量。 或者你可以在調用lenght()操作之前調用setter方法。 因此它逃脫了流分析(編譯器不確定實例變量在該點是否為空,但在第一種情況下編譯確保局部變量將始終為null)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.