控制程序流程
在Java 裡,我們利用運算符操縱對象和數據,並用執行控制語句作出選擇。Java 是建立在C++基礎上的,所以對C 和C++程序員來說,對Java 這方面的大多數語句和運算符都應是非常熟悉的。當然,Java 也進行了自己的一些改進與簡化工作。
加號(+)、減號和負號(-)、乘號(*)、除號(/)以及等號(=)的用法與其他所有編程語言都是類似的。
算術運算舉例如下:
class Number {
inti;
}
publicclass test {
publicstaticvoid main(String[] args) {
Number n1 =new Number();
Number n2 =new Number();
n1.i = 9;
n2.i = 47;
System.out.println("1: n1.i: " + n1.i +
",n2.i: " +n2.i);
n1 =n2;
System.out.println("2: n1.i: " + n1.i +
",n2.i: " +n2.i);
n1.i = 27;
System.out.println("3: n1.i: " + n1.i +
",n2.i: " +n2.i);
}
}
Number類非常簡單,它的兩個實例(n1 和n2)是在main()裡創建的。每個Number 中的i值都賦予了一個不同的值。隨後,將n2 賦給n1,而且n1 發生改變。
輸出如下:
1:n1.i: 9, n2.i: 47
2:n1.i: 47, n2.i: 47
3:n1.i: 27, n2.i: 27
再來看下方法調用中的別名處理:
class Letter {
charc;
}
publicclass test {
staticvoid f(Lettery) {
y.c ='z';
}
publicstaticvoid 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()方法表面上似乎要在方法的作用域內制作自己的自變量Letter y 的一個副本。
實際傳遞的是一個句柄。所以下面這個程序行:
y.c = 'z'; 實際改變的是f()之外的對象。輸出結果如下:
輸出如下:
1:x.c: a
2: x.c: z
Java 的基本算術運算符與其他大多數程序設計語言是相同的。其中包括加號(+)、減號(-)、除號(/)、乘號(*)以及模數(%,從整數除法中獲得余數)。整數除法會直接砍掉小數,而不是進位。 Java也用一種簡寫形式進行運算,並同時進行賦值操作。這是由等號前的一個運算符標記的,而且對於語言中的所有運算符都是固定的。例如,為了將4 加到變量x,並將結果賦給x,可用:x+=4。
示例如下:
import java.util.*;
publicclass test {
//Create a shorthand to save typing:
staticvoid prt(Strings) {
System.out.println(s);
}
//shorthand to print a string and anint:
staticvoid pInt(Strings,inti) {
prt(s +" = " +i);
}
//shorthand to print a string and a float:
staticvoid pFlt(Strings,floatf) {
prt(s +" = " +f);
}
publicstaticvoid main(String[] args) {
//Create a random number generator,
// seeds with current time by default:
Random rand =new Random();
inti,j,k;
// '%' limits maximum value to 99:
j =rand.nextInt() % 100;
k =rand.nextInt() % 100;
pInt("j",j);pInt("k",k);
i =j +k;pInt("j + k",i);
i =j -k;pInt("j - k",i);
i =k /j;pInt("k / j",i);
i =k *j;pInt("k * j",i);
i =k %j;pInt("k % j",i);
j %=k;pInt("j %= k",j);
// Floating-point number tests:
floatu,v,w;//applies to doubles, too
v =rand.nextFloat();
w =rand.nextFloat();
pFlt("v",v);pFlt("w",w);
u =v +w;pFlt("v + w",u);
u =v -w;pFlt("v - w",u);
u =v *w;pFlt("v * w",u);
u =v /w;pFlt("v / w",u);
// the following also works for
// char, byte, short,int, long,
// and double:
u +=v;pFlt("u += v",u);
u -=v;pFlt("u -= v",u);
u *=v;pFlt("u *= v",u);
u /=v;pFlt("u /= v",u);
}
} ///:~
prt()方法打印一個String;pInt()先打印一個String,再打印一個int;而pFlt()先打印一個 String,再打印一個float。當然,它們最終都要用System.out.println()結尾。
為生成數字,程序首先會創建一個 Random(隨機)對象。由於自變量是在創建過程中傳遞的,所以 Java 將當前時間作為一個“種子值”,由隨機數生成器利用。通過Random 對象,程序可生成許多不同類型的隨機數字。只需調用不同的方法即可:nextInt(),nextLong(),nextFloat()或者nextDouble()。
若隨同隨機數生成器的結果使用,模數運算符(%)可將結果限制到運算對象減1的上限(本例是99)之下。
輸出如下:
j= -58
k= 13
j+ k = -45
j- k = -71
k/ j = 0
k* j = -754
k% j = 13
j%= k = -6
v= 0.88164216
w= 0.17560738
v+ w = 1.0572495
v- w = 0.7060348
v* w = 0.15482287
v/ w = 5.02053
u+= v = 5.9021726
u-= v = 5.02053
u*= v = 4.426311
u /= v = 5.02053
和C 類似,Java 提供了豐富的快捷運算方式。這些快捷運算可使代碼更清爽,更易錄入,也更易讀者辨讀。
兩種很不錯的快捷運算方式是遞增和遞減運算符(常稱作“自動遞增”和“自動遞減”運算符)。其中,遞減運算符是“--”,意為“減少一個單位”;遞增運算符是“++”,意為“增加一個單位”。
對每種類型的運算符,都有兩個版本可供選用;通常將其稱為“前綴版”和“後綴版”。“前遞增”表示++運算符位於變量或表達式的前面;而“後遞增”表示++運算符位於變量或表達式的後面。類似地,“前遞減”意味著--運算符位於變量或表達式的前面;而“後遞減”意味著--運算符位於變量或表達式的後面。對於前遞增和前遞減(如++A或--A),會先執行運算,再生成值。而對於後遞增和後遞減(如A++或A--),會先生成值,再執行運算。下面是一個例子:
publicclass test {
publicstaticvoid main(String[] args) {
inti = 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);
}
staticvoid prt(Strings) {
System.out.println(s);
}
} ///:~
輸出如下:
i: 1
++i: 2
i++: 2
i: 3
--i: 2
i--: 2
i : 1
關系運算符生成的是一個“布爾”(Boolean)結果。它們評價的是運算對象值之間的關系。若關系是真實的,關系表達式會生成 true(真);若關系不真實,則生成false(假)。關系運算符包括小於(<)、大於(>)、小於或等於(<=)、大於或等於(>=)、等於(==)以及不等於(!=)。等於和不等於適用於所有內建的數據類型,但其他比較不適用於boolean類型。
示例如下:
publicclass test {
publicstaticvoid main(String[] args) {
Integer n1 =new Integer(47);
Integer n2 =new Integer(47);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
} ///:~
輸出如下:
false
true
達式System.out.println(n1== n2)可打印出內部的布爾比較結果。一般人都會認為輸出結果肯定先是true,再是 false,因為兩個Integer 對象都是相同的。但盡管對象的內容相同,句柄卻是不同的,而==和!=比較的正好就是對象句柄。所以輸出結果實際上先是false,再是 true。
若想對比兩個對象的實際內容是否相同,又該如何操作呢?此時,必須使用所有對象都適用的特殊方法equals()。但這個方法不適用於“主類型”,那些類型直接使用==和!=即可。
邏輯運算符 AND(&&)、OR(||)以及 NOT(!)能生成一個布爾值(true 或false)——以自變量的邏輯關系為基礎。下面這個例子向大家展示了如何使用關系和邏輯運算符。
示例如下:
import java.util.*;
publicclass test {
publicstaticvoid main(String[] args) {
Random rand =new Random();
inti =rand.nextInt() % 100;
intj =rand.nextInt() % 100;
prt("i = " +i);
prt("j = " +j);
prt("i > j is " + (i > j));
prt("i < j is " + (i < j));
prt("i >= j is " + (i >= j));
prt("i <= j is " + (i <= j));
prt("i == j is " + (i == j));
prt("i != j is " + (i != j));
//Treating anint as a boolean is
// notlegal Java
//! prt("i && jis " + (i && j));
//! prt("i || j is "+ (i || j));
//! prt("!i is " +!i);
prt("(i < 10) && (j < 10) is "
+ ((i < 10) && (j < 10)) );
prt("(i < 10) || (j < 10) is "
+ ((i < 10) || (j < 10)) );
}
staticvoid prt(Strings) {
System.out.println(s);
}
} ///:~
按位運算符允許我們操作一個整數主數據類型中的單個“比特”,即二進制位。按位運算符會對兩個自變量中對應的位執行布爾代數,並最終生成一個結果。
按位運算來源於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邏輯運算符,它並未包括在“邏輯”運算符的列表中。在移位表達式中,我們被禁止使用布爾運算。
移位運算符面向的運算對象也是二進制的“位”。可單獨用它們處理整數類型(主類型的一種)。左移位運算符(<<)能將運算符左邊的運算對象向左移動運算符右側指定的位數(在低位補 0)。“有符號”右移位運算符(>>)則將運算符左邊的運算對象向右移動運算符右側指定的位數。“有符號”右移位運算符使用了“符號擴展”:若值為正,則在高位插入0;若值為負,則在高位插入1。Java也添加了一種“無符號”右移位運算符(>>>),它使用了“零擴展”:無論正負,都在高位插入0。這一運算符是C或C++沒有的。
若對char,byte 或者short 進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。只有右側的5個低位才會用到。這樣可防止我們在一個 int數裡移動不切實際的位數。若對一個long 值進行處理,最後得到的結果也是long。此時只會用到右側的 6個低位,防止移動超過 long 值裡現成的位數。但在進行“無符號”右移位時,也可能遇到一個問題。若對byte 或short 值進行右移位運算,得到的可能不是正確的結果(Java 1.0 和Java 1.1 特別突出)。它們會自動轉換成int 類型,並進行右移位。但“零擴展”不會發生,所以在那些情況下會得到-1 的結果。
publicclass test {
publicstaticvoid main(String[] args) {
inti = -1;
i >>>= 10;
System.out.println(i);
longl = -1;
l >>>= 10;
System.out.println(l);
shorts = -1;
s >>>= 10;
System.out.println(s);
byteb = -1;
b >>>= 10;
System.out.println(b);
}
} ///:~
輸出:
4194303
18014398509481983
-1
-1
移位可與等號(<<=或>>=或>>>=)組合使用。此時,運算符左邊的值會移動由右邊的值指定的位數,再將得到的結果賦回左邊的值。
“造型”(Cast)的作用是“與一個模型匹配”。在適當的時候,Java 會將一種數據類型自動轉換成另一種。例如,假設我們為浮點變量分配一個整數值,計算機會將 int自動轉換成 float。通過造型,我們可明確設置這種類型的轉換,或者在一般沒有可能進行的時候強迫它進行。 為進行一次造型,要將括號中希望的數據類型(包括所有修改符)置於其他任何值的左側。
正如您看到的那樣,既可對一個數值進行造型處理,亦可對一個變量進行造型處理。但在這兒展示的兩種情況下,造型均是多余的,因為編譯器在必要的時候會自動進行 int值到long 值的轉換。當然,仍然可以設置一個造型,提醒自己留意,也使程序更清楚。在其他情況下,造型只有在代碼編譯時才顯出重要性。在C 和C++中,造型有時會讓人頭痛。
在Java 裡,造型則是一種比較安全的操作。但是,若進行一種名為“縮小轉換”(Narrowing Conversion)的操作(也就是說,腳本是能容納更多信息的數據類型,將其轉換成容量較小的類型),此時就可能面臨信息丟失的危險。此時,編譯器會強迫我們進行造型,就好象說:“這可能是一件危險的事情——如果您想讓我不顧一切地做,那麼對不起,請明確造型。”而對於“放大轉換”(Widening conversion),則不必進行明確造型,因為新類型肯定能容納原來類型的信息,不會造成任何信息的丟失。
Java 允許我們將任何主類型“造型”為其他任何一種主類型,但布爾值(bollean)要除外,後者根本不允許進行任何造型處理。“類”不允許進行造型。為了將一種類轉換成另一種,必須采用特殊的方法(字串是一種特殊的情況,本書後面會講到將對象造型到一個類型“家族”裡;例如,“橡樹”可造型為“樹”;反之亦然。但對於其他外來類型,如“巖石”,則不能造型為“樹”)。
在C 和C++中,sizeof()運算符能滿足我們的一項特殊需要:獲知為數據項目分配的字符數量。在C 和C++中,size()最常見的一種應用就是“移植”。不同的數據在不同的機器上可能有不同的大小,所以在進行一些對大小敏感的運算時,程序員必須對那些類型有多大做到心中有數。例如,一台計算機可用32位來保存整數,而另一台只用16位保存。顯然,在第一台機器中,程序可保存更大的值。正如您可能已經想到的那樣,移植是令C 和C++程序員頗為頭痛的一個問題。
Java 不需要sizeof()運算符來滿足這方面的需要,因為所有數據類型在所有機器的大小都是相同的。我們不必考慮移植問題——Java 本身就是一種“與平台無關”的語言。