Java之路:運算子
運算子
程式由許多語句組成,而語句組成的基本單位就是表示式與運算子。
【運算子分類】
按功能分:算術運算子、賦值運算子、關係運算符、邏輯運算子、位運算子、其他運算子。
按運算元數目:一元、二元、三元。
1、賦值運算子:=
它的意思是“取右值,把它複製給左值”。右值可以是任何常數、變數或表示式(只要它能生成一個值就行)。但左值必須是一個明確的、已命名的變數,即必須有一個物理空間可以儲存等號右邊的值。如下:
int a;
a = 4; // 可以將常量賦給變數
//以下是錯誤
4 = a; // 不能把任何東西賦給一個常數
注1:對基本資料型別的賦值是很簡單的,基本型別儲存了實際的值,而並非指向一個物件的引用,所以對其賦值的時候,是直接將一個地方的內容複製到了另一個地方。例如:a = b; 那麼b的內容就複製給了a。若是修改a,b並不受影響。
注2
class Num { int num; Num(int num){ this.num = num; } } public class Assignment { public static void main(String[] args) { Num a = new Num(1); Num b = new Num
(2); System.out.println("a.num = " + a.num + "\t" + "b.num = " + b.num); a = b; // a和b都指向原來b指向的物件 System.out.println("a.num = " + a.num + "\t" + "b.num = " + b.num); a.num = 3; // 因為此時a與b是指向同一物件,所以此時改變a所指向物件的值,b所指向的結果也改變 System.out.println("a.num = " + a.num + "\t" + "b.num = " + b.num); } }
【結果】
注:在許多程式語言中,我們可能會期望a,b是相互獨立的,但由於賦值運算子操作的是一個物件的引用,所以修改a的同時也改變了b!這是因為a和b包含的是相同的引用,它們指向相同的物件。(原本a包含的對物件的引用,是指向一個值為1的物件,但在對a賦值時,這個引用被覆蓋了,也即丟失了;而那個不再被引用的值為1的物件會由“垃圾回收器”自動清理。)
這種特殊的現象通常稱作“別名現象”,是Java操作物件的一種基本方式。而若要避免別名問題,則應該如下:
a.num = b.num;
這樣就可以保持兩個物件彼此獨立,而不是將a和b繫結到相同的物件。
2、算術運算子:+ - * / %
注
(1)整數除法會直接去掉結果的小數位,而不是四捨五入。因為整形變數無法儲存小數點後面的資料。
(2)Java中取餘運算子%的運算元可以是負數和浮點數。而在C/C++中,取餘運算子只能是整數。
(3)+運算子也可以連線兩個字串。
【示例】
public class Unary {
public static void main(String[] args) {
int x = 10/3; // 取值為3,而不是3.33333...
System.out.println("x = " + x);
int a = 10%-3;
System.out.println("a = " + a);
float b = 5.2f % 3.1f;
System.out.println("b = " + b);
}
}
【結果】
3、關係運算符:> < <= >= == !=
關係運算符生成的是一個boolean結果,它們計算的是運算元的值之間的關係。
等於(==)和不等於(!=)適用於所有的基本資料型別。而其他比較符不適用於布林型別,因為布林型別只有true和false,大小沒有意義。
測試物件的等價性【==、!=、equals()方法】
【示例】
public class Equal {
public static void main(String[] args) {
int m = 3;
int n = 3;
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(m == n);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
}
【結果】
【結果分析】
等於(==)和不等於(!=)也適用於所有的物件,但在使用時一定要注意,對於基本資料型別,它們比較的是值(基本資料型別中儲存的值)“本身”;對於引用資料型別,它們比較的變數對物件的引用(其所指向的物件在記憶體中的地址)。
(1)對於基本資料型別的變數,變數儲存的是值“本身”。而所有關係運算符比較的就是值“本身”,而
int m = 3;
int n = 3;
變數n和變數m都是直接儲存的"3"這個數值,所以用==比較的時候結果是true。
(2)對於引用型別變數,引用型別的變數儲存的並不是 “值”本身,而是於其關聯的物件在記憶體中的地址。如:
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
引用型別變數n1中儲存的是他所指向的物件在記憶體中的地址,而不是47這個值,同理,n2也是如此。
System.out.println(n1 == n2);
當執行上面語句時,實際上比較的是n1和n2的引用(其指向的物件在記憶體中的地址)。然而,n1和n2物件的引用卻是不同的,所以輸出false。
【equals()方法】
equals方法是基類Object中的方法,因此對於所有的繼承於Object的類都會有該方法。
equals()方法是用來比較兩個物件的引用是否相等,即是否指向同一個物件。
但equals()這個方法不適用於基本資料型別,基本型別直接使用==和!=即可。
public class Equal {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
【結果】
【結果分析】
不是說equals()比較的是引用嘛,那上面結果為什麼對呢?這是因為,equals()預設確實是比較引用,但是Integer類中重寫了equals()方法,使其比較引用物件的內容。
兩個引用物件的內容都是47,所以結果是true。
假設你建立了自己的類,沒有重寫equals()方法,如下:
public class Equal {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = 100;
v2.i = 100;
System.out.println(v1.equals(v2));
}
}
【結果】
【結果分析】
這是由於equals()的預設行為是比較引用,沒有重寫equals()方法,所以它無法比較引用物件的內容,只能預設比較物件的引用,引用不同,所以結果false。
4、邏輯運算子:與(&&)、或(||)、非(!)
與、或、非操作只能運用於布林值,並返回一個布林值。
(表示式1)&&(表示式2) // 其中表達式1和表示式2必須的結果必須是布林值
(表示式1)||(表示式2)
!(表示式)
注:使用與(&&)、或(||)時,一旦能夠明確無誤地確定整個表示式的值,就不用再計算表示式餘下的部分了,這種現象被稱為“短路”。因此,整個邏輯表示式靠後的部分有可能不會被運算。
非短路:&,|
& 與 | 兩邊的表示式都要進行運算。
【分享一道Java面試題】
- &和&&、|和||的關係是怎麼樣?(Java面試題)
答:
對於“與操作”:有一個條件不滿足,結果就是false。
普通與(&):所有的判斷條件都要執行;短路與(&&):如果前面有條件已經返回了false,不再向後判斷,那麼最終結果就是false。
對於“或操作”:有一個條件滿足,結果就是true。
對於普通或(|):所有的判斷條件都要執行;
短路或(||):如果前面有條件返回了true,不再向後判斷,那麼最終結果就是true。
5、位運算子
其中,除 ~ 運算子外,其他位運算子都可以與=聯合使用。&=、|=、^=、>>=、<<=、>>>=
運算子優先順序
【一個小問題】
int i;;
int n;;;;;;;;;;;;;
上面兩條語句合法嗎?
答案是合法,事實上,由於多個Java語句可以處於同一行,那麼int i ;;就可以解讀為“int i;”這條語句和另外一個空語句“;”共處於一行之上。int i語句後面即使跟10個分號也合法。