1. 程式人生 > >Java開發筆記(二十二)神奇的冒號

Java開發筆記(二十二)神奇的冒號

自增 while 點號 結束 默認 sim 作用 以及 簡單的

Java中的標點符號主要有兩類用途,一類是運算符,包括加號+、減號-、乘號*、除號/、取余號%、等號=、大於號>、小於號<、與號&、或號|、非號!、異或號^等等,另一類則是分隔符,包括區分代碼塊的花括號{}、容納特定語句的圓括號()、標明數組元素的方括號[]、分隔長句的分號、分隔短句的逗號、分隔包名類名方法名的點號等等。當然還有幾個特殊的分隔符,比如三元運算符“?:”,它的完整形式為“A?:B:C”,當式子A成立時,得到式子B的結果,不成立時得到式子C的結果。這些標點符號之中,尤以冒號最為特殊,之所以這麽說,是因為Java編程一遇到特殊的分隔場景,基本都拿冒號這個萬金油做標記。

冒號除了用在三元運算符“?:”以外,至少還有其它三種用法。第二種是在多路分支的時候,猶記得當年switch-case並肩作戰,每個case帶著數值再拖上具體的處理語句,case條件與處理語句之間可是以冒號分隔的,剩余的默認情況default也是通過冒號同處理語句區分開。譬如下面的多路分支代碼,就能看到冒號的分隔作用:

		// switch允許判斷某個變量的多個取值,並分別進行單獨處理
		switch (seq) {
			case 1: // seq值為1時進入該分支
				System.out.println("涼風有信的謎底是“諷”");
				break; // 跳出多路分支。即跳到switch分支的右花括號之後
			case 2: // seq值為2時進入該分支
				System.out.println("秋月無邊的謎底是“二”");
				break; // 跳出多路分支。即跳到switch分支的右花括號之後
			default: // seq值為其它時進入該分支
				System.out.println("您的按鍵有誤");
				break; // 跳出多路分支。即跳到switch分支的右花括號之後
		}

冒號的第三種用法,則跟數組的循環遍歷有關。要想把某個數組裏的所有元素數值都打印出來,就得通過for循環依次取出數組的每個元素,再打印該元素的數值。以整型數組為例,利用for語句遍歷並打印元素的代碼如下所示:

		int[] primeNumbers = {2, 3, 5, 7};
		for (int i = 0; i < primeNumbers.length; i++) {
			int number = primeNumbers[i]; // 獲取下標為i的元素,並賦值給名為number的變量
			System.out.println("prime number = " + number);
		}

上面的循環語句很常規,用法形式也很常見,無非是依次取出數組裏的每個元素罷了。倘若此時不修改元素數值,僅僅是讀取數值的話,那麽可以簡化成一套通用的循環模板,就像下述的循環語句那樣:

		int[] primeNumbers = {2, 3, 5, 7};
		// 在for循環中,可以利用“變量類型 變量名稱 : 數組名稱”的形式,直接把數組元素賦值給該變量
		for (int number : primeNumbers) {
			System.out.println("number = "+number);
		}

上述的Java代碼,把原循環內部的變量number提前放到for後面的圓括號當中,並且number與數組primeNumbers之間用冒號分開,表示每次循環處理之前,都把數組元素逐個賦值給number變量,然後循環內部即可直接處理該變量。如此這般便優化了先前的for循環代碼,免去了冗余的數組下標、判斷條件以及自增操作。

冒號的第四種用法也與循環語句有關,但不限於for循環,而是與for和while都有關聯。前述的循環處理,基本都只有一層循環,然而實際開發常常會遇到多層循環,也就是一個循環內部嵌套了另一個循環,看起來像是層巒疊嶂、反復重疊,故又被稱作多重循環。例如有個二維數組,要把它裏面的所有元素都打印出來,這便需要兩層循環才能搞定,第一層循環負責遍歷第一個維度的下標,而第二層循環負責遍歷第二個維度的下標,編碼上則需一個for循環嵌套另一個for循環,具體的Java實現代碼如下所示:

		double[][] triangle = { {-2.0, 0.0}, {0.0, -1.0}, {2.0, 1.0} };
		// 下面通過多重循環依次打印二維數組裏面的所有元素
		for (int i=0; i<triangle.length; i++) {
			for (int j=0; j<triangle[i].length; j++) {
				System.out.println("value = "+triangle[i][j]);
			}
		}

可見以上的多重循環代碼還是挺簡單的,並不涉及到復雜的break和continue操作。即使用到break和continue,處理邏輯也沒有什麽特別之處,因為break語句想當然就是只能跳出當前層次的循環,不能跳出上個層次的循環,continue語句同理。所以要想從內層循環跳出外層循環,就得設置一個標記,從內層循環跳到外層循環時,通過判斷該標記再決定是否立刻跳出外層循環。仍以前面的二維數組為例,假設在內層循環找到某個元素為0.0,則立即結束全部循環(包括外層循環和內層循環),按此思路編寫的代碼例子見下:

		// 下面的循環要求:一旦發現數組元素等於0.0,就立即從第二層循環跳出第一層循環(跳出兩層循環)
		for (int i=0; i<triangle.length; i++) {
			boolean isFound = false; // 該布爾變量用來標記是否找到0.0
			for (int j=0; j<triangle[i].length; j++) {
				if (triangle[i][j] == 0.0) {
					isFound = true; // 找到了0.0
					System.out.println("simple found 0.0");
					break; // 跳出第二層循環
				}
			}
			if (isFound) {
				break; // 跳出第一層循環
			}
		}

以上代碼固然實現了功能要求,但是兩個break的寫法著實令人憋屈,而且布爾變量isFound純粹是到此一遊。有沒有一種寫法允許讓代碼直接從內層循環跳出外層循環呢?與其讓布爾變量做標記,不如給外層循環加個記號,然後內層循環就能告訴編譯器,接下來的break語句要跳出指定標記的循環。這時冒號便派上用場了,通過形如“標記名稱 : for或者while”的表達式,即可給指定循環起個外號,於是語句“break 標記名稱”便實現了跳出指定循環的需求。那麽使用新寫法改造前面的循環跳出代碼,修改之後的代碼如下所示:

		// 下面的loop1是一個記號,連同後面的冒號加在for前面,表示它指代這個for循環
		loop1 : for (int i=0; i<triangle.length; i++) {
			for (int j=0; j<triangle[i].length; j++) {
				if (triangle[i][j] == 0.0) {
					System.out.println("loop1 found 0.0");
					break loop1; // 跳出loop1代表的循環,也就是跳出第一層循環
				}
			}
		}

如上代碼先在外層的for循環之前添加“loop1 : ”,表明外層循環的綽號叫loop1,然後內層循環的break語句改成“break loop1;”,表示跳出loop1這個外層循環,這樣只需一個break語句就跳出多重循環了。除了break語句,continue也允許帶上標記名稱,比如“continue loop1”表示繼續loop1這個外層循環的下一次循環處理,並且while循環也同樣認可在break和continue後面添加標記。當然,利用前面介紹的冒號第三種用法,上面的多重循環還能簡化成下述這般代碼:

		// 下面用到了兩種冒號,一種用來標記循環,另一種用來簡化數組遍歷
		loop2 : for (double[] dot : triangle) { // dot等價於前面的triangle[i]
			for (double coordinate : dot) { // coordinate等價於前面的triangle[i][j]
				if (coordinate == 0.0) {
					System.out.println("loop2 found 0.0");
					break loop2; // 跳出loop2代表的循環
				}
			}
		}

如此一來,上述的循環代碼聯合應用了冒號的兩種用法,整個代碼也變得更加精煉了。

Java開發筆記(二十二)神奇的冒號