你所不知道的,Java 中操作符的秘密?
在 Java 編程的過程中,我們對數據的處理,都是通過操作符來實現的。例如,用於賦值的賦值操作符、用於運算的運算操作符等、用於比較的比較操作符,還包括邏輯操作符、按位操作符、移位操作符、三元操作符等等。操作符的種類很多,用法也各不相同,下面讓我們一起來看看 Java 的各類操作符中,一些值得我們特別註意的知識點。
賦值操作符
對於賦值操作,相信大家都玩的特別熟,例如為基本數據類型賦值,
int love = 521;
在「對象漫談」中,我們知道了基本數據類型存儲在堆棧中,而不是 new
出來的。此外, 基本數據類型存儲了實際的數值,而不是指向對象的引用,所以在為其賦值的時候,是直接將一個地方的內容復制到了另一個地方 。而為一個對象進行賦值操作的時候,我們實際上是操作對象的引用,即將一個對象的引用賦值給另一個對象,因此兩個對象通過同一個引用指向同一塊存儲空間。感興趣的同學,可以運行如下程序進行測試,
package com.hit.chapter3;
/**
* author:Charies Gavin
* date:2017/12/09,12:40
* https:github.com/guobinhit
* description:測試賦值操作符
*/
public class AssignmentOperator {
public static void main(String[] args) {
// 創建兩個對象
Apple apple1 = new Apple("green");
Apple apple2 = new Apple();
// 輸出兩個初始化對象,apple1 初始化 color 為 green,apple2 初始化 color 為 null
System.out.println("Initial: apple1 color is " + apple1.color + ", apple2 color is " + apple2.color);
// 將 apple1 的引用賦值給 apple2
apple2 = apple1;(java學習群669823128)
// 輸出賦值後的兩個對象,兩個對象擁有同一個引用
System.out.println("Assignment: apple1 color is " + apple1.color + ", apple2 color is " + apple2.color);
// 修改 apple2 的引用
apple2.color = "red";
// 輸出 apple2 修改後的兩個對象,兩個對象都發生變化
System.out.println("Modify: apple1 color is " + apple1.color + ", apple2 color is " + apple2.color);
}
}
class Apple {
// 成員變量
String color;
/**
* 默認構造器
*/
Apple() {
}
/**
* 有參構造器
*
* @param color
*/
Apple(String color) {
this.color = color;
}
}
如上圖所示,當我們把對象 apple1
賦值給對象 apple2
的時候,兩個對象就擁有了同一個引用,因此在我們修改 apple2
的值之後, apple1
的值也受到了影響,這種現象,我們稱之為「同名現象」。如果想要避免上述的同名現象,我們可以修改賦值代碼為
apple2.color = apple1.color;
這樣的話,我們僅是將 apple1
的 color
值賦值給了 apple2
的 color
值,而不是操作對象的引用。
算術操作符
在 Java 的算術操作符中,整數的除法( /
)會直接省略掉結果的小數位,而不是四舍五入。
package com.hit.chapter3;
import java.util.Random;
/**
* author:Charies Gavin
* date:2017/12/09,13:50
* https:github.com/guobinhit
* description:測試算術操作符
*/
public class ArithmeticOperator {
public static void main(String[] args) {
/**
* 測試隨機整數除法
*/
randomDivide();
}
/**
* 隨機整數除法
*/
private static void randomDivide() {
Random random = new Random();
int x = random.nextInt(10) + 1;
int y = random.nextInt(10) + 1;
int z = x / y;
System.out.println("整數除法,默認省略結果的小數位:" + x + " / " + y + " = " + z);
}
}
如上所示,我們創建了一個隨機整數除法,並測試了整數除法會默認省略結果的小數位。在 randomDivide()
方法中,我們使用了 Random
類, 如果我們在創建 Random
對象的時候沒有傳入任何參數,那麽 Java 就會將當前時間作為隨機數生成器的種子 ,因此在每次執行上述程序的時候,都會得到不同的結果。其中,隨機數生成器的種子用於隨機數生成器的初始化值,對於相同的種子,總會產生相同的隨機數序列。此外,在我們調用 nextInt()
方法的時候,我們進行了+1
操作,這是因為 傳遞給 nextInt()
的參數設置了所能產生隨機數的上限,而其下限為 0
,下限可以取到,上限取不到 ,因此 +1
操作可以防止除數為 0
的情況。
在算術操作符中,一元減號( -
)和一元加號( +
)與二元減號和二元加號都使用相同的符號。根據表達式的書寫形式,編譯器會自動判斷出使用哪一種算術操作符。其中,一元減號用於轉變數據的符號,而一元加號只是為了一元減號相對於,它 ( +
)唯一的作用就是將較小類型的操作數提升為 int
類型 。
此外,在算術操作符中有兩個比較特殊的操作符,那就是遞增( ++
)和遞減( --
),遞增和遞減操作符不僅改變了變量,並且以變量的值作為其生產的結果。 對於前綴遞增和前綴遞減(如 i++
或者 i--
),會先執行運算,再生成值;對於後綴遞增和後綴遞減(如 ++i
或者 --i
),會先生成值,再執行運算 。
關系操作符
在 Java 語言中,關系操作符包括 >
、 <
、 >=
、 <=
、 ==
和 !=
等,其生成的結果為 boolean
類型,其中有兩個關系操作符需要我們特別註意,那就是 ==
和 !=
,執行下面的程序,進行測試:
package com.hit.chapter3;
/**
* author:Charies Gavin
* date:2017/12/10,14:43
* https:github.com/guobinhit
* description:測試關系操作符
*/
public class RelationOperator {
public static void main(String[] args) {
// 創建兩個 Integer 對象
Integer i1 = new Integer(521);
Integer i2 = new Integer(521);
// 調用兩個私有的靜態方法進行比較判斷
equivalentOperator(i1, i2);
equalsFunction(i1, i2);
}
/**
* 通過恒等運算符比較兩個對象
*
* @param o1
* @param o2
*/
private static void equivalentOperator(Object o1, Object o2) {
System.out.println(o1 + " == " + o2 + " : " + (o1 == o2));
System.out.println(o1 + " != " + o2 + " : " + (o1 != o2));
}
/**
* 通過 equals() 方法比較兩個對象
*
* @param o1
* @param o2
*/
private static void equalsFunction(Object o1, Object o2) {
System.out.println("(" + o1 + ").equals(" + o2 + ") : " + (o1).equals(o2));
System.out.println("!(" + o1 + ").equals(" + o2 + ") : " + (!(o1).equals(o2)));
}
}
如上所示,我們創建了兩個 Integer
類型的對象,通過關系操作符來比較,發現結果出人意料,兩個 Integer
類型的 521
竟然被判斷為 false
,也就是不相等。實際上,這是正常的,因為 ==
和 !=
比較的是對象的引用 ,我們通過 new
創建了兩個 Integer
類型的對象,雖然這兩個對象的內容相同,但它們在堆上擁有不同的存儲空間,也就擁有了不同的對象引用。通過對 equalsFunction
的測試,我們發現 調用 java.lang
包(默認導入)的 equals()
方法,可以正確的比較兩個對象的內容 。But,下面的程序可能就要讓我們對之前的判斷持懷疑態度了,
package com.hit.chapter3;
/**
* author:Charies Gavin
* date:2017/12/10,14:43
* https:github.com/guobinhit
* description:測試關系操作符
*/
public class RelationOperator {
public static void main(String[] args) {
// 創建兩個自定義的 Cartoon 對象
Cartoon c1 = new Cartoon();
Cartoon c2 = new Cartoon();
// 為兩個 Cartoon 對象賦值
c1.name = c2.name = "Naruto";
// 調用 equals() 方法進行比較
equalsFunction(c1, c2);
}
/**
* 通過 equals() 方法比較兩個對象
*
* @param o1
* @param o2
*/
private static void equalsFunction(Object o1, Object o2) {
System.out.println("(" + o1 + ").equals(" + o2 + ") : " + (o1).equals(o2));
System.out.println("!(" + o1 + ").equals(" + o2 + ") : " + (!(o1).equals(o2)));
}
}
/**
* 自定義卡通類
*/
class Cartoon {
String name;
}
如上所示,我們創建了兩個自定義類的對象,然後同樣是通過 equals()
方法對兩個含有相同內容的對象進行比較判斷,其結果竟然是 false
?不是說好 equals()
方法比較的是對象的內容嗎?怎麽轉眼就被打臉了呢?好吧,在這裏糾正一下, equals()
方法默認是比較對象的引用 ,不過在大多數的 Java 類庫中都實現了 equals()
方法,以便用來比較對象的內容,而非比較對象的引用。因此,如果我們想使用 equals()
方法來比較我們自定義類型的內容而非引用的話,則需要覆蓋 Object
類(終極根類)中的 equals()
方法,而在覆蓋 equals()
方法的同時,建議同時覆蓋 hashCode()
方法。下面給出一個同時覆蓋 equals()
和 hashCode()
的示例:
/**
* 自定義卡通類
*/
class Cartoon {
String name;
/**
* 覆蓋 Object 根類中的 hashCode() 方法
* @return 哈希值
*/
@Override
public int hashCode() {
return name.hashCode();
}
/**
* 覆蓋 Object 根類中的 equals() 方法
* @param o
* @return true or false
*/
@Override
public boolean equals(Object o) {
if (o instanceof Cartoon) {
if (this.name.hashCode() == ((Cartoon) o).name.hashCode())
return true;
return false;
} else {
return false;
}
}
}
在此,強烈建議: 不要用 ==
操作符來檢測兩個字符串是否相等 !因為 ==
操作符只能確定兩個字符串是否放在同一個位置上。當然,如果兩個字符串放置在同一個位置上,它們必然相等,但是完全有可能將內容相同的多個字符串的拷貝位置放置在不同的位置上。如果虛擬機始終將相同的字符串共享,就可以使用 ==
操作符來檢測兩個字符串是否相等。但實際上, 只有字符串常量是共享的 。
其他操作符
在邏輯操作符中,與( &&
)、或( ||
)、非( !
)操作只能作用於布爾值。如果在應該使用 String
值的地方使用了布爾值,那麽布爾值會自動轉換成適當的文本形式。對於布爾值,按位操作符和邏輯操作符具有相同的效果,只不過按位操作符不會中途“短路”而已。
在移位操作符中,如果 char
、 byte
或者 short
類型的數值進行移位操作,那麽在進行移位之前,它們會先被轉換為 int
類型,並且得到的結果也是 int
類型的值。對於二進制數,如果最高位(最前面)的數字(符號位)是 0
,則為正數;是 1
,則為負數。
在對基本數據類型執行算術運算或者按位運算的時候,只要類型比 int
小(即 char
、 byte
或者 short
),那麽在運算之前,這些值會自動被轉換成 int
類型。通常,表達式中出現的最大的數據類型決定了表達式結果的數據類型。
如果表達式以一個字符串開頭,那麽後續所有操作數都必須是字符串型,如果後續的操作數不是字符串型,則會被自動轉換為字符串型,並且編譯器會把雙引號內的字符序列自動轉成字符串。
在將 float
或者 double
轉型為整型值時,總是對該數字執行截尾操作。如果想要得到舍入的結果,則需要使用 java.lang.Math
中的 round()
方法。除 boolea
類外,任何一種基本數據類型都可以通過類型轉換變為其他基本類型。
此外,在使用指數計數法的時候,例如
float loveu = 5.21e2F;
編輯器 通常會將指數作為雙精度數( double
)來處理 ,如果在初始化值後面沒有 F
或者 f
的話,編譯器會報錯,提示我們必須使用類型轉換將 double
轉換為 float
類型。
java學習群669823128
你所不知道的,Java 中操作符的秘密?