1. 程式人生 > >Java 相關知識的學習(第一章至第三章)

Java 相關知識的學習(第一章至第三章)

學習的書籍:《Java核心技術+卷1:基礎知識(原書第9版)》

關於本書

第一章:概述 Java 與其他程式設計語言不同的效能;

第二章:詳細地論述如何下載和安裝 JDK 以及本書的程式示例;

第三章:開始討論 Java語言;

第四章:將介紹面向物件兩個基本成分中最重要的—封裝, 以及Java語言實現封裝的機制, 即類與方法;

第五章:將介紹另一部分—繼承;

第六章:展示如何使用 Java的介面;

第七章:開始細緻地討論應用程式設計;

第八章:詳細討論 AWTAbstract Window Toolkit) 的事件模型;

第九章:詳細討論Swing GUI工具箱;

第十章:闡述如何部署自己編寫的應用程式或

applet;

十一章:討論異常處理, 即Java的健壯機制, 它用於處理除錯好的程式可能出現意外的情況;

第十二章:概要介紹泛型程式設計, 這是Java SE 5.0的一項重要改進;

第十三章:介紹Java平臺的集合框架; 

第十四章:在這一章中將介紹多執行緒, 這是一種可以讓程式任務並行執行的特性(執行緒是程式中的控制流), 並闡述如何建立執行緒、 如何處理執行緒的同步問題;

第 1 章 Java 程式設計概述

1.1 Java 程式設計平臺 

Java 與其他程式設計語言不同的效能。

1.2 Java“白皮書” 的關鍵術語

Java 白皮書的關鍵術語:簡單性、面向物件、網路技能(Network-Savvy)、健壯性、 安全性、 體系結構中立、可移植性、 解釋型、 高效能、多執行緒、 動態性。

1.3 Java applet
Internet

在網頁中執行 Java 程式稱為 applet,為了使用 applet, 需要啟用 Java 的 Web 瀏覽器執行位元組碼。 
當 applet 首次出現時, 人們欣喜若狂。 許多人相信 applet 的魅力將會導致 Java 迅速地流行起來。 然而, 初期的興奮很快就淡化了。 不同版本的 Netscape 與 Internet Explorer 執行不同版本的 Java, 其中有些早已過時。 這種糟糕的情況導致更加難於利用 Java 的最新版本開發 applet。 今天, 當需要在瀏覽器中顯示動態效果時, 大多數網頁都直接使用 JavaScript 或Flash。 

1.4 Java
發展簡史

1996 年年初, Sun釋出了Java的第1個版本。 坦率地說,Java 1.0 的確沒有為其黃金時期的到來做好準備。
後來的 Java1.1 彌補了其中的大多明顯的缺陷, 大大改進了反射能力, 併為GUI程式設計增加了新的事件處理模型。 不過它仍然具有很大的侷限性。 1998 JavaOne會議的頭號新聞是即將釋出Java 1.2版。199812Java 1.2釋出三天之後, Sun 公司市場部將其名稱改為更加吸引人的“Java 2標準版軟體開發工具箱1.2版”。
標準版的 1.3 1.4版本對最初的Java 2版本做出了某些改進, 擴充套件了標準類庫, 提高系統性能。 當然, 還修正了一些bug。   5.0 版是自 1.1版以來第一個對Java語言做出重大改進的版本(這一版本原來被命名為1.5版, 在2004年的 JavaOne會議之後, 版本數字升至5.0)。 版本 6(沒有後綴.0) 於2006年年末釋出。
直到 2011 Oracle釋出了Java的一個新版本,Java 7, 其中只做了一些簡單的改進,而決定將重要的改進推遲到Java 8, 該版本將在2013年釋出。 

1.5 關於 Java的常見誤解

列出了一些關於 Java 的常見誤解, 同時給出瞭解釋。

2 Java程式設計環境

2.1 安裝Java開發工具箱

Oracle 公司為 Linux、 Mac OS X、 Solaris 和 Windows 提供了 Java 開發工具箱(JDK) 的最新、 最完整的版本。
在完成了 JDK 的安裝之後, 還需要執行另外一個步驟: 把jdk/bin目錄新增到執行路徑中。 
庫原始檔在 JDK 中以一個壓縮檔案 src.zip 的形式釋出, 必須將其解壓縮後才能夠訪問原始碼。 
文件包含在一個壓縮檔案中, 它是一個獨立於 JDK 的壓縮檔案。 可以直接從網站 http://www.oracle.com/technetwork/java/javase/downloads 下載獲得這個文件。
在學習 Java 的過程中, 經常需要檢視 Java 原始檔。 就學習 Java 而言, docs 和 src 是兩個最有用的子目錄。

2.2 選擇開發環境

使用 Eclipse 和 NetBeans 這兩個免費的開發環境是很好的選擇。 
總之, 應當瞭解如何使用基本的 JDK工具, 這樣才會感覺使用整合開發環境是一種享受。 

2.3 使用命令列工具

首先介紹較難的一種方法: 通過命令列編譯並執行 Java 程式。
javac 程式是一個 Java 編譯器。 它將檔案 Welcom.java
編譯成 Welcom.class, 併發送到 Java 虛擬機器。 虛擬機器執行編譯器放在 class 檔案中的位元組碼。

2.4 使用整合開發環境

書裡面試用的Eclipse 編譯程式; Eclipse 是採用 java 編寫的, 然而, 由於所使用的是非標準視窗類庫, 所以, 不如 Java 那樣具有可移植性。 使用步驟如下所示:
1) 啟動 Eclipse 之後, 從選單選擇 File → New → Java Project(如圖 2-1 所示)。這些是 Eclipse 4.2 的螢幕畫面。如果使用的 Eclipse 版本稍有差異, 不必介意。
2) 鍵入工程名 Welcome,如圖 2-2 所示。點選Next。
3) 點選Link additional source,匯入Welcome工程,如圖 2-3 所示。
4) 點選 Finish 按鈕。 這個工程已經建立完成了。 5) 在工程視窗中 點選Default package旁邊的三角, 再雙擊Welcome.java。 現在應該看到帶有程式程式碼的視窗了
6) 用滑鼠右鍵點選最左側窗格中的工程名(Welcome), 選擇RunRun AsJava Application。 可以看到: 這個視窗底部出現了一個輸出視窗。 在這個輸出視窗中顯示了程式的輸出結構(如圖2-4所示)。
圖2-1

圖2-1

圖2-3


圖2-4

2.5 執行圖形化應用程式

這個程式是一個簡單的影象檔案檢視器(viewer), 它可以載入並顯示一個影象。
首先, 由命令列編譯並執行這個程式。
1) 開啟一個 shell 視窗。
2) 進入 CoreJavaBook/v1ch02/ImageViewer。
3) 輸入: javac ImageViewer.java
java ImageViewer 執行後將彈出一個標題欄為 ImageViewer 的新程式視窗(如圖 2-5 所示)。現在, 選擇 File → Open, 然後找到一個影象檔案並開啟它(我們在同一個目錄下提供了兩個示例檔案)。 要想關閉這一程式, 只需要點選標題欄中的關閉按鈕或者從選單中選擇 File → Exit。 圖2-5 2.6 建立並執行 applet 本書給出的前兩個程式是 Java 應用程式。 它們與所有本地程式一樣, 可以獨立地執行。然而, 正如第 1 章提到的, 有關 Java 的大量宣傳都在炫耀 Java 能夠在瀏覽器中執行applet的能力。 下面介紹一下如何利用命令列建立並執行 applet。 然後, 利用 JDK 自帶的 applet 檢視器載入 applet。 最後, 在 Web 瀏覽器中顯示。 首先, 開啟 shell 視窗並將目錄轉到 CoreJavaBook/v1ch02/WelcomeApplet, 然後, 輸入下面的命令: javac WelcomeApplet.java appletviewer WelcomeApplet.html 2-6 顯示了在applet檢視器視窗中顯示的內容。  圖2-6 第一條命令是大家已經非常熟悉的呼叫 Java 編譯器的命令。 它將 WelcomeApplet.java 原始檔編譯成位元組碼檔案 WelcomeApplet.class。不過這一次不要執行 Java 程式, 而呼叫 appletviewer 程式。這是 JDK 自帶的一個特殊工具, 它可以幫助人們快速地測試 applet。 這裡需要向這個程式提供一個 HTML 檔名, 而不是 Java 類檔名。在本章中, 我們學習了有關編譯和執行 Java 程式的機制。 現在可以轉到第 3 章開始學習Java 語言了。 

第 3 章 Java 的基本程式設計結構

現在, 假定已經成功地安裝了 JDK, 並且能夠執行第 2 章中給出的示例程式。 我們從現在開始將介紹 Java 應用程式設計。 本章主要講述程式設計相關的基本概念(如資料型別、 分支以及迴圈) 在 Java 中的實現方式。

3.1 一個簡單的 Java 應用程式

下面看一個最簡單的 Java 應用程式, 它只發送一條訊息到控制檯視窗中:
public class FirstSample
{
	public static void main(String[] args)
	{	
	 System.out.println("We will not use 'Hello World!'");
	}
}
這個程式雖然很簡單, 但所有的 Java 應用程式都具有這種結構, 還是值得花一些時間研究一下。 首先, Java 對大小寫敏感。如果出現了大小寫拼寫錯誤(例如, 將 main 拼寫成Main), 那程式將無法執行。 下面逐行地檢視一下這段原始碼:
  • 關鍵字 public 稱為訪問修飾符(access modifier), 它用於控制程式的其他部分對這段程式碼的訪問級別。 
  • 關鍵字 class 表明 Java 程式中的全部內容都包含在類中。
  • 關鍵字 class 後面緊跟類名。  名字必須以字母開頭, 後面可以跟字母和數字的任意組合。 
  • 從類名 FirstSample 可以看出, 標準的命名規範為: 類名是以大寫字母開頭的名詞。 如果名字由多個單片語成, 每個單詞的第一個字母都應該大寫。
  • 原始碼的檔名必須與公共類的名字相同, 並用 .java 作為副檔名。因此, 儲存這段原始碼的檔名必須為 FirstSample.java(再次提醒大家注意, 大小寫是非常重要的, 千萬不能寫成 firstsample.java)。
  • 如果已經正確地命名了這個檔案, 並且原始碼中沒有任何錄入錯誤, 在編譯這段原始碼之後就會得到一個包含這個類位元組碼的檔案。 Java 編譯器將位元組碼檔案自動地命名為 FirstSample.class, 並與原始檔儲存在同一個目錄下。 
  • 最後, 使用下面這行命令執行這個程式:java FirstSample
  • 程式執行之後, 控制檯上將會顯示“We will not use‘Hello,World’ !”。
  • 當使用 java ClassName  執行編譯程式時, Java 虛擬機器將從指定類中的 main 方法開始執行(這裡的“方法” 就是Java 中所說的“函式”), 因此為了程式碼能夠執行, 在類的原始檔中必須包含一個 main 方法。當然, 也可以將使用者自定義的方法新增到類中, 並且在 main 方法中呼叫它們。
  • 需要注意原始碼中的括號 { }。 在 Java 中, 像在 C/C++ 中一樣, 用花括號劃分程式的各個部分(通常稱為塊)。 Java 中任何方法的程式碼都用“{” 開始, 用“}” 結束。
  • 我們暫且不去理睬關鍵字 static void, 而僅把它們當作編譯 Java 應用程式必要的部分就行了。
  • 現在需要記住: 每個 Java 應用程式都必須有一個 main 方法, 其格式如下所示:
  • public class ClassName
    {
       public static void main(String[] args)
       {
         program statements
       }
    }
  • 接下來, 研究一下這段程式碼:
  • {	
    System.out.println("We will not use 'Hello World!'");
    }
  • 一對花括號表示方法體的開始與結束, 在這個方法中只包含一條語句。
  •  在 Java 中, 每個句子必須用分號結束。
  • 在上面這個 main 方法體中只包含了一條語句, 其功能是: 將一個文字行輸出到控制檯上。
  • 在這裡, 使用了 System.out 物件並呼叫了它的 println 方法。 注意, 點號(· ) 用於呼叫方法。 
  • Java 使用的通用語法是:    這等價於函式呼叫。
    object.method(parameters)
  • 在這個示例中, 呼叫了 println 方法並傳遞給它一個字串引數。 這個方法將傳遞給它的字串引數顯示在控制檯上。 然後, 終止這個輸出行, 以便每次呼叫 println 都會在新的一行上顯示輸出。
  • 與其他程式設計語言一樣, 在 Java 的方法中, 可以沒有引數, 也可以有一個或多個引數(有的程式設計師把引數叫做實參)。 對於一個方法, 即使沒有引數也需要使用空括號。 例如, 不帶引數的 println 方法只打印一個空行。 使用下面的語句:
    System.out.println()

    3.2 註釋

與大多數程式設計語言一樣, Java 中的註釋也不會出現在可執行程式中。 因此, 可以在源程式中根據需要新增任意多的註釋, 而不必擔心可執行程式碼會膨脹。 在 Java 中, 有三種書寫註釋的方式。 最常用的方式是使用 //, 其註釋內容從 // 開始到本行結尾。
System.out.println("We will not use 'Hello World'!"); //is this too cute?
當需要長篇的註釋時, 既可以在每行的註釋前面標記 //, 也可以使用 /* 和 */ 將一段比較長的註釋括起來。
第三種註釋可以用來自動地生成文件。 這種註釋以 /** 開始, 以 */ 結束。 有關這種註釋的詳細內容和自動生成文件的具體方法請參見第 4 章。
3.3 資料型別
Java 是一種強型別語言。 這就意味著必須為每一個變數宣告一種型別。 在 Java 中, 一共有 8 種基本型別(primitive type), 其中有 4 種整型、 2 種浮點型別、 1 種用於表示 Unicode 編碼的字元單元的字元型別 char(請參見論述 char 型別的章節) 和 1 種用於表示真值的 boolean型別。

3.3.1 整型

整型用於表示沒有小數部分的數值, 它允許是負數。 Java 提供了 4 種整型, 具體內容如圖 3-1 所示。

圖3-1 在通常情況下, int 型別最常用。 但如果表示星球上的居住人數, 就需要使用 long 型別了。 byte 和 short 型別主要用於特定的應用場合, 例如, 底層的檔案處理或者需要控制佔用儲存空間量的大陣列。
長 整 型 數 值 有 一 個 後 綴 L(如 4000000000L)。 十 六 進 制 數 值 有 一 個 前 綴 0x(如0xCAFE)。 八進位制有一個字首 0, 例如, 010 對應八進位制中的 8。 很顯然, 八進位制表示法比較容易混淆, 所以建議最好不要使用八進位制常數。
從 Java 7 開始, 加上字首 0b 就可以寫二進位制數。 例如, 0b1001 就是 9。 另外, 同樣是從Java 7 開始, 還可以為數字字面量加下劃線, 如用 1_000_000(或 0b1111_0100_0010_0100_0000)表示一百萬。 這些下劃線只是為了讓人更易讀。 Java 編譯器會去除這些下劃線。

3.3.2 浮點型別

浮點型別用於表示有小數部分的數值。 在 Java 中有兩種浮點型別, 具體內容如圖 3-2 所示。

圖3-2 double 表示這種型別的數值精度是 float 型別的兩倍(有人稱之為雙精度數值)。 絕大部分應用程式都採用 double 型別。 在很多情況下, float 型別的精度很難滿足需求。例如, 用 7位有效數字足以精確地表示普通僱員的年薪, 但表示公司總裁的年薪可能就不夠用了。 實際上, 只有很少的情況適合使用 float 型別, 例如, 需要快速地處理單精度資料, 或者需要儲存大量資料。 float 型別的數值有一個字尾 F(例如, 3.14F)。 沒有後綴 F 的浮點數值(如 3.14) 預設為double 型別。 當然, 也可以在浮點數值後面新增字尾 D(例如, 3.14D)。
所有的浮點數值計算都遵循 IEEE 754 規範。 下面是用於表示溢位和出錯情況的三個特殊的浮點數值:
  • 正無窮大
  • 負無窮大
  • NaN(不是一個數字)
例如, 一個正整數除以 0 的結果為正無窮大。 計算 0/0 或者負數的平方根結果為 NaN。
註釋:常量 Double.POSITIVE_INFINITY、 Double.NEGATIVE_INFINITY 和 Double.NaN(與相應的 Float 型別的常量一樣) 分別表示這三個特殊的值, 但在實際應用中很少遇到。 特別要說明的是, 不能這樣檢測一個特定值是否等於 Double.NaN:
if(x == Double.NaN) //is never true
所有“非數值” 的值都認為是不相同的。 然而, 可以使用 Double.isNaN 方法:
if(Double.isNaN(x)) //check whether x is "not a number"

3.3.3 char 型別

char 型別用於表示單個字元。 通常用來表示字元常量。 例如: 'A' 是編碼為 65 所對應的字元常量。 與 "A" 不同, "A" 是一個包含字元 A 的字串。 Unicode 編碼單元可以表示為十六進位制值, 其範圍從 \u0000 到 \Uffff。 例如: \u2122 表示註冊符號(TM), \u03C0 表示希臘字母 p。
除了可以採用轉義序列符 \u 表示 Unicode 程式碼單元的編碼之外, 還有一些用於表示特殊字元的轉義序列符, 請參看圖 3-3。
圖3-3 所有這些轉義序列符都可以出現在字元常量或字串的引號內。 例如, '\u2122' 或 "Hello\n"。 轉義序列符 \u 還可以出現在字元常量或字串的引號之外(而其他所有轉義序列不可以)。 例如:
 public static void main(String\u005B\u005D args)
這種形式完全符合語法規則, \u005B 和 \u005D 是 [ 和 ] 的編碼。
要想弄清 char 型別, 就必須瞭解 Unicode 編碼表。 Unicode 打破了傳統字元編碼方法的限制。 在 Unicode 出現之前, 已經有許多種不同的標準: 美國的 ASCII、 西歐語言中的 ISO8859-1、 俄國的 KOI-8、 中國的 GB 18030 和 BIG-5 等。 這樣就產生了下面兩個問題: 一個是對於任意給定的程式碼值, 在不同的編碼方案下有可能對應不同的字母; 二是採用大字符集的語言其編碼長度有可能不同。 例如, 有些常用的字符采用單位元組編碼, 而另一些字元則需要兩個或更多個位元組。
設計 Unicode 編碼的目的就是要解決這些問題。
我們強烈建議不要在程式中使用 char 型別, 除非確實需要對 UTF-16 程式碼單元進行操作。 最好將需要處理的字串用抽象資料型別表示(有關這方面的內容將在 3.6 節討論)。

3.3.4 boolean 型別

boolean(布林) 型別有兩個值:falsetrue, 用來判定邏輯條件。 整型值和布林值之間不能進行相互轉換。

3.4 變數

在 Java 中, 每一個變數屬於一種型別(type)。 在宣告變數時, 變數所屬的型別位於變數名之前。 這裡列舉一些宣告變數的示例:
double salary;
int vacationDays;
long earthPopulation;
boolean done;
可以看到, 每個宣告以分號結束。 由於宣告是一條完整的語句, 所以必須以分號結束。
變數名必須是一個以字母開頭的由字母或數字構成的序列。需要注意, 與大多數程式設計語言相比, Java 中“字母” 和“數字” 的範圍要大。 字母包括 'A' ~ 'Z'、 'a' ~ 'z'、 '_'、 '$'或在某種語言中代表字母的任何 Unicode 字元。 例如, 德國的使用者可以在變數名中使用字母‘ä’ ; 希臘人可以用 p。 同樣, 數字包括 '0' ~ '9' 和在某種語言中代表數字的任何 Unicode 字元。 但 '+' 和 '©' 這樣的符號不能出現在變數名中, 空格也不行。 變數名中所有的字元都是有意義的, 並且大小寫敏感。 變數名的長度沒有限制。
  • 提示: 如果想要知道哪些 Unicode 字元屬於 Java 中的“字母”, 可以使用 Character 類的isJavaIdentifierStart 和 isJavaIdentifierPart 方法進行檢測。
  • 提示: 儘管 $ 是一個合法的 Java 字元, 但不要在你自己的程式碼中使用這個字元。 它只用在 Java 編譯器或其他工具生成的名字中。
可以在一行中宣告多個變數:
int i,j; //both are integers
不過, 不提倡使用這種風格。 逐一宣告每一個變數可以提高程式的可讀性。

3.4.1 變數初始化

宣告一個變數之後, 必須用賦值語句對變數進行顯式初始化, 千萬不要使用未被初始化的變數。 例如, Java 編譯器認為下面語句序列是錯誤的:
int vacationDays;
System.out.println(vacationDays); // ERROR--variable not initialized
要想對一個已經宣告過的變數進行賦值, 就需要將變數名放在等號(=) 左側, 相應取值的 Java 表示式放在等號的右側。也可以將變數的宣告和初始化放在同一行中。最後, 在 Java 中可以將宣告放在程式碼中的任何地方。 例如, 下列程式碼的書寫形式在 Java中是完全合法的:
double salary = 65000.0;
System.out.println(salary); 
int vacationDays = 12;// OK to declare a variable here
在 Java 中, 變數的宣告儘可能地靠近變數第一次使用的地方, 這是一種良好的程式編寫風格。

3.4.2 常量

在 Java 中, 利用關鍵字 final 指示常量。 例如:
public class Constants
{
    public static void main(String[] args)
    {
        final double CM_PER_INCH = 2.54;
        double paperWidth = 8.5;
        double paperHeight = 11;
        System.out.println("Paper size in centimeters:"
                + paperWidth * CM_PER_INCH + paperHeight * CM_PER_INCH);
    }
}
關鍵字 final 表示這個變數只能被賦值一次。 一旦被賦值之後, 就不能夠再更改了。 習慣上,常量名使用全大寫。
在 Java 中, 經常希望某個常量可以在一個類中的多個方法中使用, 通常將這些常量稱為類常量。 可以使用關鍵字 static final 設定一個類常量。 下面是使用類常量的示例:
public class Constants2
{
    public static final double CM_PER_INCH = 2.54;

    public static void main(String[] args)
    {
        double paperWidth = 8.5;
        double paperHeight = 11;
        System.out.println("Paper size in centimeters:"
                + paperWidth * CM_PER_INCH + paperHeight * CM_PER_INCH);
    }
}
需要注意, 類常量的定義位於 main 方法的外部。 因此, 在同一個類的其他方法中也可以使用這個常量。 而且, 如果一個常量被宣告為 public, 那麼其他類的方法也可以使用這個常量。 在這個示例中, Constants2.CM_PER-INCH 就是這樣一個常量。

3.5 運算子

在 Java 中, 使用算術運算子+、 -、 *、 / 表示加、 減、 乘、 除運算。 當參與 / 運算的兩個運算元都是整數時, 表示整數除法; 否則, 表示浮點除法。 整數的求餘操作(有時稱為取模) 用 % 表示。 例如, 15/2 等於 7, 15%2 等於 1, 15.0/2 等於 7.5。
需要注意, 整數被 0 除將會產生一個異常, 而浮點數被 0 除將會得到無窮大或 NaN 結果。
可以在賦值語句中採用一種簡化的格式書寫二元算術運算子。例如,x += 4; 等價於 x = x+4; (通常, 將運算子放在賦值號的左側, 如 *= 或 %=。)

3.5.1 自增運算子與自減運算子

當然, 程式設計師都知道加 1、 減 1 是數值變數最常見的操作。 在 Java 中, 借鑑了 C 和 C++的實現方式, 也使用了自增、 自減運算子: n++ 將變數 n 的當前值加 1 ; n–– 將 n 的值減 1。例如:
int n = 12;
n++;
n 的值將變為 13。 因為這些運算子改變了變數的值, 所以它的運算元不能是數值。 例如,4++ 就是一條非法的語句。
實際上, 這兩個運算子有兩種形式。 上面介紹的是運算子放在運算元後面的“字尾” 形式, 還有一種“字首” 形式, ++n。 兩種方式都是對變數值加 1。 但在表示式中, 這兩種形式就有區別了。 字首方式先進行加 1 運算; 字尾方式則使用變數原來的值。
int m = 7;
int n = 7;
int a = 2 * ++m; // now a is 16,m is 8
int b = 2 * n++; //now b is 14,n is 8
我們建議不要在其他表示式的內部使用 ++, 這樣編寫的程式碼很容易令人困惑, 並會產生煩人的 bug。

3.5.2 關係運算符與boolean運算子

Java 包含各種關係運算符。 其中, 使用兩個等號= =檢測是否相等。 例如,3 = = 7的值為false
使用
!= 檢測是否不相等。 例如,3 != 7的值為true
另外, 經常使用的運算子還有
<(小於)、>(大於)、<=(小於等於) 和>=(大於等於)。
Java 沿用了 C++ 的習慣, 用 && 表示邏輯“與”、 用 || 表示邏輯“或”。 從 != 運算子很容易看出, ! 表示邏輯“非”。 && 和 || 是按照“短路” 方式求值的。 如果第一個運算元已經能夠確定表示式的值, 第二個運算元就不必計算了。 如果用 && 對兩個表示式進行計算: expression1 && expression2 並且第一個表示式值為 false, 結果不可能為真。因此, 第二個表示式的值就沒有必要計算了。 這種方式可以避免一些錯誤的發生。例如, 表示式:
x !=0 && 1 / x > x + y // no division by 0
當 x 為 0 時, 不會計算第二部分。 因此, 若 x 為 0, 1/x 不被計算, 也不會出現除以 0 的錯誤。 與之類似, 對於 expression1 || expression2, 當第一個表示式為 true 時, 結果自動為 true,不必再計算第二部分。
最後, Java 支援三元操作符 ?:。 在很多時候, 這個操作符非常有用。 表示式condition ? expression1 : expression2 當條件 condition 為真時計算第 1 個表示式, 否則計算第 2 個表示式。例如: x < y ? x : y 返回 x 和 y 中較小的那個值。 3.5.3 位運算子 在處理整型數值時, 可以直接對組成整型數值的各個位進行操作。 這意味著可以使用遮蔽技術獲得整數中的各個位。 位運算子包括:&(" 與 ")、 |(" 或 ")、 ^(" 異或 ")、 ~(" 非 ")
這些運算子在位模式下工作。 例如, 如果 n 是一個整型變數, 並且用二進位制表示的 n 從右數第 4 位為 1, 那麼int fourthBitFromRight = (n & 0b1000) / 0b1000; 
返回 1 ; 否則返回 0。 通過運用 2 的冪次方的 & 運算可以將其他位遮蔽掉, 而只保留其中的某一位。
另外,“>>” 和“<<” 運算子將二進位制位進行右移或左移操作。 當需要建立位模式遮蔽某些位時, 使用這兩個運算子十分方便:int fourthBitFromRight = (n & (a << 3)) >> 3;  最後, >>> 運算子將用 0 填充高位; >> 運算子用符號位填充高位。 沒有 <<< 運算子。

3.5.4 數學函式與常量

在 Math 類中, 包含了各種各樣的數學函式。 在編寫不同類別的程式時, 可能需要的函式也不同。
要想計算一個數值的平方根, 可以使用 sqrt 方法:
 double x = 4;
 double y = Math.sqrt(x);
 System.out.println(y); // ptints 2.0
println 方法和 sqrt 方法存在微小的差異。 println 方法操作一個定義在 System 類中的 System.out 物件。 但是, Math 類中的 sqrt 方法處理的不是物件, 這樣的方法被稱為靜態方法。 有關靜態方法的詳細內容請參看第 4 章。
在 Java 中, 沒有冪運算, 因此需要藉助於 Math 類的 pow 方法。 語句: double y = Math.pow(x. a); 將 y 的值設定為 x 的 a 次冪(xa)。 pow 方法有兩個 double 型別的引數, 其返回結果也為double 型別。
Math 類提供了一些常用的三角函式: Math.sin、Math.cos、Math.tan、Math.atan、Math.atan2
還有指數函式以及它的反函式—自然對數以及以 10 為底的對數:Math.exp、Math.log、Math.log10
最後, Java 還提供了兩個用於表示 p 和 e 常量的近似值:Math.PI、Math.E

3.5.5 數值型別之間的轉換

在程式執行時, 經常需要將一種數值型別轉換為另一種數值型別。 圖 3-4 給出了數值型別之間的合法轉換。 圖3-4 數值型別之間的合法轉換 在圖 3-4 中有 6 個實心箭頭, 表示無資訊丟失的轉換; 有 3 個虛箭頭, 表示可能有精度損失的轉換。 例如, 123 456 789 是一個大整數, 它所包含的位數比 float 型別所能夠表達的位數多。 當將這個整型數值轉換為 float 型別時, 將會得到同樣大小的結果, 但卻失去了一定的精度。
int n = 123456789;
float f = n; // f is 1.234567892E8
使用上面兩個數值進行二元操作時(例如 n + f, n 是整數, f 是浮點數), 先要將兩個運算元轉換為同一種類型, 然後再進行計算。
如果兩個運算元中有一個是 double 型別, 另一個運算元就會轉換為 double 型別。
● 否則, 如果其中一個運算元是 float 型別, 另一個運算元將會轉換為 float 型別。
● 否則, 如果其中一個運算元是 long 型別, 另一個運算元將會轉換為 long 型別。
● 否則, 兩個運算元都將被轉換為 int 型別。

3.5.6 強制型別轉換

在上一小節中看到, 在必要的時候, int 型別的值將會自動地轉換為 double 型別。 但另一方面, 有時也需要將 double 轉換成 int。 在 Java 中, 允許進行這種數值之間的型別轉換。
當然, 有可能會丟失一些資訊。 在這種情況下, 需要通過強制型別轉換(cast) 實現這個操作。 強制型別轉換的語法格式是在圓括號中給出想要轉換的目標型別, 後面緊跟待轉換的變數名。 例如:
double x = 9.997;
int nx = (int)x;
現在, 變數 nx 的值為 10。 當呼叫 round 的時候, 仍然需要使用強制型別轉換(int)。 其原因是 round 方法返回的結果為 long 型別, 由於存在資訊丟失的可能性, 所以只有使用顯式的強制型別轉換才能夠將 long 型別轉換成 int 型別。
  • 警告: 如果試圖將一個數值從一種型別強制轉換為另一種型別, 而又超出了目標型別的表示範圍, 結果就會截斷成一個完全不同的值。 例如,(byte) 300 的實際值為 44。

3.5.7 括號與運算子級別

表 3-4 給出了運算子的優先順序。 如果不使用圓括號, 就按照給出的運算子優先順序次序進行計算。 同一個級別的運算子按照從左到右的次序進行計算(除了表中給出的右結合運算子外。) 例如, 由於 && 的優先順序比 || 的優先順序高, 所以表示式a && b || c 等價於 (a && b) || c。又因為 += 是右結合運算子, 所以表示式 a += b += c 等價於 a += (b += c),也就是將 b += c 的結果(加上 c 之後的 b) 加到 a 上。

3.5.8 列舉型別

有時候, 變數的取值只在一個有限的集合內。 例如: 銷售的服裝或比薩餅只有小、 中、大和超大這四種尺寸。 當然, 可以將這些尺寸分別編碼為 1、 2、 3、 4 或 S、 M、 L、 X。 但這樣存在著一定的隱患。 在變數中很可能儲存的是一個錯誤的值(如 0 或 m)。
針對這種情況, 可以自定義列舉型別。 列舉型別包括有限個命名的值。 例如,
enum Size {SAMLL, MEDILE, LARGE, EXTRA_LARGE};
現在, 可以宣告這種型別的變數:
Size s = Size.MEDILE;
Size 型別的變數只能儲存這個型別宣告中給定的某個列舉值, 或者 null 值, null 表示這個變數沒有設定任何值。

3.6 字串

從概念上講, Java 字串就是 Unicode 字元序列。 例如, 串“Java\u2122” 由 5 個 Unicode字元 J、a、v、a 和 TM。Java 沒有內建的字串型別, 而是在標準 Java 類庫中提供了一個預定義類,很自然地叫做 String。 每個用雙引號括起來的字串都是 String 類的一個例項:
String e = ""; // an empty string
String greeting = "Hello";

3.6.1 子串

String 類的 substring 方法可以從一個較大的字串提取出一個子串。 例如:
String geeting = "Hello";
String s = geeting.substring(0, 3);
建立了一個由字元“Hel” 組成的字串。 substring 方法的第二個引數是不想複製的第一個位置。 這裡要複製位置為 0、 1 和 2(從0 到 2, 包括 0 和 2) 的字元。 在 substring 中從 0 開始計數, 直到 3 為止, 但不包含 3。
substring 的工作方式有一個優點: 容易計運算元串的長度。 字串 s.substring(a, b) 的長度為 b–a。 例如, 子串“Hel” 的長度為 3 - 0 = 3。

3.6.2 拼接

與絕大多數的程式設計語言一樣, Java 語言允許使用 + 號連線(拼接) 兩個字串。
String expletive = "Expletive";
String PG13 = "deleted";
String message = expletive + PG13;
上述程式碼將“Expletivedeleted” 賦給變數 message(注意, 單詞之間沒有空格, + 號按照給定的次序將兩個字串拼接起來)。
當將一個字串與一個非字串的值進行拼接時, 後者被轉換成字串(在第 5 章中可以看到, 任何一個 Java 物件都可以轉換成字串)。 例如:
int age = 13;
String rating = "PG" + age;
rating 設定為“PG13”。 這種特性通常用在輸出語句中。 例如:
System.out.println("The answer is " + answer);
這是一條合法的語句, 並且將會打印出所希望的結果(因為單詞 is 後面加了一個空格, 輸出時也會加上這個空格)。

3.6.3 不可變字串

String 類沒有提供用於修改字串的方法。 如果希望將 greeting 的內容修改為“Help!”,不能直接地將 greeting 的最後兩個位置的字元修改為‘p’ 和‘!’ 。 這對於 C 程式設計師來說,將會感到無從下手。 如何修改這個字串呢? 在 Java 中實現這項操作非常容易。 首先提取需要的字元, 然後再拼接上替換的字串:
String geeting = "Hello";
geeting = geeting.substring(0, 3) + "p!";
上面這條語句將 greeting 當前值修改為“Help !”。 由於不能修改 Java 字串中的字元, 所以在 Java 文件中將 String 類物件稱為不可變字串, 如同數字 3 永遠是數字 3 一樣, 字串“Hello” 永遠包含字元 H、 e、 l、 l 和 o 的程式碼單元序列, 而不能修改其中的任何一個字元。  當然, 可以修改字串變數 greeting, 讓它引用另外一個字串, 這就如同可以將存放 3 的數值變數改成存放 4 一樣。

3.6.4 檢測字串是否相等

可以使用 equals 方法檢測兩個字串是否相等。 對於表示式:s.equal(t) 如果字串 s 與字串 t 相等, 則返回 true ; 否則, 返回 false。 s 與 t 可以是字串變數, 也可以是字串常量。 例如, 下列表達式是合法的:
"Hello".equal(greeting)
要想檢測兩個字串是否相等, 而不區分大小寫, 可以使用 equalsIgnoreCase 方法。
"Hello".equalsIgnoreCase(greeting)
一定不能使用 == 運算子檢測兩個字串是否相等! 這個運算子只能夠確定兩個字串是否放置在同一個位置上。 當然, 如果字串放置在同一個位置上, 它們必然相等。 但是,完全有可能將內容相同的多個字串的拷貝放置在不同的位置上。
String geeting = "Hello"; // initialize greeting to a string
if (geeting == "Hello")
   //probably true
if (geeting.substring(0, 3) == "Hel")
   //probably false
如果虛擬機器始終將相同的字串共享, 就可以使用 == 運算子檢測是否相等。 但實際上只有字串常量是共享的, 而 + 或 substring 等操作產生的結果並不是共享的。 因此, 千萬不要使用 == 運算子測試字串的相等性, 以免在程式中出現糟糕的 bug。 從表面上看, 這種bug 很像隨機產生的間歇性錯誤。

3.6.5 空串與Null串

空串 "" 是長度為 0 的字串。 可以呼叫以下程式碼檢查一個字串是否為空: if (str.length() == 0) 或 if (str.equals(""))
空串是一個 Java 物件, 有自己的串長度(0) 和內容(空)。 不過, String 變數還可以存放一個特殊的值, 名為 null, 這表示目前沒有任何物件與該變數關聯(關於 null 的更多資訊請參見第 4 章)。 要檢查一個字串是否為 null, 要使用以下條件:if (str == null) 或 if (str != null && str.length() != 0) 首先要檢查 str 不為 null。 在第 4 章會看到, 如果在一個 null 值上呼叫方法, 會出現錯誤。

3.6.6 程式碼點與程式碼單元

Java 字串由 char 序列組成。 從 3.3.3 節“char 型別” 已經看到, char 資料型別是一個採用 UTF-16 編碼表示 Unicode 程式碼點的程式碼單元。 大多數的常用 Unicode 字元使用一個程式碼單元就可以表示, 而輔助字元需要一對程式碼單元表示。 length 方法將返回採用 UTF-16 編碼表示的給定字串所需要的程式碼單元數量。 例如:
String geeting = "Hello"; 
int n = geeting.length(); // is 5
要想得到實際的長度, 即程式碼點數量, 可以呼叫:
int cpCount = geeting.codePointCount(0, geeting.length());
呼叫 s.charAt(n) 將返回位置 n 的程式碼單元, n 介於 0 ~ s.length()-1 之間。 例如:
char first = geeting.charAt(0); //first is 'H'
char last = geeting.charAt(4); //first is 'o'
要想得到第 i 個程式碼點, 應該使用下列語句
int index = geeting.offsetByCodePoints(0, i);
int cp = geeting.codePointAt(index);
如果想要遍歷一個字串, 並且依次檢視每一個程式碼點, 可以使用下列語句:
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;
可以使用下列語句實現回退操作:
i--;
if (Character.isSurrogate(sentence.charAt(i))) i--;
int cp = sentence.codePointAt(i);

3.6.7 字串API

Java 中的 String 類包含了 50 多個方法。 令人驚訝的是絕大多數都很有用, 可以設想使用的頻繁非常高。 下面的 API 註釋彙總了一部分最常用的方法。
  1. jajava.lang.string va.lang.string 1.0 
    • char charAt (int index) 返回給定位置的程式碼單元。 
    • int codePointAt(int index) 5.0 返回從給定位置開始或結束的程式碼點。
    • int offsetByCodePoints(int startIndex, int cpCount) 5.0 返回從 startIndex 程式碼點開始, 位移 cpCount 後的程式碼點索引。
    •  int compareTo(String other) 按照字典順序, 如果字串位於other之前返回一個負數; 如果字串位於 other 之後, 返回一個正數; 如果兩個字串相等, 返回 0。
    • boolean endsWith(String suffix) 如果字串以 suffix 結尾, 返回 true。
    • boolean equals(Object other) 如果字串與 other 相等, 返回 true。
    •  boolean equalsIgnoreCase(String other) 如果字串與 other 相等(忽略大小寫), 返回 true。
    • ● int index0f(String str)
      ● int index0f(String str, int fromIndex)
      ● int index0f(int cp)
      ● int index0f(int cp, int fromIndex) 返回與字串 str 或程式碼點 cp 匹配的第一個子串的開始位置。 這個位置從索引 0 或 fromIndex 開始計算。 如果在原始串中不存在 str, 返回 –1。
    • ● int lastIndex0f(String str)
      ● int lastIndex0f(String str, int fromIndex)
      ● int lastindex0f(int cp)
      ● int lastindex0f(int cp, int fromIndex) 返回與字串 str 或程式碼點 cp 匹配的最後一個子串的開始位置。 這個位置從原始串尾端或 fromIndex 開始計算。
    • int length( ) 返回字串的長度。
    • int codePointCount(int startIndex, int endIndex) 5.0 返回 startIndex 和 endIndex - 1 之間的程式碼點數量。 沒有配成對的代用字元將計入程式碼點。
    • String replace(CharSequence oldString,CharSequence newString) 返回一個新字串。 這個字串用 newString 代替原始字串中所有的 oldString。 可以用 String 或 StringBuilder 物件作為 CharSequence 引數。
    • boolean startsWith(String prefix) 如果字串以 preffix 字串開始, 返回 true。
    • ● String substring(int beginIndex)
      ● String substring(int beginIndex, int endIndex) 返回一個新字串。 這個字串包含原始字串中從 beginIndex 到串尾或 endIndex–1的所有程式碼單元。
    • String toLowerCase( ) 返回一個新字串。 這個字串將原始字串中的所有大寫字母改成了小寫字母。
    •  String toUpperCase( ) 返回一個新字串。 這個字串將原始字串中的所有小寫字母改成了大寫字母。
    • String trim( ) 返回一個新字串。 這個字串將刪除了原始字串頭部和尾部的空格。

3.6.8 閱讀聯機 API 文件

正如前面所看到的, String 類包含許多方法。 而且, 在標準庫中有幾千個類, 方法數量更加驚人。 要想記住所有的類和方法是一件不太不可能的事情。 因此, 學會使用線上 API 文件十分重要, 從中可以查閱到標準類庫中的所有類和方法。 API 文件是 JDK 的一部分, 它是HTML 格式的。 

3.6.9 構建字串

有些時候, 需要由較短的字串構建字串, 例如, 按鍵或來自檔案中的單詞。 採用字串連線的方式達到此目的效率比較低。 每次連線字串, 都會構建一個新的 String 物件,既耗時, 又浪費空間。 使用 StringBuilder 類就可以避免這個問題的發生。
如果需要用許多小段的字串構建一個字串, 那麼應該按照下列步驟進行。 首先, 構建一個空的字串構建器:
StringBuilder builder = new StringBuilder();
當每次需要新增一部分內容時, 就呼叫 append 方法。
builder.append(ch); // appends a single charcter
builder.append(str); // appends a string
在需要構建字串時就呼叫 toString 方法, 將可以得到一個 String 物件, 其中包含了構建器中的字元序列。
String completedString = builder.toString();
下面的 API 註釋包含了 StringBuilder 類中的重要方法。
  1. java.lang.StringBuilder 5.0
    • StringBuilder() 構造一個空的字串構建器。
    • int length() 返回構建器或緩衝器中的程式碼單元數量。
    • StringBuilder append(String str) 追加一個字串並返回 this。
    • StringBuilder append(char c) 追加一個程式碼單元並返回 this。
    • StringBuilder appendCodePoint(int cp) 追加一個程式碼點, 並將其轉換為一個或兩個程式碼單元並返回 this。
    • void setCharAt(int i,char c) 將第 i 個程式碼單元設定為 c。
    • StringBuilder insert(int offset,String str) 在 offset 位置插入一個字串並返回 this。
    • StringBuilder insert(int offset,Char c) 在 offset 位置插入一個程式碼單元並返回 this。
    • StringBuilder delete(int startIndex,int endIndex) 刪除偏移量從 startIndex 到- endIndex - 1 的程式碼單元並返回 this。
    • String toString() 返回一個與構建器或緩衝器內容相同的字串。

3.7 輸入輸出

為了增加後面示例程式的趣味性, 需要程式能夠接收輸入, 並以適當的格式輸出。 當然, 現代的程式都使用 GUI 收集使用者的輸入, 然而, 編寫這種介面的程式需要使用較多的工具與技術, 目前還不具備這些條件。 主要原因是需要熟悉 Java 程式設計語言, 因此只要有簡單的用於輸入輸出的控制檯就可以了。 第 7 章~第 9 章將詳細地介紹 GUI 程式設計。

3.7.1 讀取輸入

前面已經看到, 列印輸出到“標準輸出流”(即控制檯視窗) 是一件非常容易的事情, 只要呼叫 System.out.println 即可。 然而, 讀取“標準輸入流” System.in 就沒有那麼簡單了。 要想通過控制檯進行輸入, 首先需要構造一個 Scanner 物件, 並與“標準輸入流” System.in 關聯。
Scanner in = new Scanner(System.in);
(構造器和 new 操作符將在第 4 章中詳細地介紹。) 現在, 就可以使用 Scanner 類的各種方法實現輸入操作了。 例如, nextLine 方法將輸入一行。
System.out.print("What is your name? ");
String name = in.nextLine();
在這裡, 使用 nextLine 方法是因為在輸入行中有可能包含空格。 要想讀取一個單詞(以空白符作為分隔符), 就呼叫
String firstName = in.next();
要想讀取一個整數, 就呼叫 nextInt 方法。
System.out.print("How old are your? ");
int age = in.nextInt();
與此類似, 要想讀取下一個浮點數, 就呼叫 nextDouble 方法。
在程式清單 3-2 的程式中, 詢問使用者姓名和年齡, 然後列印一條如下格式的訊息: Hello,Cay. Next year,you'll be 52 最後, 在程式的最開始新增上一行:
import java.util.*;
程式清單 3-2 InputTest/InputTest.java
import java.util.*;

/**
 * This program demonstrates console input.
 * @version 1.10 2004-02-10
 * @author Cay Horstmann
 */
public class InputTest
{
   public static void main(String[] args)
   {
      Scanner in = new Scanner(System.in);

      // get first input
      System.out.print("What is your name? ");
      String name = in.nextLine();

      // get second input
      System.out.print("How old are you? ");
      int age = in.nextInt();

      // display output on console
      System.out.println("Hello, " + name + ". Next year, you'll be " + (age + 1));
   }
}
Scanner 類定義在 java.util 包中。 當使用的類不是定義在基本 java.lang 包中時, 一定要使用import 指示字將相應的包載入進來。 有關包與 import 指示字的詳細描述請參看第 4 章。
註釋: 因為輸入是可見的, 所以 Scanner 類不適用於從控制檯讀取密碼。 Java SE 6