2010-02-02 107 views
15

對於這個Java代碼:爲什麼javac抱怨沒有初始化變量?

String var; 
clazz.doSomething(var); 

爲什麼編譯器報告此錯誤:

Variable 'var' might not have been initialized

我以爲所有的變量或引用被初始化爲null。爲什麼你需要做:

String var = null; 

?? ??

回答

23

實例和類變量初始化爲null(或0),但本地變量不是。

爲它說基本上是一回事了非常詳細的解釋,請參閱JLS§4.12.5

Every variable in a program must have a value before its value is used:

  • Each class variable, instance variable, or array component is initialized with a default value when it is created:
    • [snipped out list of all default values]
  • Each method parameter is initialized to the corresponding argument value provided by the invoker of the method.
  • Each constructor parameter is initialized to the corresponding argument value provided by a class instance creation expression or explicit constructor invocation.
  • An exception-handler parameter is initialized to the thrown object representing the exception.
  • A local variable must be explicitly given a value before it is used, by either initialization or assignment, in a way that can be verified by the compiler using the rules for definite assignment.
+1

爲什麼做出這個決定?爲什麼不直接將它們初始化爲類似變量的'null'? –

+0

也許爲了更好的代碼/可讀性? –

+0

有很多關於爲什麼局部變量沒有默認值的stackoverflow問題。例如http://stackoverflow.com/questions/415687/why-are-local-variables-not-initialized-in-java –

7

這是因爲Java正在非常有幫助的(儘可能)。

它將使用這種相同的邏輯來捕捉一些您可能錯過的非常有趣的邊緣案例。例如:

int x; 

if(cond2) 
    x=2; 
else if(cond3) 
    x=3; 

System.out.println("X was:"+x); 

這會失敗,因爲還有一個未指定的情況。事實是,在這裏應該絕對指定一個其他情況,即使它只是一個錯誤(switch語句中的default:condition也是如此)。

你應該從中取出什麼,有趣的是,永遠不要初始化你的局部變量,直到你發現你真的必須這樣做。如果你習慣於總是說「int x = 0;」你將會阻止這個夢幻般的「壞邏輯」探測器發揮作用。這個錯誤爲我節省了不止一次的時間。

4

同上比爾K.我補充一下:

Java編譯器可以保護你從失敗的一個函數中使用它之前設置一個變量傷害自己。因此它明確不會像Bill K所描述的那樣設置默認值。

但是,當談到類變量時,編譯器將很難爲您做到這一點。類變量可以由類中的任何函數設置。編譯器很難確定可能調用函數的所有可能的順序。至少它必須分析系統中調用此類中任何函數的所有類。它可能必須檢查任何數據文件或數據庫的內容,並以某種方式預測用戶將輸入什麼內容。最好的任務將是非常複雜的,最壞的情況是不可能的。所以對於類變量,提供可靠的默認值是有意義的。基本上,默認情況下,填充字段的零位,所以你得到null爲參考,零爲整數,爲布爾值,假等

比爾說,你絕對不應該習慣於自動當你聲明它們時初始化變量。只有在聲明時初始化變量,如果這在程序的上下文中確實有意義。就像,如果99%的時間你想X是42,但在一些IF條件內,你可能會發現這是一個特例,X應該是666,那麼很好,從「int x = 42;」開始。並在IF中覆蓋此。但在更正常的情況下,如果根據任何條件計算出值,則不要初始化爲任意數字。只需填寫計算值。然後,如果您在某些條件組合下發生邏輯錯誤並未能設置值,則編譯器會告訴您,您已經搞砸了,而不是用戶。

PS我已經看到了很多蹩腳的程序,說這樣的話的:

HashMap myMap=new HashMap(); 
myMap=getBunchOfData(); 

爲什麼要創建一個對象,當你知道你要及時稍後拋出這個目標走一毫秒來初始化變量?這只是浪費時間。

編輯

舉一個簡單的例子,假設你寫了這個:

int foo; 
if (bar<0) 
    foo=1; 
else if (bar>0) 
    foo=2; 
processSomething(foo); 

這將在編譯的時候拋出一個錯誤,因爲編譯器會注意到,當酒吧== 0,你從不設置foo,但是你嘗試使用它。

但是,如果你初始化foo的一個虛擬值,像

int foo=0; 
if (bar<0) 
    foo=1; 
else if (bar>0) 
    foo=2; 
processSomething(foo); 

編譯器會看到,無論什麼吧的價值,FOO被設置的東西,所以不會產生錯誤。如果你真正想要的是當bar爲0時foo爲0,那麼這很好。但如果真正發生的是您的測試之一是< =或> =或者您打算在bar == 0時包含最後一個else,那麼您已經欺騙編譯器無法檢測到您的錯誤。順便說一下,這就是我認爲這樣的構造是糟糕的編碼風格:編譯器不僅不能確定你的意圖,而且未來的維護程序員也不能。

+0

爲什麼不把它初始化爲空?我能想到的唯一真正有問題的情況是,當你應該聲明一個最終變量時,必須在try塊中設置一個值。但是你可以在這裏定義一個臨時變量,並在try塊之後賦值給最後一個變量。 – sibidiba

+0

@sibidiba:我回答的部分答案是?如果你的意思一般,那麼你將失去編譯時檢查的優勢。看看Bill K的回答。如果你的意思是我的myMap例子的結束評論,那麼簡單的解決方案是編寫「HashMap myMap = getBunchOfData()」,並跳過無意義的額外對象。 – Jay

1

我喜歡Bill K關於讓編譯器爲你工作的觀點 - 我已經開始初始化每個自動變量,因爲它看起來像是Java的事情。我沒有理解類變量(即構造函數擔心的持久性事物)和自動變量(某些計數器等)是不同的,儘管一切都是Java中的類。

等我回去和刪除我會使用初始化,例如

List <Thing> somethings = new List<Thing>(); 
somethings.add(somethingElse); // <--- this is completely unnecessary 

尼斯。我已經得到了一個編譯器警告

List<Thing> somethings = new List(); 

我想這個問題是缺乏初始化。錯誤。問題是我沒有理解規則,我需要在「新」中標識的<Thing>,而不是創建任何類型的實際項目<Thing>

(接下來,我需要學習如何把文字小於和大於號到HTML!)

+0

@Bill IV我清理了你的報價;您只需將代碼段指定爲代碼(編輯器上方的101/010圖標即可)。 –

+0

謝謝卡爾! –

0

我不知道它背後的邏輯,但局部變量不被初始化到null。我想讓你的生活變得輕鬆。如果可能的話,他們可以用類變量來完成它。這並不意味着你必須在開始時進行初始化。這是罰款:

MyClass cls; 
if (condition) { 
    cls = something; 
else 
    cls = something_else; 
0

當然,如果你真的得在彼此最上面兩行,你展示 - 聲明它,填滿它,無需使用默認的構造函數。但是,例如,如果您想要聲明一次並多次使用它,則默認的構造函數或空聲明是相關的。或者是指向一個輕量級對象的指針,以便在循環內反覆分配它,因爲指針的分配比對象的實例化要少得多? (據推測,在循環的每一步都有一個新對象的合理原因)。

Bill IV

+0

我不是說,不要創造價值並重新使用它。我在說,避免填充虛擬價值作爲佔位符,直到你找到真正的價值。如果你只是聲明瞭變量但是沒有填充變量,那麼稍後你會通過所有的IF和BUT來填充它,然後嘗試使用它,編譯器會告訴你是否有一些可能的路徑通過你未填充的地方。但是,如果你填入一個虛擬值,編譯器無法知道這是一個虛擬值而不是真實值,所以你失去了編譯時檢查的優勢。 – Jay

相關問題