寫在前面:Hubert建議我看看《think in Java》並寫博客記錄一下筆記,於是有了此文,之後會持續更新。文中許多內容是直接引用書中的,因爲大家都知道,本文介紹的是Java基礎,就是這麼多這麼繁瑣的東西。希望大家耐心看。
1.Java運算符
1.1 優先級
運算符的優先級決定了一個存在多個運算符的表達式各部分的執行順序。
1.2 賦值
賦值是用等號運算符(=)進行的,它的意思是“取得右邊的值,把它複製到左邊”。右邊可以是任意常量、變量、表達式,只要能產生值就可以了,但是左邊必須是一個明確的、已命名的變量。舉個例子,可以將一個變量賦值給一個常量(A = 4),但是不能將任何東西賦值給常量(4 = A)。
對於主數據類型的賦值是非常直接的。由於主類型容納了實際的值,並不是指向一個對象的句柄,所以在賦值的時候,可以將一個地方的內容直接複製到另一個地方。例如,假設主類型使用“A = B”,那麼B處的內容就複製到了A。接着修改A的值,B不會受影響。
但是對“對象”賦值卻發生了變化。對一個對象進行操作時,實際操作的是它的句柄。例如對對象使用“C = D”,這時候C和D都指向了原來D指向的那個對象。
class Letter{
char c;
}
public class PassObject{
static void f(Letter y){
y.c = ‘z’;
}
public static void main(String[] args){
Letter x = new Letter();
x.c = ‘a’;
System.out.println(“1:x.c:” + x.c);
f(x);
System.out.println(“2:x.c:” + x.c);
}
}
f()表面上是要在方法的作用域內製作一個自變量y的副本,但實際上傳遞的是一個句柄,所以y.c = 'z'實際改變的是f()之外的對象。輸出結果如下:
1:x.c:a
2:x.c:z
1.3 算數運算符
Java的基本算數運算符和其他編程語言是相同的。其中包括加號(+)、減號(-)、乘號(*)、除號(/)以及模運算符(%,從整數除法運算中獲得餘數)。整數除法會直接砍掉餘數,而不是四捨五入。
把運算符和等號連在一起用,可以在進行運算的同時進行賦值操作。例如把變量x+4並將結果賦值給x,可用x += 4。
1.4 自動遞增和遞減運算符
遞增運算符“++”意思是“增加一個單位”,遞減運算符“--”意思是“減少一個單位”。例如,A是一個int值,則表達式++A,表示A = A + 1。遞增和遞減生成的是變量的值。
對於遞增和遞減有兩個版本——“前綴版”和“後綴版”。對於前綴版,會先執行運算,再生成值;對於後綴版,會先生成值,再執行運算。下面是一個例子:
public class AutoInc {
public static void main(String[] args) {
int i = 1;
prt("i : " + i);
prt("++i : " + ++i); // Pre-increment
prt("i++ : " + i++); // Post-increment
prt("i : " + i);
prt("--i : " + --i); // Pre-decrement
prt("i-- : " + i--); // Post-decrement
prt("i : " + i);
}
static void prt(String s) {
System.out.println(s);
}
}
則程序輸出如下:
i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1
1.5 關係運算符
關係運算符會生成一個Boolean結果,它評價的是運算對象之間的關係。如果關係是真的,表達式會生成true,反之表達式會生成false。關係運算符有大於(>)、大於或等於(>=)、小於(<)、小於或等於(<=)、等於(==)以及不等於(!=)。其中==和!=適用於所有內建數據類型,其他的不適用於boolean類型。
1.5.1 檢查對象是否相等
public class Equivalence {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
}
對於結果,初學者一般會認爲先打印true再打印false,因爲兩個Integer對象是相同的,但是他們錯了。儘管兩個對象內容是相同的,但是句柄是不同的,而==和!=比較的恰恰是句柄。所以輸出結果,先是false,再是true。
對於對象內容進行比較需要用到所有對象都適用的方法equals()方法,這個不適用於主類型,主類型的比較直接使用==或!=。例如:
public class EqualsMethod {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
輸出結果,正如我們所料輸出true。但是事情並未結束,如果是我們自己創建的類,結果會如何呢?例如:class Value {
int i;
}
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
System.out.println(v1.equals(v2));
}
}
輸出結果又變成了false。這是因爲equals()默認比較的是句柄,除非在自己的類中改變了equals()的行爲,之後的文章會介紹如果改變方法的行爲。因爲大多數Java類庫都實現了equals(),使其作用比較兩個對象的內容,而不是句柄。1.6 邏輯運算符
1.6.1 短路
public class ShortCircuit {
static boolean test1(int val) {
System.out.println("test1(" + val + ")");
System.out.println("result: " + (val < 1));
return val < 1;
}
static boolean test2(int val) {
System.out.println("test2(" + val + ")");
System.out.println("result: " + (val < 2));
return val < 2;
}
static boolean test3(int val) {
System.out.println("test3(" + val + ")");
System.out.println("result: " + (val < 3));
return val < 3;
}
public static void main(String[] args) {
if(test1(0) && test2(2) && test3(2))
System.out.println("expression is true");
else
System.out.println("expression is false");
}
}
每一次測試都會返回自變量,並比較真假。if(test1(0)) && test2(2) && test3(2)),新手一般會認爲三個測試都會執行,但是不是滴。第一個測試返回true,所以程序繼續執行。第二個測試返回false,意味着整個表達式肯定爲false,所以表達式就沒有繼續執行的必要了,剩下部分將不會執行,潛在的提高了性能。1.7 按位運算符
按位運算來源於C語言的低級操作。我們經常都要直接操縱硬件,需要頻繁設置硬件寄存器內的二進制位。Java的設計初衷是嵌入電視頂置盒內,所以這種低級操作仍被保留下來了。然而,由於操作系統的進步,現在也許不必過於頻繁地進行按位運算。
若兩個輸入位都是1,則按位AND運算符(&)在輸出位裏生成一個1;否則生成0。若兩個輸入位裏至少有一個是1,則按位OR運算符(|)在輸出位裏生成一個1;只有在兩個輸入位都是0的情況下,它纔會生成一個0。若兩個輸入位的某一個是1,但不全都是1,那麼按位XOR(^,異或)在輸出位裏生成一個1。按位NOT(~,也叫作“非”運算符)屬於一元運算符;它只對一個自變量進行操作(其他所有運算符都是二元運算符)。按位NOT生成與輸入位的相反的值——若輸入0,則輸出1;輸入1,則輸出0。
按位運算符和邏輯運算符都使用了同樣的字符,只是數量不同。因此,我們能方便地記憶各自的含義:由於“位”是非常“小”的,所以按位運算符僅使用了一個字符。
按位運算符可與等號(=)聯合使用,以便合併運算及賦值:&=,|=和^=都是合法的(由於~是一元運算符,所以不可與=聯合使用)。
我們將boolean(布爾)類型當作一種“單位”或“單比特”值對待,所以它多少有些獨特的地方。我們可執行按位AND,OR和XOR,但不能執行按位NOT(大概是爲了避免與邏輯NOT混淆)。對於布爾值,按位運算符具有與邏輯運算符相同的效果,只是它們不會中途“短路”。此外,針對布爾值進行的按位運算爲我們新增了一個XOR邏輯運算符,它並未包括在“邏輯”運算符的列表中。在移位表達式中,我們被禁止使用布爾運算,原因將在下面解釋。
1.8 移位運算符
若對char,byte或者short進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。只有右側的5個低位纔會用到。這樣可防止我們在一個int數裏移動不切實際的位數。若對一個long值進行處理,最後得到的結果也是long。此時只會用到右側的6個低位,防止移動超過long值裏現成的位數。但在進行“無符號”右移位時,也可能遇到一個問題。若對byte或short值進行右移位運算,得到的可能不是正確的結果(Java 1.0和Java 1.1特別突出)。它們會自動轉換成int類型,並進行右移位。但“零擴展”不會發生,所以在那些情況下會得到-1的結果。可用下面這個例子檢測自己的實現方案:
public class URShift {
public static void main(String[] args) {
int i = -1;
i >>>= 10;
<p> System.out.println(i);</p> long l = -1;
l >>>= 10;
System.out.println(l);
short s = -1;
s >>>= 10;
System.out.println(s);
byte b = -1;
b >>>= 10;
System.out.println(b);
}
}
1.9 三元運算符
1.10 逗號運算符
1.11 字符串運算符+
int x=0,y=1,z=2;
String str = "string";
System.out.println(str + x + y + z);
Java編譯程序會將x、y、z分別轉成對應的字符串形式'x'、"y"、"z",而不是將它們加在一起。如果使用System.out.println(x + str),早期的Java版本會出錯,之後版本會將x轉成"x",所以爲了避免出錯,請保證表達式的第一個對象爲string類型。1.12 運算符的常規操作
while(x = y) {
//...
}
程序的意圖是測試是否“相等”(==),而不是進行賦值操作。在C和C++中,若y是一個非零值,那麼這種賦值的結果肯定是true。這樣使可能得到一個無限循環。在Java裏,這個表達式的結果並不是布爾值,而編譯器期望的是一個布爾值,而且不會從一個int數值中轉換得來。所以在編譯時,系統就會提示出現錯誤,有效地阻止我們進一步運行程序。所以這個缺點在Java裏永遠不會造成更嚴重的後果。唯一不會得到編譯錯誤的時候是x和y都爲布爾值。在這種情況下,x = y屬於合法表達式。而在上述情況下,則可能是一個錯誤。
2. 執行控制
2.1 真和假
2.2 if-else
2.3 反覆
public class WhileTest {
public static void main(String[] args) {
double r = 0;
while(r < 0.99d) {
r = Math.random();
System.out.println(r);
}
}
}
它用到了Math庫裏的static(靜態)方法random()。該方法的作用是產生0和1之間(包括0,但不包括1)的一個double值。while的條件表達式意思是說:“一直循環下去,直到數字等於或大於0.99”。由於它的隨機性,每運行一次這個程序,都會獲得大小不同的數字列表。2.4 do...while
2.5 for
2.5.1 逗號運算符
public class CommaOperator {
public static void main(String[] args) {
for(int i = 1, j = i + 10; i < 5;
i++, j = i * 2) {
System.out.println("i= " + i + " j= " + j);
}
}
}
結果如下:i= 1 j= 11
i= 2 j= 4
i= 3 j= 6
i= 4 j= 8
大家可以看到,無論在初始化還是在步進部分,語句都是順序執行的。此外,儘管初始化部分可設置任意數量的定義,但都屬於同一類型。2.6 中斷和繼續
下面這個程序向大家展示了break和continue在for和while循環中的例子:
public class BreakAndContinue {
public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
if(i == 74) break; // Out of for loop
if(i % 9 != 0) continue; // Next iteration
System.out.println(i);
}
int i = 0;
// An "infinite loop":
while(true) {
i++;
int j = i * 27;
if(j == 1269) break; // Out of loop
if(i % 10 != 0) continue; // Top of loop
System.out.println(i);
}
}
}
在這個for循環中,i的值永遠不會到達100。因爲一旦i到達74,break語句就會中斷循環。通常,只有在不知道中斷條件何時滿足時,才需象這樣使用break。只要i不能被9整除,continue語句會使程序流程返回循環的最開頭執行(所以使i值遞增)。如果能夠整除,則將值顯示出來。
第二部分向大家揭示了一個“無限循環”的情況。然而,循環內部有一個break語句,可中止循環。除此以外,大家還會看到continue移回循環頂部,同時不完成剩餘的內容(所以只有在i值能被9整除時纔打印出值)。輸出結果如下:
0
9
18
27
36
45
54
63
72
10
20
30
40
之所以顯示0,是由於0%9等於0。無限循環的第二種形式是for(;;)。編譯器將while(true)與for(;;)看作同一回事。所以具體選用哪個取決於自己的編程習慣。