1. 程式人生 > >Java中a=a+b 與 a+=b區別 以及和型別轉換的關係

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檔案,會發現原始碼被解析成這樣子:

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 修飾的類成員來說,由於其生命週期在物件銷燬之前,所以它佔用的應該是堆記憶體。(這個不確定 --)