第三章Java的基本程序設(shè)計(jì)結(jié)構(gòu)
數(shù)據(jù)類型
Java 是一種強(qiáng)類型語言派殷,必須為每個(gè)變量聲明一種類型。在 Java 中有 8 種基本類型:4 種整型墓阀,2種浮點(diǎn)型毡惜,1 種用于表示 Unicode 編碼的字符單元的字符類型 char,和一種用于表示真值的 boolean 類型(String 和 數(shù)組都是對(duì)象)斯撮。
整型:
用于表示沒有小數(shù)部分的數(shù)值经伙,它允許是負(fù)數(shù)
取值范圍 -2^(位-1) -----2^(位-1)-1 一個(gè)字節(jié)代表8位
類型 | 存儲(chǔ)需求 | 取值范圍 | 具體值 |
---|---|---|---|
int | 4 字節(jié) | -2的32次方~2的32次方-1 | -2147483646-2147483647(正好超過20億) |
short | 2 字節(jié) | -2的16次方~2的16次方-1 | -32768~32767 |
long | 8 字節(jié) | -2的64次方~2的64次方-1 | -9223372036854775808~9223372036854775807 |
byte | 1 字節(jié) | -2的8次方~2的8次方-1 | -128~127 |
在 Java 種,整型的范圍于運(yùn)行 Java 代碼的機(jī)器無關(guān)勿锅。
長整型數(shù)值后面有一個(gè) L
后綴帕膜,從 Java7 開始,加上前綴 0b 或 0B 就可以寫二進(jìn)制數(shù)溢十。如 0b1001
就代表 9
垮刹,還可以為數(shù)字字面量加下劃線,如用 1_000_000 表示一百萬茶宵,更加可讀危纫,編譯器會(huì)去掉這些下劃線。
System.out.println(0b1001); // result : 9
System.out.println(1_000_000); // result :1000000
浮點(diǎn)類型
用于表示有小數(shù)部分的數(shù)值乌庶。Java 中有兩種浮點(diǎn)類型种蝶。
類型 | 存儲(chǔ)需求 | 取值范圍 |
---|---|---|
float | 4 字節(jié) | -1.7乘以10的38次方~1.7乘以10的38次方 |
double | 8 字節(jié) | -3.4乘以10的308次方~3.4乘以10的308次方 |
double 表示這種類型的數(shù)值精度是 float 類型的兩倍(也有人稱之為雙精度數(shù)值),float 類型的數(shù)值后有一個(gè)后綴 F 或 f瞒大,沒有后綴的默認(rèn)為 double 類型螃征,double 類型也可以加后綴 D 或 d 。
注意浮點(diǎn)類型計(jì)算存在誤差透敌,是因?yàn)楦↑c(diǎn)數(shù)值采用二進(jìn)制系統(tǒng)表示盯滚,在二進(jìn)制系統(tǒng)中無法精確地表示分?jǐn)?shù) 1/10 ,可以使用 BigDecimal
類代替實(shí)現(xiàn)酗电。
System.out.println(2.0-1.1);
result : 0.8999999999999999
char 類型
char 類型用于表示單個(gè)字符魄藕,不過現(xiàn)在有些變化,有些 Unicode 字符可以用一個(gè) char 值描述撵术,另外一些 Unicode 字符則需要兩個(gè) char 值背率。'A' 與 "A" 不同,前者是編碼值為 65 所對(duì)應(yīng)的字符常量,后者是包含一個(gè)字符 A 的字符串寝姿。char 類型的值可以表示為十六進(jìn)制值交排,其范圍從 \u0000 到 \Uffff 。
下面這行代碼是符合語法標(biāo)準(zhǔn)的饵筑,\u005B
表示 [
埃篓,\u005D
表示 ]
。Unicode 轉(zhuǎn)義序列會(huì)在解析代碼之前得到處理根资。
public static void main(String\u005B\u005D args) {}
Java中可以使用 \u + Unicode編碼
來進(jìn)行轉(zhuǎn)義架专,如 \u0022
,除了這個(gè)以外玄帕,還有一些特殊的轉(zhuǎn)義序列
轉(zhuǎn)義序列 | 名稱 | Unicode值 |
---|---|---|
\b | 退格 | \u0008 |
\t | 制表 | \u0009 |
\n | 換行 | \u000a |
\r | 回車 | \u000d |
\" | 雙引號(hào) | \u0022 |
\' | 單引號(hào) | \u0027 |
\\ | 反斜杠 | \u005c |
boolean 類型
boolean(布爾) 類型有兩個(gè)值:false 和 true胶征,用來判定邏輯條件。整型值和布爾值之間不能進(jìn)行相互轉(zhuǎn)換桨仿。
變量
在 Java 中睛低,每個(gè)變量都一個(gè)類型(type)。在聲明變量時(shí)服傍,變量的類型位于變量名之前钱雷,例如:
double salary;
int vacationDays;
boolean done;
變量名必須要以字母開頭,并由字母或數(shù)字組成的序列吹零,不過這里的“字母”的概念不單指英文字母罩抗,字母包括 A~Z
,a~z
灿椅,_
套蒂,$
,或在某種語言中表示字母的任何 Unicode 字符茫蛹,比如德國人就可以在變量名中使用字母 ?
(讀音為:ei)操刀。
常量
在 Java 中,利用關(guān)鍵字 final 指示常量婴洼,例如
final int cout = 3 ;
關(guān)鍵字 final
表示這個(gè)變量只能被賦值一次骨坑,一旦被賦值之后,就不能夠再修改了柬采,習(xí)慣上欢唾,常量名使用全大寫。聲明在類中粉捻,用 static final
聲明的變量礁遣,也被稱為類常量
運(yùn)算符
在 Java 中,使用算術(shù)運(yùn)算符 +肩刃,-祟霍,*押搪,/,表示加減乘除運(yùn)算浅碾,當(dāng)參與/運(yùn)算的兩個(gè)操作數(shù)都是整數(shù)時(shí),表示整數(shù)觸發(fā)续语;否則表示浮點(diǎn)除法垂谢。整數(shù)的求余操作(有時(shí)稱為取模),用 % 表示疮茄。
a = 15 , b = 2 a/b=7
a%b = 1;
a=15.0
a/b = 7.5
數(shù)學(xué)函數(shù)與常量
在 Math
類中包含了各種各樣的數(shù)學(xué)函數(shù)滥朱,比如這里有一個(gè)計(jì)算數(shù)值平方根的方法
double x = 4;
double y = Math.sqrt(x);// sqrt 接受一個(gè) double 值
System.out.println(y);
冪運(yùn)算的方法
// y 的值為 x 的 a 次冪,同樣接受 double 值力试。
double y = Math.pow(x,a);
數(shù)值類型之間的轉(zhuǎn)換
如果兩個(gè)操作數(shù)中有一個(gè)是 double 類型徙邻,另一個(gè)操作數(shù)就會(huì)轉(zhuǎn)換為 double 類型。
否則畸裳,如果其中一個(gè)操作數(shù)是 float 類型缰犁,另一個(gè)操作數(shù)將會(huì)轉(zhuǎn)換為 float 類型。
否則怖糊,如果其中一個(gè)操作數(shù)是 long 類型帅容,另一個(gè)操作數(shù)將會(huì)轉(zhuǎn)換為 long 類型。
否則伍伤,兩個(gè)操作數(shù)都將被轉(zhuǎn)換為 int 類型并徘。
結(jié)合賦值和運(yùn)算符
"+=",“-=”扰魂,“*=”麦乞,“%=”,這些都是在賦值中使用二元運(yùn)算符劝评,但是不會(huì)改變數(shù)據(jù)的類型姐直,例如:
int x =2 ;
x+=3.5;
此時(shí)等價(jià)于: x = (int)(x+3.5)//先變成 double ,再被轉(zhuǎn)換為 int
自增與自減運(yùn)算符
++n
蒋畜,n++
简肴,是兩種不同的含義,如果把加號(hào)放在前綴百侧,那么則會(huì)先自增砰识,再運(yùn)算表達(dá)式的值;如果放在后綴佣渴,那么則會(huì)先運(yùn)算表達(dá)式的值辫狼,再自增。另外辛润,++4
膨处,是錯(cuò)誤的,自增與自減運(yùn)算符只能用于變量,不能是數(shù)值真椿。
int a = 2;
int c = 3;
System.out.println(a++);
System.out.println(++c);
--------------------------
2
4
關(guān)系和 boolean 運(yùn)算符
- == :檢測相等性
- !=:檢測不相等
- <鹃答,>,<=突硝,>=:小于测摔,大于,小于等于解恰,大于等于
- &&:采用短路的做法锋八,如果前者為 false ,則不計(jì)算后者
- ||:采用短路的做法护盈,如果前者為 true 挟纱,則不計(jì)算后者
- ?: :三元運(yùn)算符腐宋,
condition? expression1:expression2
紊服,如果condition
為 true,就為第一個(gè)表達(dá)式的值胸竞,反之則為第二個(gè)表達(dá)式的值围苫。
位運(yùn)算符
- &:&在運(yùn)算的時(shí)候,將2個(gè)數(shù)字的二進(jìn)制做比較撤师,當(dāng)2個(gè)數(shù)字的值都為1時(shí)剂府,才為1,否則就是0
- |:充當(dāng) 數(shù)值運(yùn)算符的時(shí)候 同樣是比較2進(jìn)制剃盾,當(dāng)有一個(gè)數(shù)為1腺占,那么就取1
- ^:充當(dāng) 數(shù)值運(yùn)算符的時(shí)候 同樣是比較2進(jìn)制, 只能有1個(gè)1痒谴,那就取1
- ~:取反值
- >>:補(bǔ)最左邊的數(shù)位時(shí)衰伯,會(huì)根據(jù)符號(hào)位, 符號(hào)是1 就填充1积蔚,符號(hào)是0意鲸,就填充0;
- <<:左移
- >>>:無符號(hào)右移,:對(duì)于正數(shù) 有符號(hào)與無符號(hào)的右移沒有區(qū)別尽爆。 對(duì)于負(fù)數(shù) 來說怎顾,不管你是0還是1,都會(huì)用0去補(bǔ)位
括號(hào)與運(yùn)算符級(jí)別
如果不使用括號(hào)漱贱,就按照給出的運(yùn)算符優(yōu)先級(jí)次序進(jìn)行計(jì)算槐雾,同一個(gè)級(jí)別的運(yùn)算符按照從左到右的次序進(jìn)行是計(jì)算(除了右結(jié)合運(yùn)算符),
運(yùn)算符 | 結(jié)合性 |
---|---|
[] .()(方法調(diào)用) | 從左向右 |
! ~ ++ -- +(一元運(yùn)算) -(一元運(yùn)算) ()(強(qiáng)制類型轉(zhuǎn)換) new | 從右向左 |
* / % | 從左向右 |
+(正) -(負(fù)) | 從左向右 |
<< >> >>> | 從左向右 |
< <= > >= instanceof | 從左向右 |
== != | 從左向右 |
& | 從左向右 |
^ | 從左向右 |
| | 從左向右 |
&& | 從左向右 |
|| | 從左向右 |
?: | 從右向左 |
= += -= *= /= %= &= ^= <<= >>= >>>= | 從右向左 |
字符串概念
檢測字符串是否相等
使用 equals
方法檢測兩個(gè)字符串是否相等幅狮,s.equals(t)
募强,如果字符串 s 與字符串 t 相等株灸,則返回 true,否則擎值,返回 false慌烧。s 和 t 可以是字符串變量,也可以是字符串字面量:
String abc = "Hello";
abc.equals("Hello");
"Hello".equals(abc);
如果你希望檢測兩個(gè)字符串是否相等鸠儿,而不區(qū)分大小寫屹蚊,可以使用 equalsIgnoreCase
方法。不能使用 ==
來比較字符串是否相同捆交,因?yàn)?==
比較的是變量的內(nèi)存地址,而不是變量的值腐巢。
空串與 Null 串
空串 "" 是長度為0的字符串品追,可以調(diào)用以下代碼檢查一個(gè)字符串是否為空。
if(str.length()==0)
if(str.equals(""))
空串是一個(gè) Java 對(duì)象冯丙,有自己的串長度 (0) 和內(nèi)容 (空) 肉瓦,不過 String 變量還可以存放一個(gè)特殊值:null,表示目前沒有任何對(duì)象與該變量關(guān)聯(lián)胃惜,要檢查一個(gè)字符串是否為 null泞莉,可以使用以下條件:
if (str == null)
有時(shí)要檢查一個(gè)字符串既不是 null 也不為空串,這種情況下就需要使用以下條件:
if (str !=null && str.length() != 0)
String API
- boolean equals(Object other)
- boolean equalsIgnoreCase(String other)
- boolean startWith(String prefix) 如果字符串以 prefix 開頭船殉,則返回 true
- boolean endsWith(String suffix) 如果字符串以 suffix 結(jié)尾鲫趁,則返回 true
- int indexOf(String str)
- int indexOf(String str,int fromIndex)
- int indexOf(int cp)
- int indexOf(int cp利虫,int fromIndex)
- int lastIndexOf(String str)
- int lastIndexOf(String str挨厚,int fromIndex)
- int lastIndexOf(int cp)
- int lastIndexOf(int cp,int fromIndex)
- length()
- String replace(CharSequence oldString糠惫,CharSequence newString)疫剃,可以用 String 或 StringBuilder 對(duì)象作為 CharSequence 參數(shù)。
- String substring(int beginIndex)
- String substring(int beginIndex硼讽,int endIndex)
- String toLowerCase() 轉(zhuǎn)換為小寫
- String toUpperCase() 轉(zhuǎn)換為大寫
- String trim() 這個(gè)字符串將刪除原始字符串頭部和尾部的空格
- String join(CharSequence delimiter巢价,CharSequence... elements)
構(gòu)建字符串
如果單純用 String 來拼接字符串,每次連接字符串都會(huì)構(gòu)建一個(gè)新的 String 對(duì)象固阁,既耗時(shí)壤躲,又浪費(fèi)空間,使用 StringBuilder
就可以避免這個(gè)問題的發(fā)生备燃。
//構(gòu)建一個(gè)空的字符串構(gòu)建器
StringBuilder builder = new StringBuilder();
builder.append("Hello");
builder.append("World");
String completedString = builder.toString();
StringBuilder
的前身是 StringBuffer
柒爵,StringBuffer
的效率略低,但是允許采用多線程的方式執(zhí)行添加或刪除字符的操作赚爵,如果所有字符串都再一個(gè)單線程中編輯棉胀,則應(yīng)該使用 StringBuilder
法瑟,這兩個(gè)類的 API 是相同的。
- StringBuilder()
- int length()
- StringBuilder append(String str)
- StringBuilder append(char c)
- StringBuilder insert(int offset,String str)
- StringBuilder insert(int offset,Char c )
- StringBuilder delete(int startIndex,int endIndex)
- String toString()
格式化輸出
System.out.printf
沿用了 C 語言庫函數(shù)中的 printf
方法唁奢,可以設(shè)置多個(gè)參數(shù)霎挟,例如:
String name = "Pudge";
int age = 15;
System.out.printf("Hello,%s. Next year,you'll be %d", name, age);
每一個(gè)以 % 字符開始的格式說明符都用相應(yīng)的參數(shù)替換。個(gè)數(shù)說明符尾部的轉(zhuǎn)換符將指示被格式化的數(shù)值類型:
- d 十進(jìn)制整數(shù)
- g 通用浮點(diǎn)數(shù)
- s 字符串
- c 字符
- b 布爾
用于 printf 的標(biāo)志 -
d
printf
用于輸出酥夭,可以使用 String.format
方法來創(chuàng)建一個(gè)格式化的字符串,而不打印輸出脊奋。
大數(shù)值
如果基本的整數(shù)和浮點(diǎn)數(shù)精度不能夠滿足需求熬北,那么可以使用 java.math
包中的兩個(gè)很有用的類:BigInteger 和 BigDecimal。這兩個(gè)類可以處理包含任意長度數(shù)字序列的數(shù)值诚隙。BigInteger 實(shí)現(xiàn)了任意精度的整數(shù)運(yùn)算讶隐,BigDecimal 實(shí)現(xiàn)了任意精度的浮點(diǎn)數(shù)運(yùn)算。
//使用靜態(tài)的 valueOf 方法可以將普通的數(shù)值轉(zhuǎn)換為大數(shù)值
//不能使用+久又、-巫延、*等運(yùn)算符,需要使用add地消、multiply方法
BigInteger a = BigInteger.valueOf(100);
BigInteger c = a.add(b) // c = a + b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); //d = c *(b + 2);
BigInteger API
- BigInteger add(BigInteger other)//加法
- BigInteger subtract(BigInteger other)//減法
- BigInteger multiply(BigInteger other)//乘法
- BigInteger divide(BigInteger other)//除法
- BigInteger mod(BigInteger other)//取余
- int compareTo(BigInteger other)//比較炉峰,相等則返回 0,大于則返回 1脉执,小于則返回 -1
BigDecimal API
- BigDecimal add(BigDecimal other)
- BigDecimal subtract(BigDecimal other)
- BigDecimal multiply(BigDecimal other)
- BigDecimal divide(BigDecimal other , RoundingMode mode)//需要給出舍入方式疼阔,如 RoundingMode.HALF_UP 是在學(xué)校中學(xué)習(xí)的四舍五入方式
- int compareTo(BigDecimal other)
- static BigDecimal valueOf(long x)
- static BigDecimal valueOf(long x, int scale)// x / 10^scale
源碼中介紹的舍入模式
// Rounding Modes
/**
* Rounding mode to round away from zero. Always increments the
* digit prior to a nonzero discarded fraction. Note that this rounding
* mode never decreases the magnitude of the calculated value.
*/
public final static int ROUND_UP = 0;
/**
* Rounding mode to round towards zero. Never increments the digit
* prior to a discarded fraction (i.e., truncates). Note that this
* rounding mode never increases the magnitude of the calculated value.
*/
public final static int ROUND_DOWN = 1;
/**
* Rounding mode to round towards positive infinity. If the
* {@code BigDecimal} is positive, behaves as for
* {@code ROUND_UP}; if negative, behaves as for
* {@code ROUND_DOWN}. Note that this rounding mode never
* decreases the calculated value.
*/
public final static int ROUND_CEILING = 2;
/**
* Rounding mode to round towards negative infinity. If the
* {@code BigDecimal} is positive, behave as for
* {@code ROUND_DOWN}; if negative, behave as for
* {@code ROUND_UP}. Note that this rounding mode never
* increases the calculated value.
*/
public final static int ROUND_FLOOR = 3;
/**
* Rounding mode to round towards {@literal "nearest neighbor"}
* unless both neighbors are equidistant, in which case round up.
* Behaves as for {@code ROUND_UP} if the discarded fraction is
* ≥ 0.5; otherwise, behaves as for {@code ROUND_DOWN}. Note
* that this is the rounding mode that most of us were taught in
* grade school.
*/
public final static int ROUND_HALF_UP = 4;
/**
* Rounding mode to round towards {@literal "nearest neighbor"}
* unless both neighbors are equidistant, in which case round
* down. Behaves as for {@code ROUND_UP} if the discarded
* fraction is {@literal >} 0.5; otherwise, behaves as for
* {@code ROUND_DOWN}.
*/
public final static int ROUND_HALF_DOWN = 5;
/**
* Rounding mode to round towards the {@literal "nearest neighbor"}
* unless both neighbors are equidistant, in which case, round
* towards the even neighbor. Behaves as for
* {@code ROUND_HALF_UP} if the digit to the left of the
* discarded fraction is odd; behaves as for
* {@code ROUND_HALF_DOWN} if it's even. Note that this is the
* rounding mode that minimizes cumulative error when applied
* repeatedly over a sequence of calculations.
*/
public final static int ROUND_HALF_EVEN = 6;
/**
* Rounding mode to assert that the requested operation has an exact
* result, hence no rounding is necessary. If this rounding mode is
* specified on an operation that yields an inexact result, an
* {@code ArithmeticException} is thrown.
*/
public final static int ROUND_UNNECESSARY = 7;
ROUND_UP
向遠(yuǎn)離零的方向舍入。舍棄非零部分半夷,并將非零舍棄部分相鄰的一位數(shù)字加一竿开。
ROUND_DOWN
向接近零的方向舍入。舍棄非零部分玻熙,同時(shí)不會(huì)非零舍棄部分相鄰的一位數(shù)字加一否彩,采取截取行為。
ROUND_CEILING
向正無窮的方向舍入嗦随。如果為正數(shù)列荔,舍入結(jié)果同ROUND_UP一致;如果為負(fù)數(shù)枚尼,舍入結(jié)果同ROUND_DOWN一致贴浙。注意:此模式不會(huì)減少數(shù)值大小。
ROUND_FLOOR
向負(fù)無窮的方向舍入署恍。如果為正數(shù)崎溃,舍入結(jié)果同ROUND_DOWN一致;如果為負(fù)數(shù)盯质,舍入結(jié)果同ROUND_UP一致袁串。注意:此模式不會(huì)增加數(shù)值大小概而。
ROUND_HALF_UP
向“最接近”的數(shù)字舍入,如果與兩個(gè)相鄰數(shù)字的距離相等囱修,則為向上舍入的舍入模式赎瑰。如果舍棄部分>= 0.5,則舍入行為與ROUND_UP相同破镰;否則舍入行為與ROUND_DOWN相同餐曼。這種模式也就是我們常說的我們的“四舍五入”。
ROUND_HALF_DOWN
向“最接近”的數(shù)字舍入鲜漩,如果與兩個(gè)相鄰數(shù)字的距離相等源譬,則為向下舍入的舍入模式。如果舍棄部分> 0.5孕似,則舍入行為與ROUND_UP相同踩娘;否則舍入行為與ROUND_DOWN相同。這種模式也就是我們常說的我們的“五舍六入”鳞青。
ROUND_HALF_EVEN
向“最接近”的數(shù)字舍入霸饲,如果與兩個(gè)相鄰數(shù)字的距離相等为朋,則相鄰的偶數(shù)舍入臂拓。如果舍棄部分左邊的數(shù)字奇數(shù)彰导,則舍入行為與 ROUND_HALF_UP 相同羊苟;如果為偶數(shù),則舍入行為與 ROUND_HALF_DOWN 相同挑格。注意:在重復(fù)進(jìn)行一系列計(jì)算時(shí)霞溪,此舍入模式可以將累加錯(cuò)誤減到最小孵滞。此舍入模式也稱為“銀行家舍入法”,主要在美國使用鸯匹。四舍六入坊饶,五分兩種情況,如果前一位為奇數(shù)殴蓬,則入位匿级,否則舍去。
ROUND_UNNECESSARY
斷言請(qǐng)求的操作具有精確的結(jié)果染厅,因此不需要舍入痘绎。如果對(duì)獲得精確結(jié)果的操作指定此舍入模式,則拋出ArithmeticException肖粮。
第四章對(duì)象與類
面向?qū)ο蟪绦蛟O(shè)計(jì)概述
面向?qū)ο蟪绦蛟O(shè)計(jì)孤页,簡稱OOP。Java 是完全面向?qū)ο蟮纳荩仨毷煜?OOP 才能夠編寫 Java 程序行施。
類
class
是構(gòu)造對(duì)象的模板或藍(lán)圖允坚,由類構(gòu)造 (construct) 對(duì)象的過程稱為創(chuàng)建類的實(shí)例 (instance)。
封裝 (encapsulation悲龟,有時(shí)稱為數(shù)據(jù)隱藏)屋讶,對(duì)象中的數(shù)據(jù)稱為實(shí)例域 (instance field),操作數(shù)據(jù)的過程稱為方法 (method)须教。對(duì)于每個(gè)特定的類實(shí)例都有一組特定的實(shí)例域值皿渗。這些值的集合就是這個(gè)對(duì)象的當(dāng)前狀態(tài) (state)。實(shí)現(xiàn)封裝的關(guān)鍵在于絕對(duì)不能讓類中的方法直接地訪問其他類的實(shí)例域轻腺。程序僅通過對(duì)象的方法與對(duì)象數(shù)據(jù)進(jìn)行交互乐疆。
OOP的另一個(gè)原則就是可以通過擴(kuò)展一個(gè)類來建立另外一個(gè)新的類,在 Java 中贬养,所有的類都源自于 Object挤土。通過擴(kuò)展一個(gè)類來建立另外一個(gè)類的過程稱為繼承(inheritance)。
對(duì)象
對(duì)象的三個(gè)主要特性
- 對(duì)象的行為 (behavior):可以對(duì)對(duì)象施加哪些操作误算,或可以對(duì)對(duì)象施加哪些方法仰美?
- 對(duì)象的狀態(tài) (state):當(dāng)施加那些方法時(shí),對(duì)象如何響應(yīng)儿礼?
- 對(duì)象標(biāo)識(shí) (identity):如何辨別具有相同行為與狀態(tài)的不同對(duì)象咖杂?
識(shí)別類
識(shí)別類的簡單規(guī)則是在分析問題的過程中尋找名詞,而方法對(duì)應(yīng)著動(dòng)詞蚊夫。
類之間的關(guān)系
- 依賴:uses-a 一個(gè)類的方法操縱另一個(gè)類的對(duì)象诉字,我們就說一個(gè)類依賴于另一個(gè)類
- 聚合:has-a 一個(gè)類的對(duì)象包含另一個(gè)類的對(duì)象
- 繼承:is-a 一個(gè)類是另一個(gè)類的子類或者父類,類之間進(jìn)行了擴(kuò)展知纷。
使用預(yù)定義類
Java 中沒有類就不能做任何事情壤圃,然而,并不是所有的類都具有面向?qū)ο筇卣骼旁@?Math 類伍绳,在程序中,可以使用 Math 類的方法乍桂,只需要知道方法名和參數(shù)冲杀,而不必了解它的具體實(shí)現(xiàn)過程,這正是封裝的關(guān)鍵所在模蜡,但是 Math 類只封裝了功能漠趁,它不需要也不必隱藏?cái)?shù)據(jù),由于沒有數(shù)據(jù)忍疾,因此也不必?fù)?dān)心生成對(duì)象以及初始化實(shí)例域闯传。
對(duì)象與對(duì)象變量
要想使用對(duì)象,必須首先構(gòu)造對(duì)象卤妒,并指定其初始狀態(tài)甥绿。然后字币,對(duì)對(duì)象應(yīng)用方法。
在 Java 中共缕,使用構(gòu)造器 (constructor) 構(gòu)造新實(shí)例洗出,構(gòu)造器是一種特殊的方法,用來構(gòu)造并初始化對(duì)象图谷。構(gòu)造器的名字應(yīng)該與類名相同翩活,要想構(gòu)造一個(gè)類的對(duì)象,需要在構(gòu)造器前面加上 new
操作符便贵。
new Date()
System.out.println(new Date());
String s = new Date().toString();
在上述例子種菠镇,構(gòu)造的對(duì)象僅使用了一次,如果希望多次使用承璃,需要將對(duì)象存放在一個(gè)變量中利耍。
Date birthday = new Date();
一定要認(rèn)識(shí)到:一個(gè)對(duì)象變量并沒有實(shí)際包含一個(gè)對(duì)象,而僅僅引用一個(gè)對(duì)象盔粹。
Java 類庫中的 LocalDate 類
Date
類用來表示時(shí)間點(diǎn)隘梨,LocalDate
用來表示大家熟悉的日歷表達(dá)法。
LocalDate.now()
// 2017-07-08
還可以調(diào)用 LocalDate.of(1999,12,6)
方法來構(gòu)造對(duì)應(yīng)一個(gè)特定日期的對(duì)象舷嗡。
一旦有了一個(gè) LocalDate
對(duì)象轴猎,可以使用方法 geetYear
、getMonthValue
咬崔、getDayOfMonth
得到年税稼、月烦秩、日垮斯。下列的方法可以得到未來的日期或者過去的日期。
LocalDate c = LocalDate.now();//c 是當(dāng)前時(shí)間
System.out.println(c.plusDays(1));//明天
System.out.println(c.plusDays(-1));//昨天
更改器方法與訪問器方法
Java 庫的一個(gè)較早版本曾經(jīng)有另一個(gè)類來處理日歷只祠,名為 GregorianCalendar
與 c.plusDays(1)
不同兜蠕,GregorianCalendar.add
方法與 plusDays
方法功能差不多,但是是一個(gè)更改器方法 (mutator method)抛寝。調(diào)用這個(gè)方法后熊杨,GregorianCalendar
對(duì)象的狀態(tài)會(huì)改變。
GregorianCalendar c = new GregorianCalendar(1999,1,10);
c.add(Calendar.YEAR, 1);
int year = c.get(Calendar.YEAR);
System.out.println(year);
相反盗舰,只訪問對(duì)象而不修改對(duì)象的方法有時(shí)稱為訪問器方法 (accessor method)晶府。例如 LocalDate.get
和 GregorianCalendar.get
。
下面的代碼可以構(gòu)建一個(gè)當(dāng)月的日歷钻趋。
//帶 * 號(hào)表示今天
Mon Tue Wed Thu Fri Sat Sun
1 2
3 4 5 6 7 8* 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
-----------------------------------------
LocalDate date = LocalDate.now();
int month = date.getMonthValue();// 7
int today = date.getDayOfMonth();// 8
date = date.minusDays(today - 1);// 返回到月初
DayOfWeek weekday = date.getDayOfWeek();// 得到星期幾
int value = weekday.getValue();//得到月初的星期
System.out.println("Mon Tue Wed Thu Fri Sat Sun");//先打印好星期行
for (int i = 1; i < value; i++) {//控制 1 出現(xiàn)的位置
System.out.print(" ");
}
while (date.getMonthValue() == month) {//
System.out.printf("%3d", date.getDayOfMonth());//打印1
if (date.getDayOfMonth() == today) {
System.out.print("*");
} else {
System.out.print(" ");
}
date = date.plusDays(1);
if (date.getDayOfWeek().getValue() == 1) {
System.out.println();
}
}
LocalDate API
- LocalTime now()
- LocalTime of(int year, int month, int day)
- int getYear()
- int getMonthValue()
- int getDayOfMonth()
- DayOfWeek getDayOfWeek
- LocalDate plusDays(int n)
- LocalDate minusDays(int n)
用戶自定義類
要想創(chuàng)建一個(gè)完整的程序川陆,應(yīng)該將若干類組合在一起,其中只有一個(gè)類有 main 方法蛮位。
Employee 類
class ClassName
{
field;
field;
...
constructor1
constructor2
...
method1
method2
...
}
public class Demo01 {
public static void main(String[] args) throws Exception {
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e : staff) {
e.raiseSalary(5);
}
for(Employee e :staff){
System.out.println("name=" +e.getName()+",salary="+e.getSalary()+",hireDay="+e.getHireDay());
}
}
}
class Employee {
private String name;
private double salary;
private LocalDate hireDay;
//constructor
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
hireDay = LocalDate.of(year, month, day);
}
// a method
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
}
當(dāng)我們的 .java
文件包含 2 個(gè)類的時(shí)候较沪,我們編譯時(shí)可以采用這兩種方法
javac Employee*.java //可以使用通配符鳞绕,所有與通配符匹配的源文件都將被編譯成類文件
javac Demo01.java //Java編譯器會(huì)自動(dòng)搜索使用的Employee類,并編譯
從構(gòu)造器開始
public Employee(String n, double s, int year, int month, int day){
name = n ;
salary = s;
LocalDate hireDay = LocalDate.of(year,month,day);
}
構(gòu)造器與類同名尸曼,構(gòu)造器與其他的方法有一個(gè)重要的不同们何。構(gòu)造器總是伴隨著 new 操作符的執(zhí)行被調(diào)用,而不能對(duì)一個(gè)已經(jīng)存在的對(duì)象調(diào)用 constructor 來達(dá)到重新設(shè)置實(shí)例域的目的控轿,例如
`james.Employee("James Bond",250000,1950,1,1)`//ERROR,會(huì)產(chǎn)生編譯錯(cuò)誤冤竹。
構(gòu)造器的基本特點(diǎn):
- 構(gòu)造器與類同名
- 每個(gè)類可以有一個(gè)以上的構(gòu)造器
- 構(gòu)造器可以有 0 個(gè)、1 個(gè)或多個(gè)參數(shù)
- 構(gòu)造器沒有返回值
- 構(gòu)造器總是伴隨著 new 操作一起調(diào)用
隱式參數(shù)與顯式參數(shù)
public void raiseSalary(double byPercent){
double raise = salary * byPercent / 100
salary +=raise;
//salary的前面省略了參數(shù)茬射,
//完整的寫法是
double raise = number007.salary * byPercent / 100
number007.salary +=raise;
}
raiseSalary
有兩個(gè)參數(shù)贴见,第一個(gè)參數(shù)稱為隱式參數(shù) (implicit) 參數(shù),是出現(xiàn)在方法名前的 Employee 類對(duì)象躲株。第二個(gè)參數(shù)位于方法名后面括號(hào)中的數(shù)值片部,這是一個(gè)顯式 (explicit) 參數(shù)。隱式參數(shù)也被稱為方法調(diào)用的目標(biāo)或接收者霜定。
在每個(gè)方法中档悠,關(guān)鍵字 this 表示隱式參數(shù)。如果需要的話望浩,可以用下列方式編寫 raiseSalary
方法:
public void raiseSalary(double Percent){
double raise = this.salary * byPercent / 100;
salary +=raise;
}
encapsulation 的優(yōu)點(diǎn)
使用 public get
方法來代替 public name
辖所,將全局變量設(shè)置為 private
可以防止受到外界的破壞。
注意:不要編寫返回引用可變對(duì)象的 get
方法磨德,因?yàn)樗旧硎强勺兊脑捲祷兀瑫?huì)破壞 encapsulation 性,我們應(yīng)該只能通過 set
方法來改變對(duì)象的狀態(tài)典挑,如果必須要這樣做酥宴,那么我們可以使用 clone
。
class Employee{
public Date getHireDay()
{
return (Date) hireDay.clone();
}
}
基于類的訪問權(quán)限
public boolean equals(Employee other) {
return name.equals(other.name);
}
這段代碼中 Employee 類的 name 變量是 private 修飾的您觉,other.name
意味著我們訪問了另一個(gè)對(duì)象的 private 屬性拙寡,這與我們之前說的是對(duì)不起來的,其原因是: other 是 Employee 類對(duì)象琳水,而 Employee 類的方法可以訪問 Employee 類的任何一個(gè)對(duì)象的私有域肆糕。
私有方法
有時(shí),可能希望將一個(gè)計(jì)算代碼劃分為若干個(gè)獨(dú)立的賦值方法在孝。通常诚啃,這些輔助方法不應(yīng)該成為公有接口的一部分,最好將這樣的方法設(shè)計(jì)為 private私沮。
final
構(gòu)造對(duì)象時(shí)必須初始化這樣的域始赎,也就是說,必須確保在每一個(gè)構(gòu)造器執(zhí)行之后,這個(gè)域的值被設(shè)置极阅,并且在后面的操作中胃碾,不能夠再對(duì)它進(jìn)行修改。如果將 final 用在一個(gè)可變對(duì)象上筋搏,那么 final 只表示該變量的對(duì)象引用不會(huì)更改仆百,對(duì)象本身是可以更改的。
靜態(tài)域與靜態(tài)方法 static
靜態(tài)域
如果將域定義為 static奔脐,每個(gè)類中只有一個(gè)這樣的域俄周。而每一個(gè)對(duì)象對(duì)于所有的實(shí)例域卻都有自己的一份拷貝。static 修飾的屬性屬于類髓迎,而不屬于任何獨(dú)立的對(duì)象峦朗。
靜態(tài)常量
靜態(tài)變量用得比較少,但靜態(tài)常量卻使用得筆記多排龄。例如 Math.PI
public class Math{
public static final double PI = 3.14159265358979323846;
}
這樣做的好處就是波势,我們可以不需要構(gòu)建 Math 的對(duì)象,直接通過 Math.PI
來進(jìn)行訪問橄维,同時(shí)尺铣,設(shè)置為 fanal,避免了被修改的問題争舞。
靜態(tài)方法
靜態(tài)方法是一種不能向?qū)ο髮?shí)施操作的方法凛忿,例如,Math 類的 pow 方法就是一個(gè)靜態(tài)方法竞川。
Math.pow(x,a)
店溢,在運(yùn)算時(shí),不使用任何 Math 對(duì)象委乌。換句話說床牧,沒有隱式的參數(shù)。
可以認(rèn)為靜態(tài)方法是沒有 this 參數(shù)的方法福澡,這也說明了為什么靜態(tài)方法無法調(diào)用非靜態(tài)變量叠赦。
在下面兩種情況下使用靜態(tài)方法:
- 一個(gè)方法不需要訪問對(duì)象狀態(tài)驹马,其所需參數(shù)都是通過顯式參數(shù)提供革砸,例如:Math.pow
- 一個(gè)方法只需要訪問類的靜態(tài)域,例如:Employee.getNextId
工廠方法
靜態(tài)方法還有另外一種常見的用途糯累。類似 LocalDate 和 NumberFormat 的類使用靜態(tài)工程方法 (factory method) 來構(gòu)造對(duì)象算利。
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
double x = 0.1;
System.out.println(currencyFormatter.format(x)); // ¥0.10
System.out.println(percentFormatter.format(x)); // 10%
為什么 NumberFormat 不利用 constructor 來完成這些操作呢?
- 無法命名構(gòu)造器泳姐,構(gòu)造器的名字必須要和類名一樣效拭,但是,這里希望將得到的貨幣實(shí)例和百分比實(shí)例采用不同的名字。
- 當(dāng)使用構(gòu)造器時(shí)缎患,無法改變所構(gòu)造的對(duì)象類型慕的,而 Factory 方法將返回一個(gè) DecimalFormat 類對(duì)象,這是 NumberFormat 的子類挤渔。
main 方法
需要注意肮街,不需要使用對(duì)象調(diào)用靜態(tài)方法。例如判导,不需要構(gòu)造 Math 類對(duì)象就可以調(diào)用 Math.pow嫉父。每一個(gè)類可以有一個(gè) main 方法。這是一個(gè)常用于對(duì)類進(jìn)行單元測試的技巧眼刃。例如绕辖,可以在 Employee 類中添加一個(gè) main 方法,如果想要獨(dú)立地測試 Employee 類擂红,只需要執(zhí)行 java Employee
仪际,如果 Employee 類是一個(gè)更大型應(yīng)用程序的一部分,就可以使用下面這條語句運(yùn)行程序 java Application
昵骤,Employee 類的 main 方法永遠(yuǎn)不會(huì)執(zhí)行弟头。
方法參數(shù)
按值調(diào)用 (call by value) 表示方法接收的是調(diào)用者提供的值。而按引用調(diào)用 (call by reference) 表示方法接收的是調(diào)用者提供的變量地址涉茧。一個(gè)方法可以修改傳遞引用所對(duì)應(yīng)的變量值赴恨,而不能修改傳遞值調(diào)用所對(duì)應(yīng)的變量值。call by
是一個(gè)標(biāo)準(zhǔn)的計(jì)算機(jī)科學(xué)術(shù)語伴栓。Java 程序設(shè)計(jì)語言總是采用按值調(diào)用伦连。也就是說,方法得到的是所有參數(shù)值的一個(gè)拷貝钳垮,特別是惑淳,方法不能修改傳遞給它的任何參數(shù)變量的內(nèi)容。
//假定一個(gè)方法:試圖將參數(shù)值增加至3倍
public static void tripleValue(double x)
{
x = 3 * x ;
}
double percent = 10;
tripleValue(percent)
不過饺窿,并沒有實(shí)現(xiàn)把參數(shù)增加到 3 倍歧焦。讓我們一步步來細(xì)化:
- x 被初始化為 percent 值的一個(gè)拷貝(也就是10)
- x 被乘以 3 后等于 30,但是 percent 還是 10
- 這個(gè)方法結(jié)束之后肚医,參數(shù)變量 x 不再使用
然后绢馍,方法參數(shù)共有兩種類型: - 基本數(shù)據(jù)類型 (數(shù)字、布爾值)
- 對(duì)象引用
下面這段代碼實(shí)現(xiàn)了當(dāng)對(duì)象引用作為參數(shù)的時(shí)候肠套,方法修改了參數(shù)舰涌。
public static void tripleDSalary(Employee x){
x.raiseSalary(200);
}
harry = new Employee(...);
tripleSalary(harry)
具體的執(zhí)行過程是:
- x 被初始化為 harry 值的拷貝,這是一個(gè)對(duì)象的引用
- raiseSalary 方法應(yīng)用于這個(gè)對(duì)象引用你稚。x 和 harry 同時(shí)引用的哪個(gè) Employee 對(duì)象的 salary 提高了 200%
- 方法結(jié)束后瓷耙,參數(shù)變量 x 不再使用朱躺,當(dāng)然,對(duì)象變量 harry 繼續(xù)引用那個(gè)薪金增至 3 倍的雇員對(duì)象搁痛。
下面總結(jié)一下 Java 中方法參數(shù)的使用情況: - 一個(gè)方法不能修改一個(gè)基本數(shù)據(jù)類型的參數(shù) (即數(shù)值型或布爾型)
- 一個(gè)方法可以改變一個(gè)對(duì)象參數(shù)的狀態(tài)
- 一個(gè)方法不能讓對(duì)象參數(shù)引用一個(gè)新的對(duì)象
對(duì)象構(gòu)造
由于對(duì)象構(gòu)造非常重要长搀,所以 Java 提供了多種編寫構(gòu)造器的機(jī)制。
重載
有些類有多個(gè) constructor鸡典,例如盈滴,可以如下構(gòu)造一個(gè)空的 StringBuilder 對(duì)象。
StringBuilder messages = new StringBuilder();
或者轿钠,可以指定一個(gè)初始字符串:
StringBuilder todoList = new StringBuilder("To do:\n");
這種特征叫做重載 (overloading)巢钓。如果多個(gè)方法有相同的名字、不同的參數(shù)疗垛,便產(chǎn)生了 overload症汹。編譯器必須挑選出具體執(zhí)行哪個(gè)方法,它通過用各個(gè)方法給出的參數(shù)類型與特定方法調(diào)用所使用的值類型進(jìn)行匹配來挑選出相應(yīng)的方法贷腕。要注意的是背镇,重載與返回值無關(guān),也就是說泽裳,方法名相同瞒斩,參數(shù)相同,返回值類型不相同是無法構(gòu)成 overload 的涮总。
默認(rèn)域初始化
如果在構(gòu)造器中沒有顯式地給域賦予初值胸囱,那么就會(huì)被自動(dòng)地賦為默認(rèn)值;數(shù)值為 0瀑梗、布爾值為 false烹笔、對(duì)象引用為 null。
這是全局變量與局部變量的主要不同點(diǎn)抛丽。必須明確地初始化方法中的局部變量谤职。但是,如果沒有初始化類中的全局變量亿鲜,將會(huì)被自動(dòng)初始化為默認(rèn)值 (0允蜈、false或null)。
無參數(shù)的構(gòu)造器
只有當(dāng)類沒有提供任何構(gòu)造器時(shí)蒿柳,系統(tǒng)才會(huì)提供一個(gè)默認(rèn)的無參構(gòu)造器饶套。
顯式域初始化
class Employee{
private String name = "";
}
在執(zhí)行構(gòu)造器之前,會(huì)先執(zhí)行顯式賦值操作其馏。當(dāng)一個(gè)類的所有構(gòu)造器都希望把相同的值賦予某個(gè)特定的實(shí)例域時(shí)凤跑,這種方法特別有用。
參數(shù)名
在編寫很小的構(gòu)造器時(shí)叛复,常常在參數(shù)命名上出現(xiàn)錯(cuò)誤。
通常,參數(shù)用單個(gè)字符命名褐奥,但是我們推薦這樣做:
public Employee(String aName, double aSalary){
name = aName;
salary = aSalary;
}
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
調(diào)用另一個(gè)構(gòu)造器
關(guān)鍵字 this 引用方法的隱式參數(shù)咖耘。然而,這個(gè)關(guān)鍵字還有另外一個(gè)含義撬码。
如果構(gòu)造器的第一個(gè)語句形如 this(...)
儿倒,這個(gè)構(gòu)造器將調(diào)用同一個(gè)類的另一個(gè)構(gòu)造器。下面是個(gè)典型的例子:
public Employee(double s){
//call Employee(String, double)
this("Employee #" + nextId,s);
nextId++:
}
初始化塊
前面已經(jīng)講過兩種初始化數(shù)據(jù)域的方法:
- 在構(gòu)造器中設(shè)置值
- 在聲明中賦值
實(shí)際上呜笑,Java 還有第三種機(jī)制夫否,稱為初始化塊 (initialization block)。在一個(gè)類的聲明種叫胁,可以包含多個(gè)代碼塊凰慈。只要構(gòu)造類的對(duì)象,這些塊就會(huì)被執(zhí)行驼鹅。例如微谓,
class Employee
{
private static int nextId;
private int id;
private String name;
private double salary;
{
d = nextId;
nextId++:
}
public Employee(String n, double s)
{
name = n ;
salary = s;
}
public Employee()
{
name = "";
salary = 0;
}
}
通常會(huì)直接將初始化代碼放在構(gòu)造器中。
由于初始化數(shù)據(jù)域有多種途徑输钩,所以列出構(gòu)造過程的所有路徑可能相當(dāng)混亂豺型,下面是具體處理步驟:
- 所有數(shù)據(jù)域被初始化為默認(rèn)值 (0、false 或 null )买乃。
- 按照在類聲明中出現(xiàn)的次序姻氨,依次執(zhí)行所有域初始化語句和初始化塊。
- 如果構(gòu)造器第一行調(diào)用了第二個(gè)構(gòu)造器剪验,則執(zhí)行第二個(gè)構(gòu)造器主體哼绑。
- 執(zhí)行這個(gè)構(gòu)造器的主體。
還可以使用靜態(tài)代碼塊碉咆,在類第一次加載的時(shí)候抖韩,將會(huì)進(jìn)行靜態(tài)域的初始化。
Random API
- Random() //構(gòu)造一個(gè)新的隨機(jī)數(shù)生成器
- int nextInt(int n)//返回一個(gè) 0~ n-1 之間的隨機(jī)數(shù)
包
Java 允許使用 包 (package) 將類組織起來疫铜,借助于包可以方便地組織自己的代碼茂浮。使用包的主要原因是確保類名的唯一性。
類的導(dǎo)入
一個(gè)類可以使用所屬包中的所有類壳咕,以及其他包中的公有類 (public class)席揽,我們可以采用兩種方式訪問另一個(gè)包中的共有類。第一種方式是在每個(gè)類名之前添加完整的包名谓厘。
java.time.LocalDate today = java.time.LocalDate.now();
這樣比較麻煩幌羞,我們推薦使用 import
導(dǎo)包,import 語句應(yīng)該位于源文件的頂部 (但位于 package 語句的后面)竟稳。例如属桦,可以使用下面這條語句導(dǎo)入 java.util 包種所有的類熊痴。
import java.util.*;
,也可以導(dǎo)入特定的類 import java.time.LocalDate;
靜態(tài)導(dǎo)入
import 不僅可以導(dǎo)入類聂宾,還增加導(dǎo)入靜態(tài)方法和靜態(tài)域的功能果善。
例如:import static java.lang.System.*;
我們就可以使用 System 類的靜態(tài)方法和靜態(tài)域,而不必加類名前綴:
out.println("Goodbye,World!");
//還可以導(dǎo)入特定的方法或域
import static java.lang.System.out;
將類放入包中
要想將一個(gè)類放入包中系谐,必須將包的名字放在源文件的開頭巾陕,包中定義類的代碼之前。
package com.horstmann.corejava;
public class Employee{
}
包作用域
標(biāo)記為 public 的部分可以被任意的類使用纪他;標(biāo)記為 private 的部分只能被定義它們的類使用鄙煤。如果沒有指定,則為 default茶袒,表示可以被同一個(gè)包中的所有方法訪問梯刚。
文檔注釋
JDK 包含一個(gè)很有用的工具,叫做 javadoc弹谁。它可以由源文件生成一個(gè) HTML 文檔乾巧。如果在源代碼種 添加以專用的 /**
開始注釋,那么可以很容易地生成一個(gè)看上去具有專業(yè)水準(zhǔn)的文檔预愤。
注釋的插入
javadoc 從下面幾個(gè)特性種抽取信息:
- 包
- 公有類與接口
- 公有的和受保護(hù)的構(gòu)造器及方法
- 公有的和受保護(hù)的域
每個(gè)/** */
文檔注釋在標(biāo)記之后緊跟著自由格式文檔沟于。標(biāo)記由 @ 開始,如 @author 或 @param植康。在自由格式文本種旷太,可以使用 HTML 修飾符,例如:<em> </em>
销睁,<strong> </strong>
供璧。
類注釋
類注釋必須放在 import 語句之后,類定義之前冻记。
package com.example;
/**
* Afdfsdfsdfsdf
* sdfsdfsdfsdfs
* fsdfdssfsdfsf
*/
public class PackageTest {
方法注釋
每一個(gè)方法注釋必須放在所描述的方法之前睡毒。除了通用標(biāo)記之外,還可以使用下面的標(biāo)記:
- @param 變量描述
- @return 描述
- @throws 類描述
下面是一個(gè)方法注釋的實(shí)例:
/**
* 我用來說明方法的概要
* @param s 我用來說明參數(shù)的作用
* @param g 我用來說明參數(shù)的作用
* @return 我用來說明返回值的作用
*/
public int gogogo(String s , int g ){
return 4;
}
域注釋
只需要對(duì)公有域 (通常指的是靜態(tài)常量) 建立文檔冗栗。例如
/**
* 我用來說明常量作用
*/
public static final int YEAR = 5;
通用注釋
下面的標(biāo)記可以用在類文檔的注釋中演顾。
- @author 姓名,將產(chǎn)生一個(gè) "author" 條目隅居,可以使用多個(gè)钠至。
- @version 版本,這個(gè)標(biāo)記將產(chǎn)生一個(gè) "version" 條目
- @since 這個(gè)標(biāo)記將產(chǎn)生一個(gè) "since" 條目胎源,這里的 text 可以是對(duì)引入特性的版本描述棉钧。
- @deprecated 這個(gè)標(biāo)記將對(duì)類、方法或變量添加一個(gè)不再使用的注釋涕蚤。
- @see 引用宪卿,它可以用于類中的诵,也可以用于方法中。它有三種情況
- package.class#feature label
<a href="...">label</a>
- "text"
@see 的第一種情況最常見愧捕。只要提供類奢驯、方法或變量的名字申钩,javadoc 就在文檔中插入一個(gè)超鏈接次绘。例如:
@see com.horstmann.corejava.Employee#raiseSalary(double)
@see com.example.GoGoGo#fuck(String)
@see GoGoGo#fuck(String)
也可以省略包名。
如果 @see 標(biāo)記后面有一個(gè)<
字符撒遣,就需要指定一個(gè)超鏈接邮偎,可以超鏈接到任何 URL。
@see <a href ="wwww.baidu.com">百度</a>
@see "百度"
如果愿意的話义黎,還可以在注釋中的任何位置放置指向其他類或方法的超級(jí)鏈接禾进,以及插入一個(gè)專用的標(biāo)記,例如:
{@link GoGoGo#fuck(String) label}
包與概述注解
類廉涕、方法泻云、變量的注釋都可以放置在 Java 源文件中,但是要想產(chǎn)生包注釋狐蜕,就需要在每一個(gè)包目錄中添加一個(gè)單獨(dú)的文件宠纯。可以有如下兩個(gè)選擇:
- 提供一個(gè)以 package.html 命名的 HTML 文件层释。在標(biāo)記
<body>..</body>
之間的所有文本都會(huì)被抽取出來婆瓜。 - 提供一個(gè)以 package-info.java 命名的 Java 文件。這個(gè)文件必須包含一個(gè)初始的以
/** */
界定的 Javadoc 注釋贡羔,跟隨在一個(gè)包語句之后廉白。它不應(yīng)該包含更多的代碼或注釋。
還可以為所有的源文件提供一個(gè)概述性的注釋乖寒,這個(gè)注釋將被放置在一個(gè)名為overview.html
的文件中猴蹂,這個(gè)文件位于包含所有源文件的父目錄中。標(biāo)記<body>..</body>
之間的所有文件將被抽取出來楣嘁。當(dāng)用戶從導(dǎo)航欄中選擇“Overview”時(shí)磅轻,就會(huì)顯示出這些注釋內(nèi)容。
類設(shè)計(jì)技巧
- 一定要保證數(shù)據(jù)私有:將全局變量設(shè)置為 private马澈。
- 一定要對(duì)數(shù)據(jù)初始化
- 不要在類中使用過多的基本類型
- 不是所有的變量都需要
get
瓢省、set
方法 - 將職責(zé)過多的類進(jìn)行分解
- 類名和方法名要能夠體現(xiàn)它們的職責(zé)
- 優(yōu)先使用不可變的類
第六章接口、lambda表達(dá)式與內(nèi)部類
接口
接口概念
在 Java 中痊班,接口不是類勤婚,而是對(duì)類的一組需求描述,這些類要遵從接口描述的統(tǒng)一格式進(jìn)行定義涤伐。
例如:Arrays 類中的 sort 方法承諾可以對(duì)對(duì)象數(shù)組進(jìn)行排序馒胆,但要求滿足下列前提:對(duì)象所屬的類必須實(shí)現(xiàn)了 Comparable 接口缨称。
//這是 Comparable 的代碼
public interface Comparable<T>
{
int compareTo(T other);
}
接口中的所有方法自動(dòng)地屬于 public。因此祝迂,在接口中聲明方法時(shí)睦尽,不必提供關(guān)鍵字 public。接口可以定義常量型雳,接口絕不能含有實(shí)例域当凡。
- Comparable<T>
- int compareTo(T other):建議實(shí)現(xiàn)用這個(gè)對(duì)象與 other 進(jìn)行比較。如果這個(gè)對(duì)象小于 other 則返回負(fù)值纠俭;如果相等則返回0沿量;否則返回正值。
- Arrays
- static void sort(Object[] a):使用 mergesort 算法對(duì)數(shù)組 a 中的元素進(jìn)行排序冤荆。要求數(shù)組中的元素必須屬于實(shí)現(xiàn)了 Comparable 接口的類朴则,并且元素之間必須是可比較的牲剃。
- Integer & Double
- static int compare(int x, int y)
- static int compare(double x, double y)
- x < y 返回 -1丰榴,x=y 返回 0纹烹,x>y 返回 1
接口的特性
接口不是類谤逼,尤其不能使用 new 實(shí)例化一個(gè)接口滔迈。
然而袋马,盡管不能構(gòu)造接口的對(duì)象烂斋,卻能聲明接口的變量:Comparable x;
接口變量必須引用了實(shí)現(xiàn)接口的類對(duì)象:x = new Employee()
接下來辽俗,可以使用 instanceof 檢測一個(gè)對(duì)象是否實(shí)現(xiàn)了某個(gè)特定的接口
if(anObject instanceof Comparable)
接口可以實(shí)現(xiàn)繼承坐榆,而且可以多繼承拴魄。
雖然在接口中不能包含實(shí)例域或靜態(tài)方法,卻可以包含常量席镀,接口中的域?qū)⒈蛔詣?dòng)設(shè)為 public static final
匹中。
盡管每個(gè)類只能擁有一個(gè)父類,但卻可以實(shí)現(xiàn)多個(gè)接口豪诲。
接口與抽象類
接口與抽象類的目的差不多顶捷,都想讓實(shí)現(xiàn)類實(shí)現(xiàn)自己的抽象方法,但 Java 是單繼承的屎篱,如果沒有接口服赎,只有抽象類,那一個(gè)類繼承完抽象類后交播,就不能再繼承其他類了重虑,所以接口顯得更加靈活。
靜態(tài)方法
在 Java8 中秦士,允許在接口中添加靜態(tài)方法缺厉。
默認(rèn)方法
可以為接口方法提供一個(gè)默認(rèn)實(shí)現(xiàn)。必須用 default 修飾符標(biāo)記這個(gè)方法。不過一般沒有什么作用提针,因?yàn)閷?shí)現(xiàn)了接口的類往往會(huì)重新實(shí)現(xiàn)這個(gè)方法命爬,如果設(shè)置了 default,那么在實(shí)現(xiàn)接口的時(shí)候就不會(huì)強(qiáng)制要求你實(shí)現(xiàn)這個(gè)抽象方法了辐脖。
default int compareTo(Object other) {
return 0;
}
解決默認(rèn)方法沖突
如果先在一個(gè)接口中將一個(gè)方法定義為默認(rèn)方法饲宛,然后又在父類或另一個(gè)接口中定義了同樣的方法,會(huì)發(fā)生什么情況嗜价?
- 父類優(yōu)先艇抠。如果父類提供了一個(gè)具體方法,同名而且有相同參數(shù)類型的默認(rèn)方法會(huì)被忽略炭剪。
- 接口沖突练链。如果一個(gè)父接口提供了一個(gè)默認(rèn)方法翔脱,另一個(gè)接口提供了一個(gè)同名而且參數(shù)類型相同的方法奴拦,必須 override 這個(gè)方法來解決沖突。
//兩個(gè)接口届吁,同樣的方法错妖,一個(gè)設(shè)置為 default
public interface Named {
default String getName() {
return “d“;
}
}
interface Person {
String getName();
}
//當(dāng)你同時(shí)實(shí)現(xiàn)這兩個(gè)接口的時(shí)候,編譯器會(huì)強(qiáng)制要求你實(shí)現(xiàn)一個(gè) getName() 方法
//而不是直接使用 Named 的 default 方法
class Student implements Named,Person{
}
那么父類和接口擁有同樣的方法會(huì)發(fā)生什么呢疚沐?
public interface Named {
default String getName() {
return ”d“;
}
}
public class Student extends Person implements Named {
}
class Person {
public String getName() {
return ”superClass“;
}
}
此時(shí)的話暂氯,是“類優(yōu)先”的,無論 Named
的方法加不加 default
亮蛔,父類的方法都會(huì) override 接口的方法痴施。
接口實(shí)例
接口與回調(diào)
回調(diào) (callback) 是一種常見的程序設(shè)計(jì)模式。在這種模式中究流,可以指出某個(gè)特定事件發(fā)生時(shí)應(yīng)該采取的動(dòng)作辣吃。
Comparator 接口
之前我們已經(jīng)了解了如何對(duì)一個(gè)對(duì)象數(shù)組排序,前提是這些對(duì)象是實(shí)現(xiàn)了 Comparator 接口的類的實(shí)例芬探。例如神得,可以對(duì)一個(gè)字符串?dāng)?shù)組排序,因?yàn)?String 類實(shí)現(xiàn)了 Comparable<String>
偷仿,而且 String.compareTo
方法可以對(duì)字典順序比較字符串哩簿。
public static void main(String[] args) {
String[] staff = { "fgfgfgf", "v", "zdgdgdgdgdgdgdg", "adgdgdgfgdgdgdgdgdg", "d", "m", "q" };
Arrays.sort(staff);
System.out.println(Arrays.toString(staff));
}
//[adgdgdgfgdgdgdgdgdg, d, fgfgfgf, m, q, v, zdgdgdgdgdgdgdg]
//是用字典順序規(guī)律排序的,無視長度
假設(shè)我們現(xiàn)在希望用長度遞增的順序?qū)ψ址M(jìn)行排序酝静,而不是按字典順序進(jìn)行排序节榜,如果要實(shí)現(xiàn)這種情況,可以選用 Arrays.sort
方法的第二個(gè)版本别智,有一個(gè)數(shù)組和一個(gè)比較器 (comparator) 作為參數(shù)宗苍,comparator 是實(shí)現(xiàn)了 Comparator 接口的類的實(shí)例。
public class LengthComparator implements Comparator<String> {
@Override public int compare(String s, String t1) {
return s.length() - t1.length();
}
}
public class EmployeeSortTest {
public static void main(String[] args) {
String[] staff = { "fgfgfgf", "v", "zdgdgdgdgdgdgdg", "adgdgdgfgdgdgdgdgdg", "d", "m", "q" };
Arrays.sort(staff);
System.out.println(Arrays.toString(staff));
Arrays.sort(staff, new LengthComparator());
System.out.println(Arrays.toString(staff));
}
}
//
[adgdgdgfgdgdgdgdgdg, d, fgfgfgf, m, q, v, zdgdgdgdgdgdgdg]
[d, m, q, v, fgfgfgf, zdgdgdgdgdgdgdg, adgdgdgfgdgdgdgdgdg]
對(duì)象克隆
本節(jié)會(huì)討論 Cloneable
接口亿遂,這個(gè)接口指示一個(gè)類提供了一個(gè)安全的 clone 方法浓若。(克隆并不太常見渺杉,可以稍作了解,等真正需要時(shí)再深入學(xué)習(xí))挪钓。先來回憶為一個(gè)包含對(duì)象引用的變量建立副本時(shí)會(huì)發(fā)生什么是越。原變量和副本都是同一個(gè)對(duì)象的引用。這說明碌上,任何一個(gè)變量改變都會(huì)影響另一個(gè)變量倚评。
Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(10);
此時(shí),改變 copy
的狀態(tài)馏予,就會(huì)改變 original
的狀態(tài)天梧。如果我們希望 copy
是一個(gè)新對(duì)象,它的初始狀態(tài)與 original
相同霞丧,但是之后它們各自會(huì)有自己不同的狀態(tài)呢岗,這種情況下就可以使用 clone
方法。
Employee original = new Employee("John Public", 50000);
//Employee copy =original.clone();
//copy.raiseSalary(10); //此時(shí) original 不會(huì)發(fā)生改變
不過并沒有這么簡單蛹尝。clone
方法是 Object
的一個(gè) protected
方法后豫,如果我們使用從 Object
繼承得到的 clone
方法,從 A 克隆出一個(gè) B 的話突那,它們的域如果都是基本數(shù)據(jù)類型的話挫酿,那么是可以實(shí)現(xiàn)互不干涉的,但是假設(shè)它們的域中包含引用對(duì)象愕难,那么 A 和 B 的引用對(duì)象域仍然會(huì)存在共享的情況早龟。這種默認(rèn)的 clone
操作叫做淺拷貝,存在缺陷猫缭。
不過葱弟,通常子對(duì)象都是可變的,必須重新定義 clone 方法來建立一個(gè)深拷貝饵骨,同時(shí)克隆所有子對(duì)象翘悉。
對(duì)于一個(gè)類需要確定:
- 默認(rèn)的 clone 方法是否滿足要求;
- 是否可以在可變的子對(duì)象上調(diào)用 clone 來修補(bǔ)默認(rèn)的 clone 方法居触;
- 是否不該使用 Clone妖混。
如果選擇第 1 項(xiàng)或第 2 項(xiàng),類必須: - 實(shí)現(xiàn)
Cloneable
接口 - 重新定義 clone 方法轮洋,并指定 public 修飾符制市。
Cloneable
接口是 Java 提供的一組標(biāo)記接口 (tagging interface) 之一。也可以稱為記號(hào)接口 (marker interface)弊予。
即使 clone 的淺拷貝用法也能夠滿足要求祥楣,還是需要實(shí)現(xiàn)Cloneable
接口,將 clone 重新定義為 public,再調(diào)用super.clone()
误褪。下面是個(gè)例子责鳍。
class Employee implements Cloneable{
@Override
public Employee clone() throws CloneNotSupportedException {
return (Employee) super.clone();
}
}
下面來看一個(gè)深拷貝的 clone 方法的例子:
@Override
public CloneTest clone() throws CloneNotSupportedException {
CloneTest copy = (CloneTest) super.clone();
copy.mEmployee = mEmployee.clone();
return copy;
}
另外,所欲偶的數(shù)組類型都有一個(gè) public 的 clone 方法兽间,而不是 protected历葛。可以用這個(gè)方法建立一個(gè)新數(shù)組嘀略,包含原數(shù)組所有元素的副本恤溶,例如:
int[] luckyNumbers= {2,3,5,7,11,13}
int[] cloned = luckyNumbers.clone();
cloned[5] = 12; //不會(huì)改變 luckyNumbers[5] 的數(shù)值
lambda 表達(dá)式
了解如何使用 lambda 表達(dá)式采用一種簡潔的語法定義代碼塊,以及如何編寫處理 lambda 表達(dá)式的代碼帜羊。
Why lambda ?
lambda 表達(dá)式是一個(gè)可傳遞的代碼塊咒程,可以在以后執(zhí)行一次或多次。在 Java 中讼育,不能直接傳遞代碼塊帐姻。必須構(gòu)造一個(gè)對(duì)象,這個(gè)對(duì)象的類需要有一個(gè)方法能包含所需的代碼窥淆。lambda 的設(shè)計(jì)卖宠,是為了解決 Java 如何做到函數(shù)式編程。
lambda 表達(dá)式的語法
(String first, String second) -> first.length() - second.length()
參數(shù)忧饭,箭頭 → 以及一個(gè)表達(dá)式。如果代碼要完成的計(jì)算無法放在一個(gè)表達(dá)式中筷畦,就可以像寫方法一樣词裤,把這些代碼放在 {} 中,并包含顯式的 return 語句鳖宾。例如:
(String first, String second) ->
{
if(first.length() < second.length()) return -1;
else if(first.length() > second.length()) return 1;
else return 0;
}
即使 lambda 表達(dá)式?jīng)]有參數(shù)吼砂,仍然要提供空括號(hào),就像無參數(shù)方法一樣
() -> {for (int i=100;i>=0;i--) System.out.println(i);}
如果可以推導(dǎo)出一個(gè) lambda 表達(dá)式的參數(shù)類型鼎文,則可以忽略其類型渔肩。例如:
Comparator<String> comp = (first, second) ->first.length() - second.length();
在這里,編譯器可以推導(dǎo)出 first 和 second 必然是字符串拇惋,因?yàn)檫@個(gè) lambda 表達(dá)式將賦給一個(gè)字符串比較器周偎。
如果方法只有一個(gè)參數(shù),而且這個(gè)參數(shù)的類型可以推導(dǎo)得出撑帖,那么甚至還可以省略小括號(hào)蓉坎;
ActionListener listener = event -> System.out.println("The time is"+new Date());
無需指定 lambda 表達(dá)式的返回類型。lambda 表達(dá)式的返回類型總是由上下文推導(dǎo)得出胡嘿。
(String first, String second) -> first.length() - second.length()
蛉艾,可以在需要 int 類型結(jié)果的上下文中使用。
public static void main(String[] args) {
String[] planets =
new String[] { "Mercury", "Venus", "Earth", "Jupiter", "Saturn", "Uranus", "Neptune" };
System.out.println(Arrays.toString(planets));
System.out.println("Sorted in dictionary order:");
Arrays.sort(planets);
System.out.println(Arrays.toString(planets));
System.out.println("Sorted by length");
Arrays.sort(planets, (first, second) -> first.length() - second.length());
System.out.println(Arrays.toString(planets));
}
函數(shù)式接口
對(duì)于只有一個(gè)抽象方法的接口,需要這種接口的對(duì)象時(shí)勿侯,就可以提供一個(gè) lambda 表達(dá)式拓瞪。這種接口稱為函數(shù)式接口 (functionnal interface)。最好把 lambda 表達(dá)式看作是一個(gè)函數(shù)助琐,而不是一個(gè)對(duì)象吴藻,另外要接受 lambda 表達(dá)式可以傳遞到函數(shù)式接口。
lambda 表達(dá)式可以轉(zhuǎn)換為接口弓柱,這一點(diǎn)讓 lambda 表達(dá)式很有吸引力沟堡。
//這是將 lambda 轉(zhuǎn)換為函數(shù)式接口的例子
BiFunction<String, Integer, Boolean> comp = (name, age) -> name.length() > age;
ArrayList 類有一個(gè) removeIf 方法,它的參數(shù)就是一個(gè) Predicate矢空。
public interface Predicate<T> {boolean test(T t);}
航罗,這也是一個(gè)函數(shù)式接口,所以我們可以使用 lambda屁药。
ArrayList<String> a = new ArrayList<>();
a.add(null);
a.add(null);
a.add(null);
a.add(null);
a.add("dsada");
a.add("czxzd");
a.add("gadga");
a.add("zcbzc");
a.removeIf(e -> e == null);
for (int i = 0; i < a.size(); i++) {
System.out.println(a.get(i));
}
//Predicate 的泛型是根據(jù) ArrayList 的泛型的粥血,這里的代碼就是將 ArrayList 中的 null 值都刪除了。
方法引用
有時(shí)酿箭,可能已經(jīng)有現(xiàn)成的方法可以完成你想要傳遞到其他代碼的某個(gè)動(dòng)作复亏。
Timer t = new Timer(10000,System.out::println)
,等價(jià)于 x -> System.out.println(x)
再來看一個(gè)例子,假設(shè)你想對(duì)字符串排序缭嫡,而不考慮字母的大小寫缔御。可以傳遞以下方法表達(dá)式:
Arrays.sort(strings,String::compareToIgonreCase)
從這些例子可以看出妇蛀,要用 ::
操作符分隔方法名與對(duì)象或類名耕突。主要有 3 種情況:
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
在前 2 種情況種,方法引用等價(jià)于提供方法參數(shù)的 lambda 表達(dá)式评架。前面已經(jīng)提到眷茁,System.out::println
等價(jià)于x -> System.out.println(x)
。類似地纵诞,Math::pow
等價(jià)于(x,y) ->Math.pow(x,y)
上祈。
對(duì)于第 3 種情況,第 1 個(gè)參數(shù)會(huì)成為方法的目標(biāo)浙芙。
例如登刺,String::compareToIgnoreCase
等同于(x,y) ->x.compareToIgnoreCase(y)
。
可以在方法引用中使用 this 參數(shù)茁裙。例如塘砸,this::equals
等同于x-> this.equals(x)
。使用 super 也是合法的晤锥。下面的方法表達(dá)式
super:instanceMethod
構(gòu)造器引用
構(gòu)造器引用與方法引用很類似掉蔬,只不過方法名為 new廊宪。例如,Person::new
是 Person 構(gòu)造器的一個(gè)引用女轿。哪一個(gè)構(gòu)造器呢箭启?這取決于上下文。
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
Stream<Person> stream = names.stream().map(Person::new);
}
}
class Person {
Person(String name) {
重點(diǎn)是 map 方法會(huì)為各個(gè)列表元素調(diào)用 Person(String) 構(gòu)造器蛉迹。
可以用數(shù)組類型建立構(gòu)造器引用傅寡。例如,int[]::new
是一個(gè)構(gòu)造器引用北救,它有一個(gè)參數(shù):即數(shù)組的長度荐操。這等價(jià)于 lambda 表達(dá)式 x -> new int[x]
。
@FunctionalInterface interface Fuck {
int[] createIntegerArray(int length);
}
public static void main(String[] args) {
Fuck fuck = int[]::new;
}
Java 有一個(gè)限制珍策,無法構(gòu)造泛型類型 T 的數(shù)組托启。數(shù)組構(gòu)造器引用對(duì)于客服這個(gè)限制很有用。表達(dá)式 new T[n]
會(huì)產(chǎn)生錯(cuò)誤攘宙,因?yàn)檫@會(huì)改為 new Object[n]屯耸。不過 toArray
有一個(gè)重載方法,引用一個(gè)函數(shù)式接口蹭劈,解決了這個(gè)問題疗绣。
Person[] people = stream.toArray(Person[]::new);
變量作用域
通常,你可能希望能夠在 lambda 表達(dá)式中訪問外圍方法或類中的變量铺韧。
public static void repeatMessage(String test, int delay) {
ActionListener listener = event -> System.out.println(test);//這里只是實(shí)現(xiàn)了接口的方法而已多矮,并沒有馬上調(diào)用
test = "Change";//當(dāng)我們改變test值的時(shí)候,編譯器會(huì)報(bào)錯(cuò)
}
Variable used in lambda expression should be final or effectively final
lambda 表達(dá)式有 3個(gè)部分:
- 一個(gè)代碼塊
- 參數(shù)
- 自由變量的值祟蚀,這是指非參數(shù)而且不在代碼中定義的變量工窍。
在我們的代碼中,lambda 有 1 個(gè)自由變量 test前酿。表示 lambda 的數(shù)據(jù)結(jié)構(gòu)必須存儲(chǔ)自由變量的指。在這里就是字符串 test鹏溯。我們說它被 lambda 捕獲 (captured)罢维。例如,可以把一個(gè) lambda 轉(zhuǎn)換為包含一個(gè)方法的對(duì)象丙挽,這樣自由變量的值就會(huì)復(fù)制到這個(gè)對(duì)象的實(shí)例變量中肺孵。
關(guān)于代碼塊以及自由變量值有一個(gè)術(shù)語:閉包 (closure),在 Java 中颜阐,lambda 表達(dá)式就是閉包平窘。
在 lambda 中,只能引用值不會(huì)改變的變量凳怨,也不能在 lambda 中改變自由變量的值瑰艘。
在一個(gè) lambda 中使用 this 關(guān)鍵字時(shí)是鬼,是指創(chuàng)建這個(gè) lambda 的方法的 this 參數(shù)。
public class Application()
{
public void init()
{
ActionListener listener = event ->
{
System.out.println(this.toString());
}
}
}
這里會(huì)調(diào)用 Application 對(duì)象的 toString 方法紫新,而不是 ActionListener 實(shí)例的方法均蜜。
處理 lambda 表達(dá)式
下面來看如何編寫方法處理 lambda 表達(dá)式。
使用 lambda 的重點(diǎn)是延遲執(zhí)行 (deferred execution)芒率。之所以希望以后再執(zhí)行代碼囤耳,這有很多原因
- 在一個(gè)單獨(dú)的線程中運(yùn)行代碼
- 多次運(yùn)行代碼
- 在算法適當(dāng)位置運(yùn)行代碼
- 發(fā)生某種情況執(zhí)行代碼,點(diǎn)擊了一個(gè)按鈕偶芍,數(shù)據(jù)到達(dá)充择,等等
- 只在必要時(shí)才運(yùn)行代碼
可以為你自己設(shè)計(jì)的函數(shù)式接口打上@FunctionalInterface
標(biāo)記。