很久之前學習過 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文件,會發現源代碼被解析成這樣子:
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+=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 修飾的類成員來說,由於其生命周期在對象銷毀之前,所以它占用的應該是堆內存。(這個不確定 --)
Java 9 的新特性發布 http://www.linuxidc.com/Linux/2014-08/105707.htm
Java編程思想(第4版) 中文清晰PDF完整版 http://www.linuxidc.com/Linux/2014-08/105403.htm
編寫高質量代碼 改善Java程序的151個建議 PDF高清完整版 http://www.linuxidc.com/Linux/2014-06/103388.htm
Java 8簡明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm
Java對象初始化順序的簡單驗證 http://www.linuxidc.com/Linux/2014-02/96220.htm
Java對象值傳遞和對象傳遞的總結 http://www.linuxidc.com/Linux/2012-12/76692.htm