Java中a=a+b 與 a+=b區別 以及和型別轉換的關係
很久之前學習過 a=a+b 和a+=b的一些區別,進來再次回想起來,發現理解的還不透徹,所以又查資料找檔案重新學習了一番。
比較這兩種運算子的區別,可以有以下兩個方面的比較: 執行效率和型別轉換。
首先說一下執行效率問題
就單純的執行這兩條語句,不考慮編譯器的優化的話,a=a+b的執行效率是低於a+=b的,因為它多進行了一步中間變數的操作,而且會多佔用一個變數的空間。而Java編譯器預設對其進行了優化,優化之後兩條語句都當做 a+=b來執行了,所以實際上是沒有任何卻別的。
其次說一下有關型別轉換的區別。
相信大家都碰到過這種情況:
public class Test { public static void main(String[] args){ int a = 2; float b = 6; a+=b; //right // a=a+b; //error a=(int) (a+b); //right } }
當使用a=a+b的時候,會丟擲”Exception in thread "main" java.lang.Error: Unresolved compilation problem: Type mismatch: cannot convert from float to int“的異常,這是可以理解的,如果不使用(int)強制型別轉換的話,float 是不能直接復值給int 變數的。我們將a=a+b註釋掉,javac編譯完之後,再使用反編譯軟體(例如XJad)開啟Test.class檔案,會發現原始碼被解析成這樣子:
即a+=b進行了強制型別轉換,和 a=(int)((float)a+b)是等價的!public class Test { public Test() { } public static void main(String args[]) { int a = 2; float b = 6F; a = (int)((float)a + b); a = (int)((float)a + b); } }
到這裡我們就明白了為什麼a=a+b會丟擲異常了。
原因:在Java中,在基本型別進行算術運算的時候,會發生小位元組型別向大位元組型別轉換的現象。如圖中 int 型別和float型別進行加法運算時會將 a 先轉換為float型別,然後再和b相加。這樣結果型別變成了float型別,如果這時候試圖把float型別賦值給a時便會拋異常。
另外,對於short,byte,char 比int 位元組數小的變數型別來說,運算結果會自動轉換為int型別,如
byte a = 1 , b=2;
b=a+b;
byte a = 1; short b =2; b=a+b;
都會丟擲”Exception in thread "main" java.lang.Error: Unresolved compilation problem: Type mismatch: cannot convert from int to byte“類似的異常,可以看出a+b結果變成了int型別。
這是由於Java編譯器會在編譯期或者執行期將byte和short型別的資料帶符號擴充套件為相應的int型別資料,將boolean和char型別資料零位擴充套件為相應的int型別資料。因此,在處理boolean 、byte、short 和 char 型別的陣列是,也會用相應的int型別的位元組碼指令來處理。因此,大多數對於上述型別資料的操作,實際上都是使用相應的 int 型別作為運算型別。
如果是final 修飾的變數,進行運算的時候則不會出現型別轉換異常。
public class Test {
final int d = 3;
public static void main(String[] args){
final short a = 1;
final short b =2;
short c=a+b;
}
}
編譯後使用反編譯軟體開啟後,程式碼被解析成了這樣:
public class Test
{
final int d = 3;
public Test()
{
}
public static void main(String args[])
{
short a = 1;
short b = 2;
short c = 3;
}
}
可以看到,對於final 修飾的基本型別的變數來說,他們之間的運算直接就被硬編碼成了直接賦值語句,連中間結果都沒有了,型別轉換的異常也就沒了。
此外,我們可以注意到,對於方法內的final 變數 a , b 來說,編碼時被直接省略了。而Test 類的final 成員變數 d 依然保留著final 屬性。
所以說,是否使用final修飾方法中普通變數對JVM來說沒有區別!使用final修飾方法中普通變數主要是為了給Java前端編譯器(如javac)看的!也就是說方法中被final修飾的普通變數在前端編譯時被javac檢查並保證該變數不會在作用域內被改變新值,但被編譯成位元組碼後用於修飾方法中普通變數的final就已經不存在了!說的再具體點就是你用或不用final修飾方法中普通變數而生成的位元組碼檔案(.class檔案)沒有區別。
PS:對於final 修飾的類成員來說,由於其生命週期在物件銷燬之前,所以它佔用的應該是堆記憶體。(這個不確定 --)