簡單資料型別的取值范圍
byte:8 位,1 位元組,最大資料存盤量是 255,數值范圍是 ?128 ~ 127,short:16 位,2 位元組,最大資料存盤量是 65536,數值范圍是 ?32768 ~ 32767,int:32 位,4 位元組,最大資料存盤容量是 2^32 - 1,數值范圍是 ?2^31 ~ 2^31 - 1,long:64 位,8 位元組,最大資料存盤容量是 2^64 - 1 數值范圍是 ?2^63 ~ 2^63 - 1,float:32 位,4 位元組,數值范圍是 3.4e?45 ~ 1.4e38,直接賦值時必須在數字后加上f或F,double:64 位,8 位元組,數值范圍在 4.9e?324 ~ 1.8e308,賦值時可以加 d 或 D,也可以不加,boolean:只有true和false兩個取值,char:16 位,2 位元組,存盤 Unicode 碼,用單引號'賦值,
字符型

關系運算子和邏輯運算子
在 Java 程式設計中,關系運算子(Relational Operator)和邏輯運算子(Logical Operator)顯得十分重要,關系運算子定義值與值之間的相互關系,邏輯(logical)運算子定義可以用真值和假值連接在一起的方法,
關系運算子
在數學運算中有大于、小于、等于、不等于關系,在程式中可以使用關系運算子來表示上述關系,下圖中列出了 Java 中的關系運算子,通過這些關系運算子會產生一個結果,這個結果是一個布林值,即 true 或 false,在 Java 中,任何型別的資料,都可以用 == 比較是不是相等,用 != 比較是否不相等,只有數字才能比較大小,關系運算的結果可以直接賦予布爾變數,

邏輯運算子
布爾邏輯運算子是最常見的邏輯運算子,用于對布爾型運算元進行布爾邏輯運算,Java 中的布爾邏輯運算子如下圖示,

邏輯運算子與關系運算子運算后得到的結果一樣,都是布爾型別的值,在 Java 程式設計中,&& 和 || 布爾邏輯運算子不總是對運算子右邊的運算式求值,如果使用邏輯與 & 和邏輯或 |,則運算式的結果可以由運算子左邊的運算元單獨決定,通過下表,同學們可以了解常用邏輯運算子 &&、||、! 運算后的結果,

位邏輯運算子
位邏輯運算子在 Java 程式設計中,使用位邏輯運算子來操作二進制資料,讀者必須注意,位邏輯運算子只能操作二進制資料,如果用在其他進制的資料中,需要先將其他進制的資料轉換成二進制資料,位邏輯運算子(Bitwise Operator)可以直接操作整數型別的位,這些整數型別包括 long、int、short、char 和 byte,Java 語言中位邏輯運算子的具體說明如下表所示,

因為位邏輯運算子能夠在整數范圍內對位操作,所以這樣的操作對一個值產生什么效果是很重要的,具體來說,了解 Java 如何存盤整數值并且如何表示負數是非常有用的,下表中演示了運算元 A 和運算元 B 按位邏輯運算的結果,

移位運算子把數字的位向右或向左移動,產生一個新的數字,Java 的右移運算子有兩個,分別是 >> 和 >>>,
>>運算子:把第一個運算元的二進制碼右移指定位數后,將左邊空出來的位以原來的符號位填充,即,如果第一個運算元原來是正數,則左邊補 0;如果第一個運算元是負數,則左邊補 1,>>>:把第一個運算元的二進制碼右移指定位數后,將左邊空出來的位以 0 填充,
條件運算子
條件運算子是一種特殊的運算子,也被稱為三目運算子,它與前面所講的運算子有很大不同,Java 中提供了一個三目運算子,其實這跟后面講解的 if 陳述句有相似之處,條件運算子的目的是決定把哪個值賦給前面的變數,在 Java 語言中使用條件運算子的語法格式如下所示,
變數 = (布爾運算式) ? 為 true 時賦予的值 : 為 false 時賦予的值;
賦值運算子
注意:在 Java 中可以對賦值運算子進行擴展,其中最為常用的有如下擴展操作,
另外,在后面的學習中我們會接觸到
equals()方法,此方法和賦值運算子==的功能類似,要想理解兩者之間的區別,我們需要從變數說起,Java 中的變數分為兩類,一類是值型別,它存盤的是變數真正的值,比如基礎資料型別,值型別的變數存盤在記憶體的堆疊中;另一類是參考型別,它存盤的是物件的地址,與該地址對應的記憶體空間中存盤的才是我們需要的內容,比如字串和物件等,參考型別的變數存盤在記憶體中的堆中,賦值運算子==比較的是值型別的變數,如果比較兩個參考型別的變數,比較的就是它們的參考地址,equals()方法只能用來比較參考型別的變數,也就是比較參考的內容,
==運算子比較的是左右兩邊的變數是否來自同一個記憶體地址,如果比較的是值型別(基礎資料型別,如int和char之類)的變數,由于值型別的變數存盤在堆疊里面,當兩個變數有同一個值時,其實它們只用到同一個記憶體空間,所以比較的結果是true,
eqluals()方法是 Object 類的基本方法之一,所以每個類都有自己的equals()方法,功能是比較兩個物件是否是同一個,通俗的理解就是比較這兩個物件的內容是否一樣,
賦值運算子是等號 =,Java 中的賦值運算與其他計算機語言中的賦值運算一樣,起到賦值的作用,在 Java 中使用賦值運算子的格式如下所示,
+=:對于x+=y,等效于x=x+y,-=:對于x-=y,等效于x=x?y,*=:對于x*=y,等效于x=x*y,/=:對于x/=y,等效于x=x/y,%=:對于x%=y,等效于x=x%y,&=:對于x&=y,等效于x=x&y,|=:對于x|=y,等效于x=x|y,^=:對于x^=y,等效于x=x^y,<<=:對于x<<=y,等效于x=x<<y,>>=:對于x>>=y,等效于x=x>>y,>>>=:對于x>>>=y,等效于x=x>>>y,
其中,變數 var 的型別必須與運算式 expression 的型別一致, 賦值運算子有一個有趣的屬性,它允許我們對一連串變數進行賦值,請看下面的代碼, 在上述代碼中,使用一條賦值陳述句將變數 x、y、z 都賦值為 100,這是由于 = 運算子表示右邊運算式的值,因此 z = 100 的值是 100,然后該值被賦給 y,并依次被賦給 x,使用字串賦值是給一組變數賦予同一個值的簡單辦法,在賦值時型別必須匹配,否則將會出現編譯錯誤,
運算子的優先級
數學中的運算都是從左向右運算的,在 Java 中除了單目運算子、賦值運算子和三目運算子外,大部分運算子也是從左向右結合的,單目運算子、賦值運算子和三目運算子是從右向左結合的,也就是說,它們是從右向左運算的,乘法和加法是兩個可結合的運算,也就是說,這兩個運算子左右兩邊的運算子可以互換位置而不會影響結果,
運算子有不同的優先級,所謂優先級,就是在運算式運算中的運算順序,下表中列出了包括分隔符在內的所有運算子的優先級,上一行中的運算子總是優先于下一行的,

字串的初始化
在 Java 程式中,使用關鍵字 new 來創建 String 實體,具體格式如下所示,
String a = new String();
上面這行代碼創建了一個名為 a 的 String 類的實體,并把它賦給變數,但它此時是一個空的字串,接下來就為這個字串復制,賦值代碼如下所示,
a = "I am a person.";
在 Java 程式中,我們將上述兩句代碼合并,就可以產生一種簡單的字串表示方法,
String s = new String("I am a person.");
除了上面的表示方法,還有表示字串的如下一種形式,
String s = ("I am a person.");
String 類
在 Java 程式中可以使用 String 類來操作字串,在該類中有許多方法可以供程式員使用,
索引
在 Java 程式中,通過索引函式 charAt() 可以回傳字串中指定索引的位置,讀者需要注意的是,這里的索引數字從零開始,使用格式如下所示,
public char charAt(int index)
追加字串
追加字串函式 concat() 的功能是在字串的末尾添加字串,追加字串是一種比較常用的操作,具體語法格式如下所示,
public String concat(String s)
StringBuffer 類
StringBuffer 類是 Java 中另一個重要的操作字串的類,當需要對字串進行大量的修改時,使用 StringBuffer 類是最佳選擇,接下來將詳細講解 StringBuffer 類中的常用方法,
追加字符
在 StringBuffer 類中實作追加字符功能的方法的語法格式如下所示,
public synchronized StringBuffer append(char b)
插入字符
前面的字符追加方法總是在字串的末尾添加內容,倘若需要在字串中添加內容,就需要使用方法 insert(),語法格式如下所示,
public synchronized StringBuffer insert(int offset, String s)
上述語法格式的含義是:將第 2 個引數的內容添加到第 1 個引數指定的位置,換句話說,第 1 個引數表示要插入的起始位置,第 2 個引數是需要插入的內容,可以是包括 String 在內的任何資料型別,
顛倒字符
字符顛倒方法能夠將字符顛倒,例如 "我是誰",顛倒過來就變成 "誰是我",很多時候需要顛倒字符,字符顛倒方法 reverse() 的語法格式如下所示,
public synchronized StringBuffer reverse()
自動型別轉換
如果系統支持把某種基本型別的值直接賦給另一種基本型別的變數,這種方式被稱為自動型別轉換,當把一個取值范圍小的數值或變數直接賦給另一個取值范圍大的變數時,系統可以進行自動型別轉換,
Java 中所有數值型變數之間可以進行型別轉換,取值范圍小的可以向取值范圍大的進行自動型別轉換,就好比有兩瓶水,當把小瓶里的水倒入大瓶時不會有任何問題,Java 支持自動型別轉換的型別如下圖所示,

在上圖所示的型別轉換圖中,箭頭左邊的數值可以轉換為箭頭右邊的數值,當對任何基本型別的值和字串進行連接運算時,基本型別的值將自動轉換為字串型別,盡管字串型別不再是基本型別,而是參考型別,因此,如果希望把基本型別的值轉換為對應的字串,可以對基本型別的值和一個空字串進行連接,
Java 11 新特性:新增的 String 函式
在新發布的 JDK 11 中,新增了 6 個字串函式,下面介紹各個字串函式,
-
String.repeat(int)函式
String.repeat(int)的功能是根據 int 引數的值重復 String, -
String.lines()函式
String.lines()的功能是回傳從該字串中提取的行,由行終止符分隔,行要么是零個或多個字符的序列,后面跟著一個行結束符;要么是一個或多個字符的序列,后面是字串的結尾,一行不包括行終止符,在 Java 程式中,使用函式String.lines()回傳的流包含該字串中出現的行的順序, -
String.strip()函式
String.strip()的功能是回傳一個字串,該字串的值為該字串,其中所有前導和尾部空白均被洗掉,如果該 String 物件表示空字串,或者如果該字串中的所有代碼點是空白的,則回傳一個空字串,否則,回傳該字串的子字串,該字串從第一個不是空白的代碼點開始,直到最后一個不是空白的代碼點,并包括最后一個不是空白的代碼點,在 Java 程式中,開發者可以使用此函式去除字串開頭和結尾的空白, -
String.stripLeading()函式
String.stripLeading()的功能是回傳一個字串,其值為該字串,并且洗掉字串前面的所有空白,如果該 String 物件表示空字串,或者如果該字串中的所有代碼點是空白的,則回傳空字串, -
String.stripTrailing()函式
String.stripTrailing()的功能是回傳一個字串,其值為該字串,并且洗掉字串后面的所有空白,如果該 String 物件表示空字串,或者如果該字串中的所有代碼點是空白的,則回傳空字串, -
String.isBlank()函式
String.isBlank()的功能是判斷字串是否為慷訓僅包含空格,如果字串為慷訓僅包含空格則回傳true;否則,回傳false,
定義常量時的注意事項
在 Java 語言中,主要利用 final 關鍵字(在 Java 類中靈活使用 static 關鍵字)來進行 Java 常量的定義,當常量被設定后,一般情況下就不允許再進行更改,在定義常量時,需要注意如下 3 點,
- 在定義 Java 常量的時候,就需要對常量進行初始化,也就是說,必須在宣告常量時就對它進行初始化,跟區域變數或類成員變數不同,在定義一個常量的時候,進行初始化之后,在應用程式中就無法再次對這個常量進行賦值,如果強行賦值的話,編譯器會彈出錯誤資訊,并拒絕接受這一新值,
- 需要注意
final關鍵字的使用范圍,final關鍵字不僅可以用來修飾基本資料型別的常量,還可以用來修飾物件的參考或方法,比如陣列就是物件參考,為此,可以使用final關鍵字定義一個常量的陣列,這是 Java 語言中的一大特色,一個陣列物件一旦被final關鍵字設置為常量陣列之后,它就只能恒定地指向一個陣列物件,無法將其指向另一個物件,也無法更改陣列中的值, - 需要注意常量的命名規則,在定義變數或常量時,不同的語言,都有自己的一套編碼規則,這主要是為了提高代碼的共享程度與易讀性,在 Java 中定義常量時,也有自己的一套規則,比如在給常量取名時,一般都用大寫字母,在 Java 語言中,區分大小寫字母,之所以采用大寫字母,主要是為了跟變數進行區分,雖然說給常量取名時采用小寫字母,也不會有語法上的錯誤,但是為了在撰寫代碼時能夠一目了然地判斷變數與常量,最好還是能夠將常量設定為大寫字母,另外,在常量中,往往通過下劃線來分隔不同的字符,而不像物件名或類名那樣,通過首字母大寫的方式來進行分隔,這些規則雖然不是強制性的,但是為了提高代碼的友好性,方便開發團隊中的其他成員閱讀,這些規則還是需要遵守的, 總之,Java 開發人員需要注意,被定義為
final的常量需要采用大寫字母命名,并且中間最好使用下劃線作為分隔符來連接多個單詞,定義為final的資料不論是常量、物件參考還是陣列,在主函式中都不可以改變,否則會被編輯器拒絕并提示錯誤資訊,
char 型別中單引號的意義
char 型別使用單引號括起來,而字串使用雙引號括起來,關于 String 類的具體用法以及對應的各個方法,讀者可以參考查閱 API 檔案中的資訊,其實 Java 語言中的單引號、雙引號和反斜線都有特殊的用途,如果在一個字串中包含這些特殊字符,應該使用轉義字符,
例如希望在 Java 程式中表示絕對路徑 c:\daima,但這種寫法得不到我們期望的結果,因為 Java 會把反斜線當成轉義字符,所以應該寫成 c:\\daima 的形式,只有同時寫兩個反斜線,Java 才會把第一個反斜線當成轉義字符,與后一個反斜線組成真正的反斜線,
正無窮和負無窮的問題
Java 還提供了 3 個特殊的浮點數值——正無窮大、負無窮大和非數,用于表示溢位和出錯,
例如,使用一個正浮點數除以 0 將得到正無窮大,使用一個負浮點數除以 0 將得到負無窮大,用 0.0 除以 0.0 或對一個負數開方將得到一個非數,
正無窮大通過 Double 或 Float 的 POSITIVE_INFINITY 表示,負無窮大通過 Double 或 Float 的 NEGATIVE_INFINITY 表示,非數通過 Double 或 Float 的 NaN 表示,
請注意,只有用浮點數除以 0 才可以得到正無窮大或負無窮大,因為 Java 語言會自動把和浮點數運算的 0(整數)當成 0.0(浮點數)來處理,如果用一個整數除以 0,則會拋出 “ArithmeticException:/by zero”(除以 0 例外),
移位運算子的限制
Java 移位運算子只能用于整型,不能用于浮點型,也就是說,>>、>>>和<<這 3 個移位運算子并不適合所有的數值型別,它們只適合對 byte、short、char、int 和 long 等整型數進行運算,除此之外,進行移位運算時還有如下規則:
- 對于低于
int型別(如byte、short和char)的運算元來說,總是先自動型別轉換為int型別后再移位, - 對于
int型別的整數移位,例如a >> b,當b > 32時,系統先用b對 32 求余(因為int型別只有 32 位),得到的結果才是真正移位的位數,例如,a >> 33和a >> l的結果完全一樣,而a >> 32的結果和a相同, - 對
long型別的整數移位時,例如a >> b,當b > 64時,總是先用b對 64 求余(因為long型別是 64 位),得到的結果才是真正移位的位數, 當進行位移運算時,只要被移位的二進制碼沒有發生有效位的數字丟失現象(對于正數而言,通常指被移出的位全部都是 0),不難發現左移 n 位就相當于乘以 2n,右移則相當于除以 2n,這里存在一個問題:左移時,左邊舍棄的位通常是無效的,但右移時,右邊舍棄的位常常是有效的,因此通過左移和右移更容易看出這種運行結果,并且位移運算不會改變運算元本身,只是得到一個新的運算結果,原來的運算元本身是不會改變的,
if 陳述句
if 陳述句由保留字 if、條件陳述句和位于后面的陳述句組成,條件陳述句通常是一個布爾運算式,結果為 true 和 false,如果條件為 true,則執行陳述句并繼續處理其后的下一條陳述句;如果條件為 false,則跳過陳述句并繼續處理緊跟整個 if 陳述句的下一條陳述句,例如在下圖中,當條件(condition)為 true 時,執行 statement1 陳述句;當條件為 false 時,執行 statement2 陳述句,

if 陳述句的語法格式如下所示,
if (條件運算式)
語法說明:if 是該陳述句中的關鍵字,后續緊跟一對小括號,這對小括號任何時候都不能省略,小括號的內部是具體的條件,語法上要求條件運算式的結果為 boolean 型別,后續為功能代碼,也就是當條件成立時執行的代碼,在書寫程式時,一般為了直觀地表達包含關系,功能代碼需要縮進,
例如下面的演示代碼,
int a = 10; //定義 int 型變數 a 的初始值是 10
if (a >= 0)
System.out.println("a 是正數"); //a 大于或等于 0 時的輸出內容
if ( a % 2 == 0)
System.out.println("a 是偶數"); //a 能夠整除 2 時的輸出內容
在上述演示代碼中,第一個條件判斷變數 a 的值是否大于或等于零,如果該條件成立,輸出 "a 是正數";第二個條件判斷變數 a 是否為偶數,如果成立,也輸出 "a 是偶數",
再看下面的代碼的執行流程,
int m = 20; //定義 int 型變數 m 的初始值是 20
if ( m > 20) //如果變數 m 的值大于 20
m += 20; //將 m 的值加上 20
System.out.println(m); //輸出 m 的值
按照前面的語法格式說明,只有 m += 20 這行代碼屬于功能代碼,而后續的輸出陳述句和前面的條件形成順序結構,所以該程式執行以后輸出的結果為 20,當條件成立時,如果需要執行的陳述句有多句,可以使用陳述句塊來進行表述,具體語法格式如下所示,
if (條件運算式) {
功能代碼塊;
}
這種語法格式中,使用功能代碼塊來代替前面的功能代碼,這樣可以在代碼塊內部書寫任意多行代碼,而且也使整個程式的邏輯比較清楚,所以在實際的代碼撰寫中推薦使用這種方式,
if陳述句的延伸
在第一種 if 陳述句中,大家可以看到,并不對條件不符合的內容進行處理,因為這是不允許的,所以 Java 引入了另外一種條件陳述句 if…else,基本語法格式如下所示,
if (condition) // 設定條件 condition
statement1; // 如果條件 condition 成立,執行 statement1 這一行代碼
else // 如果條件 condition 不成立
statement2; // 執行 statement2 這一行代碼
if…else 陳述句的執行流程如下圖所示,

有多個條件判斷的 if 陳述句
if 陳述句實際上是一種功能十分強大的條件陳述句,可以對多種情況進行判斷,可以判斷多個條件的陳述句是 if-else-if,語法格式如下所示,
if (condition1)
statement1;
else if (condition2)
statement2;
else
statement3;
上述語法格式的執行流程如下,
- 判斷第一個條件 condition1,當為
true時執行 statement1,并且程式運行結束,當 condition1 為false時,繼續執行后面的代碼, - 當 condition1 為
false時,接下來先判斷 condition2 的值,當 condition2 為true時執行 statement2,并且程式運行結束,當 condition2 為false時,執行后面的 statement3,也就是說,當前面的兩個條件 condition1 和 condition2 都不成立(為false)時,才會執行 statement3,
if-else-if 的執行流程如下圖所示,

在 Java 陳述句中,if…else 可以嵌套無限次,可以說,只要遇到值為 true 的條件,就會執行對應的陳述句,然后結束整個程式的運行,
switch 陳述句的形式
switch 陳述句能夠對條件進行多次判斷,具體語法格式如下所示,
switch(整數選擇因子) {
case 整數值 1 : 陳述句; break;
case 整數值 2 : 陳述句; break;
case 整數值 3 : 陳述句; break;
case 整數值 4 : 陳述句; break;
case 整數值 5 : 陳述句; break;
//..
case 整數值 n : 陳述句; break;
default: 陳述句;
}
其中,整數選擇因子 必須是 byte、short、int 和 char 型別,每個整數必須是與 整數選擇因子 型別兼容的一個常量,而且不能重復,整數選擇因子 是一個特殊的運算式,能產生整數,switch 能將整數選擇因子的結果與每個整數做比較,發現相符的,就執行對應的陳述句(簡單或復合陳述句),沒有發現相符的,就執行 default 陳述句,
在上面的定義中,大家會注意到每個 case 均以一個 break 結尾,這樣可使執行流程跳轉至 switch 主體的末尾,這是構建 switch 陳述句的一種傳統方式,但 break 是可選的,若省略 break,將會繼續執行后面的 case 陳述句的代碼,直到遇到 break 為止,盡管通常不想出現這種情況,但對有經驗的程式員來說,也許能夠善加利用,注意,最后的 default 陳述句沒有 break,因為執行流程已到達 break 的跳轉目的地,當然,如果考慮到編程風格方面的原因,完全可以在 default 陳述句的末尾放置一個 break,盡管它并沒有任何實際用處,
switch 陳述句的執行流程如下圖所示,

無 break 的情況
多次出現了 break 陳述句,其實在 switch 陳述句中可以沒有 break 這個關鍵字,一般來說,當 switch 遇到一些 break 關鍵字時,程式會自動結束 switch 陳述句,如果把 switch 陳述句中的 break 關鍵字去掉了,程式將繼續向下執行,直到整個 switch 陳述句結束,
case 陳述句后沒有執行陳述句
當 case 陳述句后沒有執行陳述句時,即使條件為 true,也會忽略掉不執行,
default 可以不在結尾
通過前面的學習,很多初學者可能會誤認為 default 一定位于 switch 的結尾,其實不然,它可以位于 switch 中的任意位置
for 回圈
在 Java 程式中,for 陳述句是最為常見的一種回圈陳述句,for 回圈是一種功能強大且形式靈活的結構,下面對它進行講解,
書寫格式
for 陳述句是一種十分常見的回圈陳述句,語法格式如下所示,
for(initialization;condition;iteration){
statements;
}
從上面的語法格式可以看出,for 回圈陳述句由如下 4 部分組成,
initialization:初始化操作,通常用于初始化回圈變數,condition:回圈條件,是一個布爾運算式,用于判斷回圈是否持續,iteration:回圈迭代器,用于迭代回圈變數,statements:要回圈執行的陳述句(可以有多條陳述句),
上述每一部分間都用分號分隔,如果只有一條陳述句需要重復執行,大括號就沒有必要有了,
在 Java 程式中,for 回圈的執行程序如下,
- 當回圈啟動時,先執行初始化操作,通常這里會設定一個用于主導回圈的回圈變數,重要的是要理解初始化運算式僅被執行一次,
- 計算回圈條件,
condition必須是一個布爾運算式,它通常會對回圈變數與目標值做比較,如果這個布爾運算式為真,則繼續執行回圈體statements;如果為假,則回圈終止, - 執行回圈迭代器,這部分通常是用于遞增或遞級訓圈變數的一個運算式,以便接下來重新計算回圈條件,判斷是否繼續回圈,
while 回圈陳述句
在 Java 程式里,除了 for 陳述句以外,while 陳述句也是十分常見的回圈陳述句,其特點和 for 陳述句十分類似,while 回圈陳述句的最大特點,就是不知道回圈多少次,在 Java 程式中,當不知道某個陳述句塊或陳述句需要重復運行多少次時,通過使用 while 陳述句可以實作這樣的回圈功能,當回圈條件為真時,while 陳述句重復執行一條陳述句或某個陳述句塊,while 陳述句的基本使用格式如下所示,
while (condition) // condition 運算式是回圈條件,其結果是一個布林值
{
statements;
}
while 陳述句的執行流程如下圖所示,

do...while 回圈陳述句
許多軟體程式中會存在這種情況:當條件為假時也需要執行陳述句一次,初學者可以這么理解,在執行一次回圈后才測驗回圈的條件運算式,在 Java 語言中,我們可以使用 do…while 陳述句實作上述回圈,
書寫格式
在 Java 語言中,do…while 回圈陳述句的特點是至少會執行一次回圈體,因為條件運算式在回圈的最后,do…while 回圈陳述句的使用格式如下所示,
do{
statements;
}
while (condition) // condition 表示回圈條件,是一個布林值
在上述格式中,do…while 陳述句先執行 statement 一次,然后判斷回圈條件,如果結果為真,回圈繼續;如果為假,回圈結束,
do…while 回圈陳述句的執行流程如下圖所示,

break 陳述句的應用
在本小節前面的內容中,我們事實上已經接觸過 break 陳述句,了解到它在 switch 陳述句里可以終止一條陳述句,其實除這個功能外,break 還能實作其他功能,例如退出回圈,break 陳述句根據用戶使用的不同,可以分為無標號退出回圈和有標號退出回圈兩種,
無標號退出回圈是指直接退出回圈,當在回圈陳述句中遇到 break 陳述句時,回圈會立即終止,回圈體外面的陳述句也將會重新開始執行,
return 陳述句的使用
在 Java 程式中,使用 return 陳述句可以回傳一個方法的值,并把控制權交給呼叫它的陳述句,return 陳述句的語法格式如下所示,
return [expression];
expression 表示運算式,是可選引數,表示要回傳的值,它的資料型別必須同方法宣告中回傳值的型別一致,這可以通過強制型別轉換實作,
在撰寫 Java 程式時,return 陳述句如果被放在方法的最后,它將用于退出當前的程式,并回傳一個值,如果把單獨的 return 陳述句放在一個方法的中間,會出現編譯錯誤,如果非要把 return 陳述句放在方法的中間,可以使用條件陳述句 if,然后將 return 陳述句放在這個方法的中間,用于實作將程式中未執行的全部陳述句退出,
continue 陳述句
在 Java 語言中,continue 陳述句不如前面幾種跳轉陳述句應用得多,其作用是強制當前這輪迭代提前回傳,也就是讓回圈繼續執行,但不執行當前迭代中 continue 陳述句生效之后的陳述句,
使用 for 回圈的技巧
控制 for 回圈的變數經常只用于該回圈,而不用在程式的其他地方,在這種情況下,可以在回圈的初始化部分宣告變數,當我們在 for 回圈內宣告變數時,必須記住重要的一點:該變數的作用域在 for 回圈執行后就結束了(因此,該變數的作用域僅限于 for 回圈內),由于回圈控制變數不會在程式的其他地方使用,因此大多數程式員都在 for 回圈中宣告它,
另外,初學者經常以為,只要在 for 后面的括號中控制了回圈迭代陳述句,就萬無一失了,其實不是這樣的,請看下面的代碼,
public class TestForError {
public static void main(String[] args) {
// 回圈的初始化條件、回圈條件、回圈迭代陳述句都在下面一行
for (int count = 0; count < 10; count++) {
System.out.println(count);
// 再次修改了回圈變數
count *= 0.1;
}
System.out.println("回圈結束!");
}
}
在上述代碼中,我們在回圈體內修改了 count 變數的值,并且把這個變數的值乘以 0.1,這會導致 count 的值永遠都不超過 10,所以上述程式是一個死回圈,
其實在使用 for 回圈時,還可以把初始化條件定義在回圈體之外,把回圈迭代陳述句放在回圈體內,把 for 回圈的初始化陳述句放在回圈之前定義還有一個好處,那就是可以擴大初始化陳述句中定義的變數的作用域,在 for 回圈里定義的變數,其作用域僅在該回圈內有效,for 回圈終止以后,這些變數將不可被訪問,
跳轉陳述句的選擇技巧
由此可見,continue 的功能和 break 有點類似,區別在于 continue 只是中止當前迭代,接著開始下一次迭代;而 break 則完全終止回圈,我們可以將 continue 的作用理解為:略過當前迭代中剩下的陳述句,重新開始新一輪的迭代,
宣告一維陣列
陣列本質上就是某類元素的集合,每個元素在陣列中都擁有對應的索引值,只需要指定索引值就可以取出對應的資料,在 Java 中宣告一維陣列的格式如下所示,
int[] arrar;
也可以用下面的格式,
int array[];
雖然這兩種格式的形式不同,但含義是一樣的,各個引數的具體說明如下,
int:陣列型別,array:陣列名稱,[]:一維陣列的內容通過這個符號括起來,
除上面宣告的整型陣列外,還可以宣告多種資料型別的陣列,例如下面的代碼,
boolean[] array; // 宣告布爾型陣列
float[] array; // 宣告浮點型陣列
double[] array; // 宣告雙精度型陣列
創建一維陣列
創建陣列實質上就是為陣列申請相應的存盤空間,陣列的創建需要用大括號 {} 括起來,然后將一組相同型別的資料放在存盤空間里,Java 編譯器負責管理存盤空間的分配,創建一維陣列的方法十分簡單,具體格式如下所示,
int[] a = {1,2,3,5,8,9,15};
上述代碼創建了一個名為 a 的整型陣列,但是為了訪問陣列中的特定元素,應指定陣列元素的位置序號,也就是索引(又稱下標),一維陣列的內部結構如下圖所示,

上面這個陣列的名稱是 a,方括號的數值表示陣列元素的索引,這個序號通常也被稱為下標,這樣就可以很清楚地表示每一個陣列元素,陣列 a 的第一個值就用 a[0] 表示,第 2 個值就用 a[1] 表示,以此類推,
初始化一維陣列
在 Java 程式里,一定要將陣列看作一個物件,它的資料型別和前面的基本資料型別相同,很多時候我們需要對陣列進行初始化處理,在初始化的時候需要規定陣列的大小,當然,也可以初始化陣列中的每一個元素,下面的代碼演示了 3 種初始化一維陣列的方法,
int[] a = new int[8]; // 使用 new 關鍵字創建一個含有 8 個元素的 int 型別的陣列 a
int[] a = new int{1,2,3,4,5,6,7,8}; // 初始化并設定陣列 a 中的 8 個陣列元素
int[] a = {1,2,3,4}; // 初始化并設定陣列 a 中的 4 個陣列元素
對上面代碼的具體說明如下所示,
int:陣列型別,a:陣列名稱,new:物件初始化陳述句,
在初始化陣列的時候,當使用關鍵字 new 創建陣列后,一定要明白它只是一個參考,直到將值賦給參考,開始進行初始化操作后才算真正結束,在上面 3 種初始化陣列的方法中,同學們可以根據自己的習慣選擇一種初始化方法,
宣告二維陣列
在前面已經學習了宣告一維陣列的知識,宣告二維陣列也十分簡單,因為它與宣告一維陣列的方法十分相似,很多程式員習慣將二維陣列看作一種特殊的一維陣列,其中的每個元素又是一個陣列,宣告二維陣列的語法格式如下所示,
float A[][]; //float 型別的二維陣列 A
char B[][]; //char 型別的二維陣列 B
int C[][]; //int 型別的二維陣列 C
上述代碼中各個引數的具體說明如下所示,
float、char和int:表示陣列和型別,A、B和C:表示陣列的名稱,[][]:二維陣列的內容通過這個符號括起來,
創建二維陣列的程序,實際上就是在計算機上申請一塊存盤空間的程序,例如下面是創建二維陣列的代碼,
int A[][]={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
上述代碼創建了一個二維陣列,A 是陣列名,實質上這個二維陣列相當于一個 3 行 4 列的矩陣,當需要獲取二維陣列中的值時,可以使用索引來顯示,具體格式如下所示,
array[i - 1][j - 1];
上述代碼中各個引數的具體說明如下所示,
i:陣列的行數,j:陣列的列數,
下面以一個二維陣列為例,看一下 3 行 4 列的陣列的內部結構,如下圖所示,

初始化二維陣列
初始化二維陣列的方法非常簡單,可以看作是由多個一維陣列構成,也是使用下面的語法格式實作的,
array = new int[][]{
{第一個陣列第一個元素的值,第一個陣列第二個元素的值,第一個陣列第三個元素的值},
{第二個陣列第一個元素的值,第二個陣列第二個元素的值,第二個陣列第三個元素的值},
};
或者使用 new 關鍵字,
array = new int[3][4]; // 創建一個 3 行 4 列的陣列,
使用 new 關鍵字初始化的陣列,其所有元素的值都默認為該陣列的型別的默認值,這里是 int 型別陣列,則默認值為 0.
上述代碼中各個引數的具體說明如下所示,
array:陣列名稱,new:物件實體化陳述句,int:陣列型別,
宣告三維陣列
宣告三維陣列的方法十分簡單,與宣告一維、二維陣列的方法相似,具體格式如下所示,
float a[][][];
char b[][][];
上述代碼中各個引數的具體說明如下所示,
float和char:陣列型別,a和b:陣列名稱,
創建三維陣列的方法
在 Java 程式中,創建三維陣列的方法也十分簡單,例如下面的代碼,
int [][][] a = new int[2][2][3];
初始化三維陣列
初始化三維陣列的方法十分簡單,例如可以用下面的代碼初始化一個三維陣列,
int[][][]a={
//初始化三維陣列
{{1,2,3}, {4,5,6}}
{{7,8,9},{10,11,12}}
}
通過上述代碼,可以定義并且初始化三維陣列中元素的值,
復制陣列
復制陣列是指復制陣列中的數值,在 Java 中可以使用 System 的方法 arraycopy() 實作陣列復制功能,方法 arraycopy() 有兩種語法格式,其中第一種語法格式如下所示,
System.arraycopy(arrayA,indexA,arrayB,indexB,a.length);
arrayA:來源陣列名稱,indexA:來源陣列的起始位置,arryaB:目的陣列名稱,indexB:要從來源陣列復制的元素個數,
上述陣列復制方法 arraycopy() 有一定局限,可以考慮使用方法 arraycopy() 的第二種格式,使用第二種格式可以復制陣列內的任何元素,第二種語法格式如下所示,
System.arraycopy(arrayA,2,arrayB,3,3);
arrayA:來源陣列名稱,2:來源陣列從起始位置開始的第2個元素,arrayB:目的陣列名稱,3:目的陣列從其實位置開始的第3個元素,3:從來源陣列的第 2 個元素開始復制3個元素,
比較陣列
比較陣列就是檢查兩個陣列是否相等,如果相等,則回傳布林值 true;如果不相等,則回傳布林值 false,在 Java 中可以使用方法 equals() 比較陣列是否相等,具體格式如下所示,
Arrays.equals(arrayA,arrayB);
arrayA:待比較陣列的名稱,arrayB:待比較陣列的名稱,
如果兩個陣列相等,就會回傳 true;否則回傳 false,
排序陣列
排序陣列是指對陣列內的元素進行排序,在 Java 中可以使用方法 sort() 實作排序功能,并且排序規則是默認的,方法 sort() 的語法格式如下所示,
Arrays.sort(array);
引數 array 是待排序陣列的名稱,
搜索陣列中的元素
在 Java 中可以使用方法 binarySearch() 搜索陣列中的某個元素,語法格式如下所示,
int i = binarySearch(a,"abcde");
a:要搜索的陣列名稱,abcde:需要在陣列中查找的內容,
填充陣列
在 Java 程式設計里,可以使用 fill() 方法向陣列中填充元素,fill() 方法的功能十分有限,只能使用同一個數值進行填充,使用 fill() 方法的語法格式如下所示,
int a[] = new int[10];
Arrays.fill(array,11);
其中,引數 a 是將要填充的陣列的名稱,上述格式的含義是將數值 11 填充到陣列 a 中,
遍歷陣列
在 Java 語言中,foreach 陳述句是從 Java 1.5 開始出現的新特征之一,在遍歷陣列和遍歷集合方面,foreach 為開發人員提供極大的方便,從實質上說,foreach 陳述句是 for 陳述句的特殊簡化版本,雖然 foreach 陳述句并不能完全取代 for 陳述句,但是任何 foreach 陳述句都可以改寫為 for 陳述句版本,
foreach 并不是一個關鍵字,習慣上將這種特殊的 for 陳述句稱為 foreach 陳述句,從英文字面意思理解,foreach 就是「為每一個」的意思,foreach 陳述句的語法格式如下所示,
for(type 變數 x : 遍歷物件 obj){
參考了 x 的 Java 陳述句;
}
其中,type 是陣列元素或集合元素的型別,變數 x 是一個形參,foreach 回圈自動將陣列元素、集合元素依次賦給變數 x,
動態初始化陣列的規則
在執行動態初始化時,程式員只需要指定陣列的長度即可,即為每個陣列元素指定所需的記憶體空間,系統將負責為這些陣列元素分配初始值,在指定初始值時,系統按如下規則分配初始值,
- 陣列元素的型別是基本型別中的整數型別(
byte、short、int和long),陣列元素的值是 0, - 陣列元素的型別是基本型別中的浮點型別(
float、double),陣列元素的值是 0.0, - 陣列元素的型別是基本型別中的字符型別(
char),陣列元素的值是 '\u0000', - 陣列元素的型別是基本型別中的布爾型別(
boolean),陣列元素的值是false, - 陣列元素的型別是參考型別(類、介面和陣列),陣列元素的值是
null,
參考型別
如果記憶體中的一個物件沒有任何參考的話,就說明這個物件已經不再被使用了,從而可以被垃圾回收,不過由于垃圾回收器的運行時間不確定,可被垃圾回收的物件實際被回收的時間是不確定的,對于一個物件來說,只要有參考存在,它就會一直存在于記憶體中,如果這樣的物件越來越多,超出 JVM 中的記憶體總數,JVM 就會拋出 OutOfMemory 錯誤,雖然垃圾回收器的具體運行是由 JVM 控制的,但是開發人員仍然可以在一定程度上與垃圾回收器進行互動,目的在于更好地幫助垃圾回收器管理好應用的記憶體,這種互動方式就是從 JDK 1.2 開始引入的 java.lang.ref 包,
強參考
在一般的 Java 程式中,見到最多的就是強參考(strong reference),例如 Date date = new Date(),其中的 date 就是一個物件的強參考,物件的強參考可以在程式中到處傳遞,很多情況下,會同時有多個參考指向同一個物件,強參考的存在限制了物件在記憶體中的存活時間,假如物件 A 中包含物件 B 的一個強參考,那么一般情況下,物件 B 的存活時間就不會短于物件 A,如果物件 A 沒有顯式地把物件 B 的參考設為 null 的話,那么只有當物件 A 被垃圾回收之后,物件 B 才不再有參考指向它,才可能獲得被垃圾回收的機會, 除了強參考之外,java.lang.ref包還提供了對一個物件的另一種不同的參考方式,JVM 的垃圾回收器對于不同型別的參考有不同的處理方式,
軟參考
軟參考(soft reference)在強度上弱于強參考,通過類 SoftReference 來表示,它的作用是告訴垃圾回收器,程式中的哪些物件不那么重要,當記憶體不足的時候是可以被暫時回收的,當 JVM 中的記憶體不足時,垃圾回收器會釋放那些只被軟參考指向的物件,如果全部釋放完這些物件之后,記憶體仍不足,則會拋出 OutOfMemory 錯誤,軟參考非常適合于創建快取,當系統記憶體不足時候,快取中的內容是可以被釋放的,比如考慮一個影像編輯器的程式,該程式會把影像檔案的全部內容讀取到記憶體中,以方便進行處理,用戶也可以同時打開多個檔案,當同時打開的檔案過多時,就可能造成記憶體不足,如果使用軟參考來指向影像檔案的內容,垃圾回收器就可以在必要的時候回收這些記憶體,
陣列的初始化
在 Java 中不存在只分配記憶體空間而不賦初始值的情況,因為一旦為陣列的每個陣列元素分配記憶體空間,記憶體空間里存盤的內容就是陣列元素的值,即使記憶體空間存盤的內容為空,「空」也是值,用 null 表示,不管以哪一種方式初始化陣列,只要為陣列元素分配了記憶體空間,陣列元素就有了初始值,獲取初始值的方式有兩種:一種由系統自動分配;另一種由程式員指定,
Java 面向物件的幾個核心概念
類
只要是一門面向物件的編程語言(例如 C++、C#等),那么就一定有類這個概念,類是指將相同屬性的東西放在一起,類是一個模板,能夠描述一類物件的行為和狀態,請看下面兩個例子,
- 在現實生活中,可以將人看成一個類,這類稱為人類,
- 如果某個男孩想找一個物件(女朋友),那么所有的女孩都可能是這個男孩的女朋友,所有的女孩就是一「類」,
Java 中的每一個源程式至少都會有一個類,在本書前面介紹的實體中,用關鍵字 class 定義的都是類,Java 是面向物件的程式設計語言,類是面向物件的重要內容,我們可以把類當成一種自定義資料型別,可以使用類來定義變數,這種型別的變數統稱為參考型變數,也就是說,所有類都參考資料型別,
物件
物件是實際存在某個類中的每一個個體,因而也稱為實體(instance),物件的抽象是類,類的具體化就是物件,也可以說類的實體是物件,類用來描述一系列物件,類會概述每個物件包括的資料和行為特征,因此,我們可以把類理解成某種概念、定義,它規定了某類物件所共同具有的資料和行為特征,
接著前面的兩個例子,
- 人這個「類」的范圍實在是太籠統了,人類里面的秦始皇是一個具體的人,是一個客觀存在的人,我們就將秦始皇稱為一個物件,
- 想找物件(女朋友)的男孩已經找到目標了,他的女朋友名叫「大美女」,注意,假設叫這個名字的女孩人類中僅有這一個,此時名叫「大美女」的這個女孩就是一個物件,
在面向物件的程式中,首先要將一個物件看作一個類,假定人是物件,任何一個人都是一個物件,類只是一個大概念而已,而類中的物件是具體的,它們具有自己的屬性(例如漂亮、身材好)和方法(例如會作詩、會編程),
Java 中的物件
通過上面的講解可知,我們的身邊有很多物件,例如車、狗、人等,所有這些物件都有自己的狀態和行為,拿一條狗來說,它的狀態有名字、品種、顏色;行為有叫、搖尾巴和跑,
現實物件和軟體物件之間十分相似,軟體物件也有狀態和行為,軟體物件的狀態就是屬性,行為通過方法來體現,在軟體開發程序中,方法操作物件內部狀態的改變,物件的相互呼叫也是通過方法來完成的,
注意:類和物件有以下區別,
類描述客觀世界里某一類事物的共同特征,而物件則是類的具體化,Java 程式使用類的構造器來創建該類的物件,
類是創建物件的模板和藍圖,是一組類似物件的共同抽象定義,類是一個抽象的概念,不是一個具體的事物,
物件是類的實體化結果,是真實的存在,代表現實世界的某一事物,
屬性
屬性有時也稱為欄位,用于定義該類或該類的實體所包含的資料,在 Java 程式中,屬性通常用來描述某個物件的具體特征,是靜態的,例如姚明(物件)的身高為 2.6m,小白(物件)的毛發是棕色的,二郎神(物件)額頭上有只眼睛等,都是屬性,
方法
方法用于定義該類或該類實體的行為特征或功能實作, 每個物件都有自己的行為或者使用它們的方法,比如說一只狗(物件)會跑會叫等,我們把這些行為稱為方法,它是動態的,可以使用這些方法來操作一個物件,
類的成員
屬性和方法都被稱為所在類的成員,因為它們是構成一個類的主要部分,如果沒有這兩樣東西,那么類的定義也就沒有內容了,
定義類
在 Java 語言中,定義類的語法格式如下所示,
[修飾符] class 類名
{
零到多個構造器的定義…
零到多個屬性…
零到多個方法…
}
在上面定義類的語法格式中,修飾符可以是 public、final 或 static,也可以完全省略它們,類名只要是一個合法的識別符號即可,但這僅滿足了 Java 的語法要求;如果從程式的可讀性方面來看,那么 Java 類名必須由一個或多個有意義的單詞構成,其中每個單詞的首字母大寫,其他字母全部小寫,單詞與單詞之間不要使用任何分隔符,
在定義一個類時,它可以包含 3 個最常見的成員,它們分別是構造器、屬性和方法,這 3 個成員可以定義零個或多個,如果 3 個成員都只定義了零個,則說明定義了一個空類,這沒有太大的實際意義,類中各個成員之間的定義順序沒有任何影響,各個成員之間可以相互呼叫,需要注意的是,一個類的 static 方法需要通過實體化其所在類來訪問該類的非 static 成員,
下面的代碼定義一個名為 person 的類,這是具有一定特性(人類)的一類事物,而 Tom 則是類的一個物件實體,其代碼如下所示,
class person {
int age; //人具有 age 屬性
String name; //人具有 name 屬性
void speak(){ //人具有 speak 方法
System.out.println("My name is"+name);
}
}
public static void main(String args[]){
//類及類屬性和方法的使用
person Tom=new person(); //創建一個物件
Tom.age=27; //物件的 age 屬性是 27
Tom.name="TOM"; //物件的 name 屬性是 TOM
Tom.speak(); //物件的方法是 speak
}
一個類需要具備對應的屬性和方法,其中屬性用于描述物件,而方法可讓物件實作某個具體功能,例如在上述實體代碼中,類、物件、屬性和方法的具體說明如下所示,
- 類:代碼中的
person就是一個類,它代表人類, - 物件:代碼中的 Tom(注意,不是 TOM)就是一個物件,它代表一個具體的人,
- 屬性:代碼中有兩個屬性:
age和name,其中屬性 age 表示物件 Tom 這個人的年齡是 27,屬性 name 表示物件 Tom 這個人的名字是 TOM, - 方法:代碼中的
speak()是一個方法,它表示物件 Tom 這個人具有說話這一技能,
定義屬性
在 Java 中定義屬性的語法格式如下所示,
[修飾符] 屬性型別 屬性名 [=默認值];
上述格式的具體說明如下所示,
- 修飾符:修飾符可以省略,也可以是
public、protected、private、static、final,其中public、protected、private最多只能出現一個,它可以與static、final組合起來修飾屬性, - 屬性型別:屬性型別可以是 Java 語言允許的任何資料型別,它包括基本型別和現在介紹的復合型別,
- 屬性名:屬性名只要是一個合法的識別符號即可,但這只是從語法角度來說的,如果從程式可讀性角度來看,那么作者建議屬性名應該由一個或多個有意義的單詞構成,第一個單詞的首字母小寫,后面每個單詞的首字母大寫,其他字母全部小寫,單詞與單詞之間不需使用任何分隔符,
- 默認值:在定義屬性時可以定義一個由用戶指定的默認值,如果用戶沒有指定默認值,則該屬性的默認值就是其所屬型別的默認值,
定義方法
在 Java 中定義方法的語法格式如下所示,
[修飾符] 方法回傳值型別 方法名 ([形參串列,..]){
由零潭訓多條可執行陳述句組成的方法體;
}
-
修飾符:它可以省略,也可以是
public、protected、private、static、final、abstract,其中public、protected、private這 3 個最多只能出現一個;abstract和final最多只能出現一個,它們可以與static組合起來共同修飾方法, -
方法回傳值型別:回傳值型別可以是 Java 語言允許的任何資料型別,這包括基本型別、復合型別與 _
void型別_,如果宣告了方法的回傳值型別,則方法體內就必須有一個有效的return陳述句,該陳述句可以是一個變數或一個運算式,這個變數或者運算式的型別必須與該方法宣告的回傳值型別相匹配,當然,如果一個方法中沒有回傳值,那么我們也可以將回傳值宣告成void型別, -
方法名:方法名的命名規則與屬性的命名規則基本相同,我們建議方法名以英文的動詞開頭,
形參串列:形參串列用于定義該方法可以接受的引數,形參串列由零到多組“引數型別形參名”組合而成,多組引數之間以英文逗號(,)隔開,形參型別和形參名之間以英文空格隔開,一旦在定義方法時指定了形參串列,則在呼叫該方法時必須傳入對應的引數值——誰呼叫方法,誰負責為形參賦值,
在方法體中的多條可執行性陳述句之間有著嚴格的執行順序,在方法體前面的陳述句總是先執行,在方法體后面的陳述句總是后執行,
同學們實際上在前面的章節中已經多次接觸過方法,例如 public static void main(String args[]){...} 這段代碼中就使用了方法 main(),在下面的代碼中也定義了幾個方法,
public class test_class {
//定義一個無回傳值的方法
public void cheng(){ //方法名是 cheng
System.out.println("我已經長大了"); //方法 cheng 的功能是輸出文本"我已經長大了"
//…
}
//定義一個有回傳值的方法
public int Da(){ //方法名是 Da
int a=100; //定義變數 a,設定初始值是 100
return a; //方法 Da 的功能回傳變數 a 的值
}
定義構造器
構造器是一個創建物件時自動呼叫的特殊方法,目的是執行初始化操作,構造器的名稱應該與類的名稱一致,當 Java 程式在創建一個物件時,系統會默認初始化該物件的屬性,基本型別的屬性值為 0(數值型別)、false(布爾型別),把所有的參考型別設定為 null,構造器是類創建物件的根本途徑,如果一個類沒有構造器,那么這個類通常將無法創建實體,為此 Java 語言提供構造器機制,系統會為該類提供一個默認的構造器,一旦為類提供了構造器,那么系統將不再為該類提供構造器,
定義構造器的語法格式與定義方法的語法格式非常相似,在呼叫時,我們可以通過關鍵字 new 來呼叫構造器,從而回傳該類的實體,下面,我們先來看一下定義構造器的語法格式,
[修飾符] 構造器名 ([形參串列,..]);
{
由零潭訓多條可執行陳述句組成的構造器執行體;
}
上述格式的具體說明如下所示,
-
修飾符:修飾符可以省略,也可以是
public、protected、private其中之一, -
構造器名:構造器名必須和類名相同,
-
形參串列:這和定義方法中的形參串列的格式完全相同,
與一般方法不同的是,構造器不能定義回傳值的型別,也不能使用 void 定義構造器沒有回傳值,如果為構造器定義了回傳值的型別,或使用 void 定義構造器沒有回傳值,那么在編譯時就不會出錯,但 Java 會把它當成一般方法來處理,下面的代碼演示了使用構造器的程序,
public class Person { //定義類 Person
public String name; //定義屬性 name
public int age; //定義屬性 age
public Person(String name, int age) { //構造器函式 Person()
this.name = name; //開始自定義構造器,添加 name 屬性
this.age = age; //繼續自定義構造器,添加 age 屬性
}
public static void main(String[] args) {
// 使用自定義的構造器創建物件(構造器是創建物件的重要途徑)
Person p = new Person("小明", 12); //創建物件 p,名字是“小明”,年齡是 12
System.out.println(p.age); //輸出物件 p 的年齡
System.out.println(p.name); //輸出物件 p 的名字
}
}
public 修飾符
在 Java 程式中,如果將屬性和方法定義為 public 型別,那么此屬性和方法所在的類和及其子類、同一個包中的類、不同包中的類都可以訪問這些屬性和方法,
private 修飾符
在 Java 程式里,如果將屬性和方法定義為 private 型別,那么該屬性和方法只能在自己的類中訪問,在其他類中不能訪問,
protected 修飾符
在撰寫 Java 應用程式時,如果使用修飾符 protected 修飾屬性和方法,那么該屬性和方法只能在自己的子類和類中訪問,
其他修飾符
前面幾節講解的修飾符是在 Java 中最常用的修飾符,除了這幾個修飾符外,在 Java 程式中還有許多其他的修飾符,具體說明如下所示,
- 默認修飾符:如果沒有指定訪問控制修飾符,則表示使用默認修飾符,這時變數和方法只能在自己的類及同一個包下的類中訪問,
static:由static修飾的變數稱為靜態變數,由static修飾的方法稱為靜態方法,final:由final修飾的變數在程式執行程序中最多賦值一次,所以經常定義它為常量,transient:它只能修飾非靜態變數,當序列化物件時,由transient修飾的變數不會序列化到目標檔案,當物件從序列化檔案中重構物件時(反序列化程序),不會恢復由transient欄位修飾的變數,volatile:和transient一樣,它只能修飾變數,這個關鍵字的作用就是告訴編譯器,只要是被此關鍵字修飾的變數都是易變、不穩定的,abstract:由abstract修飾的成員稱為抽象方法,用abstract修飾的類可以擴展(增加子類),且不能直接實體化,用abstract修飾的方法不能在宣告它的類中實作,且必須在某個子類中重寫,synchronized:該修飾符只能應用于方法,不能修飾類和變數,此關鍵字用于在多執行緒訪問程式中共享資源時實作順序同步訪問資源,
方法與函式的關系
不論是從定義方法的語法上來看,還是從方法的功能上來看,都不難發現方法和函式之間的相似性,盡管實際上方法是由傳統函式發展而來的,但方法與傳統的函式有著顯著不同,在結構化編程語言里,函式是老大,整個軟體由許多的函陣列成,在面向物件的編程語言里,類才是老大,整個系統由許多的類組成,因此在 Java 語言里,方法不能獨立存在,方法必須屬于類或物件,在 Java 中如果需要定義一個方法,則只能在類體內定義,不能獨立定義一個方法,一旦將一個方法定義在某個類體內,并且這個方法使用 static 來修飾,則這個方法屬于這個類;否則,這個方法屬于這個類的物件,
在 Java 語言中,型別是靜態的,即我們當定義一個類之后,只要不再重新編譯這個類檔案,那么該類和該類物件所擁有的方法是固定的,且永遠都不會改變,因為 Java 中的方法不能獨立存在,它必須屬于一個類或者一個物件,所以方法也不能像函式那樣獨立執行,在執行方法時必須使用類或物件作為呼叫者,即所有方法都必須使用 類,方法 或 物件,方法 的格式來呼叫,此處可能會產生一個問題,當同一個類的不同方法之間相互呼叫時,不可以直接呼叫嗎?在此需要明確一個原則:當在同一個類的一個方法中呼叫另外一個方法時,如果被調方法是普通方法,則默認使用 this 作為呼叫者;如果被調方法是靜態方法,則默認使用類作為呼叫者,盡管從表面上看起來某些方法可以獨立執行,但實際上它還是使用 this 或者類來作為呼叫者,
永遠不要把方法當成獨立存在的物體,正如現實世界由類和物件組成,而方法只能作為類和物件的附屬,Java 語言里的方法也是一樣,講到此處,可以總結 Java 里的方法有如下主要屬性,
- 方法不能獨立定義,只能在類體里定義,
- 從邏輯意義上來看,方法要么屬于該類本身,要么屬于該類的一個物件,
- 永遠不能獨立執行方法,執行方法必須使用類或物件作為呼叫者,
長度可變的方法
自 JDK 1.5 之后,在 Java 中可以定義形參長度可變的引數,從而允許為方法指定數量不確定的形參,如果在定義方法時,在最后一個形參型別后增加 3 點 ...,則表明該形參可以接受多個引數值,它們當成陣列傳入,在下面的實體代碼中定義了一個形參長度可變的方法,
不使用 void 關鍵字構造方法名
前面小節節已經講解了構造器的知識,在此提醒同學們,構造方法沒有回傳型別,不用 void 修飾,只有一個 public 之類的修飾符而已,
遞回方法
如果一個方法在其方法體內呼叫自身,那么這稱為方法遞回,方法遞回包含一種隱式的回圈,它會重復執行某段代碼,但這種重復執行無須回圈控制,
使用 this
在講解變數時,曾經將變數分為區域變數和全域變數兩種,當區域變數和全域變數的資料型別和名稱都相同時,全域變數將會被隱藏,不能使用,為了解決這個問題,Java 規定可以使用關鍵字 this 去訪問全域變數,使用 this 的語法格式如下所示,
this. 成員變數名
this. 成員方法名()
創建和使用物件
在 Java 程式中,一般通過關鍵字 new 來創建物件,計算機會自動為物件分配空間,然后訪問變數和方法,對于不同的物件,變數也是不同的,方法由物件呼叫,
使用靜態變數和靜態方法
在前面已經講過,只要使用修飾符 static 關鍵字在變數和方法前面,那么這個變數和方法就稱作靜態變數和靜態方法,靜態變數和靜態方法的訪問只需要類名,通過運算 . 即可以實作對變數的訪問和對方法的呼叫,
抽象類和抽象方法的基礎
抽象方法和抽象類必須使用 abstract 修飾符來定義,有抽象方法的類只能定義成抽象類,類里可以沒有抽象方法,所謂抽象類是指只宣告方法的存在而不去實作它的類,抽象類不能實體化,也就是不能創建物件,在定義抽象類時,要在關鍵字 class 前面加上關鍵字 abstract,其具體格式如下所示,
abstract class 類名{
類體
}
- 抽象類必須使用
abstract修飾符來修飾,抽象方法也必須使用abstract修飾符來修飾,方法不能有方法體, - 抽象類不能實體化,無法使用關鍵字 new 來呼叫抽象類的構造器創建抽象類的實體,
- 抽象類里不能包含抽象方法,這個抽象類也不能創建實體,
- 抽象類可以包含屬性、方法(普通方法和抽象方法都可以)、構造器、初始化塊、內部類、列舉類 6 種,抽象類的構造器不能創建實體,主要用于被其子類呼叫,
- 含有抽象方法的類(包括直接定義一個抽象方法;繼承一個抽象父類,但沒有完全實作父類包含的抽象方法;實作一個介面,但沒有完全實作介面包含的抽象方法)只能定義成抽象類,
由此可見,抽象類同樣能包含與普通類相同的成員,只是抽象類不能創建實體,普通類不能包含抽象方法,而抽象類可以包含抽象方法,
抽象方法和空方法體的方法不是同一個概念,例如 public abstract void test() 是一個抽象方法,它根本沒有方法體,即方法定義后沒有一對花括號,然而,但 public void test(){} 是一個普通方法,它已經定義了方法體,只是這個方法體為空而已,即它的方法體什么也不做,因此這個方法不能使用 abstract 來修飾,
抽象類必須有一個抽象方法
創建抽象類最大的要求是必須有一個抽象方法
抽象類的作用
抽象類不能創建實體,它只能當成父類來繼承,從語意的角度看,抽象類是從多個具體類中抽象出來的父類,它具有更高層次的抽象,從多個具有相同特征的類中抽象出一個抽象類,以這個抽象類作為其子類的模板,從而避免子類設計的隨意性,
抽象類體現的是一種模板模式的設計,抽象類為多個子類的通用模板,子類在抽象類的基礎上進行擴展、改造,但總體上子類會大致保留抽象類的行為方式,如果撰寫一個抽象父類,父類提供了多個子類的通用方法,并把一個或多個方法留給其子類實作,那么這就是一種模板模式,模板模式也是最常見、最簡單的設計模式之一,接下來看一個模板模式的實體代碼,在它演示的抽象父類中,父類的普通方法依賴于一個抽象方法,而抽象方法則推遲到子類中實作,
軟體包的定義
定義軟體包的方法十分簡單,只需要在 Java 源程式的第一句中添加一段代碼即可,在 Java 中定義包的格式如下所示,
package 包名;
package 宣告了多程式中的類屬于哪個包,在一個包中可以包含多個程式,在 Java 程式中還可以創建多層次的包,具體格式如下所示,
package 包名1[.包名2[.包名3]];
新建 UseFirst.java 檔案,撰寫以下代碼,
package China.CQ; //加載一個包,其中父目錄函式“China”的子目錄是"CQ"
public class UseFirst { //定義類
public static void main(String[] args){
System.out.println("這個程式定義了一個包");
}
}
執行上述代碼后將會創建一個多層次的包,由此可見,定義軟體包的程序實際上就是新建一個檔案夾,將編譯后的檔案放在新建檔案夾中,定義軟體包實際上完成的就是這個事情,
在程式里插入軟體包
在 Java 程式中插入軟體包的方法十分簡單,只需使用 import 陳述句插入所需的類即可,在上一節實驗中,已經對插入軟體包這個概念進行了初次的接觸,在 Java 程式中插入軟體包的格式如下所示,
import 包名1.[包名2[.包名3]].(類名1*);
上述格式中,各個引數的具體說明如下所示,
- 包名 1:一級包,
- 包名 2:二級包,
- 類名:是需要匯入的類名,也可以使用
*號,表示將匯入這個包中的所有類,
掌握 this 的好處
關鍵字 this 最大的作用就讓類中的一個方法訪問該類的另一個方法或屬性,其實 this 關鍵字是很容易理解的,接下來作者舉兩個例子進行對比,相信大家看后對 this 的知識就完全掌握了,
第一段代碼演示了沒有使用 this 的情況,具體代碼如下所示,
class A {
private int aa, bb; // 宣告兩個 int 型別變數
public int returnData(int x, int y) { // 一個回傳整數的方法
aa = x;
bb = y;
return aa + bb;
}
}
在第二段代碼中使用 this,具體代碼如下所示,
在下面的代碼中需要重點注意在 MyDate newDay=new MyDate(this); 陳述句中 this 的作用,
class MyDate {
private int day;
private int month;
private int year; // 定義 3 個成員變數
public MyDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
} // 構造方法
public MyDate(MyDate date) {
this.day = date.day;
this.month = date.month;
this.year = date.year; // 將引數 Date 類中的成員變數賦給 MyDate 類
} // 構造方法
public int getDay() {
return day;
}// 方法
public void setDay(int day) {
this.day = day; // 引數 day 賦給此類中的 ddy
}
public MyDate addDays(int moreDay) {
MyDate newDay = new MyDate(this);
newDay.day = newDay.day + moreDay;
return newDay; // 回傳整個類
}
public void print() {
System.out.println("My Date: " + year + "-" + month + "-" + day);
}
}
public class TestMyDate {
public static void main(String args[]) {
MyDate myBirth = new MyDate(19, 11, 1987); // 利用建構式初始化
MyDate next = myBirth.addDays(7);
// addDays() 的回傳值是類,將其回傳值賦給變數 next
next.print();
}
}
事實上,前兩個類從本質說是相同的,而為什么在第二個類中使用 this 關鍵字呢?注意,第二個類中的方法 returnData (int aa,int bb) 的形式引數分別為 aa 和 bb,這剛好和 private int aa,bb; 里的變數名是一樣的,現在問題來了:究竟如何在 returnData 的方法體中區別形式引數 aa 和全域變數 aa 呢?兩個 bb 也是如此嗎?這就是引入 this 關鍵字的用處所在了,this.aa 表示的是全域變數 aa,而沒有加 this 的 aa 表示形式引數 aa,bb 也是如此,
在此建議,在編程中不能過多使用 this 關鍵字,這從上面的代碼中也可以看出,當相同的變數名加上 this 關鍵字過多時,有時會讓人分不清,這時可以按照第三段代碼進行修改,避免使用 this 關鍵字,
class A {
private int aa, bb; // 宣告兩個 int 型別變數
public int returnData(int aa1, int bb1) {
aa = aa1; // 在 aa 后面加上數字 1 加以區分,其他以此類推
bb = bb1;
return aa + bb;
}
}
由此可以看出,盡管上面的第一段代碼、第二段代碼、第三段代碼都是一樣的,但是第三段代碼既避免了使用 this 關鍵字,又避免了第一段代碼中引數意思不明確的缺點,所以建議使用與第三段代碼一樣的方法,
推出抽象方法的原因
當撰寫一個類時,常常會為該類定義一些方法,這些方法用以描述該類的行為方式,這時這些方法都有具體的方法體,在某些情況下,某個父類只是知道其子類應該包含什么樣的方法,但卻無法準確知道這些子類如何實作這些方法,例如定義一個 Shape 類,這個類應該提供一個計算周長的方法 scalPerimeter(),不同 Shape 子類對周長的計算方法是不一樣的,也就是說 Shape 類無法準確知道其子類計算周長的方法,
很多人以為,既然 Shape 不知道如何實作 scalPerimeter() 方法,那么就干脆不要管它了,其實這是不正確的作法,假設有一個 Shape 參考變數,該變數實際上會參考到 Shape 子類的實體,那么這個 Shape 變數就無法呼叫 scalPerimeter() 方法,必須將其強制型別轉換為其子型別別才可呼叫 scalPerimeter() 方法,這就降低了 Shape 的靈活性, 究竟如何既能在 Shape 類中包含 scalPerimeter() 方法,又無須提供其方法實作呢?Java 中的做法是使用抽象方法滿足該要求,抽象方法是只有方法簽名,并沒有方法實作的方法,
static 修飾的作用
使用 static 修飾的方法屬于這個類,或者說屬于該類的所有實體所共有,使用 static 修飾的方法不但可以使用類作為呼叫者來呼叫,也可以使用物件作為呼叫者來呼叫,值得指出的是,因為使用 static 修飾的方法還是屬于這個類的,所以使用該類的任何物件來呼叫這個方法都將會得到相同的執行結果,這與使用類作為呼叫者的執行結果完全相同,
不使用 static 修飾的方法則屬于該類的物件,它不屬于這個類,因此不使用 static 修飾的方法只能用物件作為呼叫者來呼叫,不能使用類作為呼叫者來呼叫,使用不同物件作為呼叫者來呼叫同一個普通方法,可能會得到不同的結果,
陣列內是同一型別的資料
Java 是一門是面向物件的編程語言,能很好地支持類與類之間的繼承關系,這樣可能產生一個陣列里可以存放多種資料型別的假象,例如有一個水果陣列,要求每個陣列元素都是水果,實際上陣列元素既可以是蘋果,也可以是香蕉,但這個陣列中的陣列元素型別還是唯一的,只能是水果型別,
另外,由于陣列是一種參考型別的變數,因此使用它定義一個變數時,僅表示定義了一個參考變數(也就是定義了一個指標),這個參考變數還未指向任何有效的記憶體,因此定義陣列時不能指定陣列的長度,由于定義陣列僅是定義了一個參考變數,并未指向任何有效的記憶體空間,所以還沒有記憶體空間來存盤陣列元素,這時這個陣列也不能使用,只有陣列初始化后才可以使用,
繼承的定義
類的繼承是指從已經定義的類中派生出一個新類,是指我們在定義一個新類時,可以基于另外一個已存在的類,從已存在的類中繼承有用的功能(例如屬性和方法),這時已存在的類便被稱為父類,而這個新類則稱為子類,在繼承關系中,父類一般具有所有子類的共性特征,而子類則會為自己增加一些更具個性的方法,類的繼承具有傳遞性,即子類還可以繼續派生子類,因此,位于上層的類在概念上就更抽象,而位于下層的類在概念上就更具體,
父類和子類
繼承是面向物件的機制,利用繼承可以創建一個公共類,這個類具有多個專案的共同屬性,我們可再用一些具體的類來繼承該類,同時加上自己特有的屬性,在 Java 中實作繼承的方法十分簡單,具體格式如下所示,
<修飾符> class <子類名> extends <父類名> {
[<成員變數定義>]…
[<方法定義>]…
}
我們通常所說的子類一般指的是某父類的直接子類,而父類也可稱為該子類的直接超類,如果存在多層繼承關系,比如,類 A 繼承的是類 B,則它們之間的關系就必須符合下面的要求,
- 若存在另外一個類 C,類 C 是類 B 的子類,類 A 是類 C 的子類,那么可以判斷出類 A 是類 B 的子類,
- 在 Java 程式中,一個類只能有一個父類,也就是說在 extends 關鍵字前只能有一個類,它不支持多重繼承,
呼叫父類的構造方法
構造方法是 Java 類中比較重要的方法,一個子類可以訪問構造方法,這在前面已經使用過多次,Java 語言呼叫父類構造方法的具體格式如下所示,
super(引數);
訪問父類的屬性和方法
在 Java 程式中,一個類的子類可以訪問父類的屬性和方法,具體語法格式如下所示,
super.[方法和全域變數];
在黑夜里夢想著光,心中覆寫悲傷,在悲傷里忍受孤獨,空守一絲溫暖, 我的淚水是無底深海,對你的愛已無言,相信無盡的力量,那是真愛永在, 我的信仰是無底深海,澎湃著心中火焰,燃燒無盡的力量,那是忠誠永在
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/401410.html
標籤:其他
