1. Java編程基礎
1.1. 基本概念
Ø 什麼是計算機語言
計算機語言指用於人與計算機之間通訊的語言。計算機語言是人與計算機之間傳遞信息的媒介。爲了使電子計算機進行各種工作,就需要有一套用於編寫計算機程序的數字、字符和語法規劃,由這些組成計算機指令就是計算機語言。
軟件就是由若干條計算機語言所組成的。
Ø 計算機語言分類
機器語言:
機器語言是直接用二進制代碼指令表達的計算機語言,指令是用0和1組成的一串代碼,它們有一定的位數,並分成若干段,各段的編碼表示不同的含義。
彙編語言:
彙編語言是使用一些特殊的符號來代替機器語言的二進制碼,計算機不能直接識別,需要用一種軟件將彙編語言翻譯成機器語言。
高級語言:
使用普通英語進行編寫源代碼,通過編譯器將源代碼翻譯成計算機直接識別的機器語言,之後再由計算機執行。
Ø 高級語言工作原理
1.2. Java開發環境搭建
Ø JDK與JRE
JDK(Java Development Kit) Java開發工具,包含開發Java程序的所有組件,包含JRE
JRE(Java Runtime Environment) Java運行環境,如果要運行Java程序,就需要JRE的支持
常用組件:
src.zip Java是一門開源的語言,其源代碼都在這個壓縮包中
rt.jar Java的基礎核心類庫,我們編寫Java程序時使用的class都在這個jar包中
javac.exe 編譯器,將.java源代碼編譯爲.class文件
java.exe 虛擬機,運行Java程序的工具
jar.exe 將class文件打成jar包的工具
javadoc.exe 生成幫助文檔的工具
Ø 環境變量:環境變量是指在操作系統中用來指定操作系統運行環境的一些參數
path:
如果想在任意目錄下運行一個程序,我們就需要將程序所在的路徑配置在path環境變量中。
通常我們會將javac.exe所在目錄配置到path中,因爲我們需要在任意目錄下都能編譯Java源文件。
配置完成之後可以在命令行輸入javac測試,如果顯式幫助信息則是配置成功。
classpath:
Java虛擬機運行時加載類的路徑。JDK5之後不配置默認爲當前目錄“.”。如使用JDK1.4或以下版本時需要人工配置。
暫時不需要配置,默認加載當前目錄下的所有class文件。
配置方式:
a. 命令行
點擊屏幕左下角開始– 運行– 輸入cmd – 在命令行中直接輸入命令進行修改
查看變量值:set 變量名
設置變量值:set 變量名=變量值,多個值之間使用分號“;”分割,引用變量時使用“%變量名%”形式
注意:此種方式僅適用於當前窗口
b. 我的電腦
鼠標右鍵點擊我的電腦 – 屬性 – 高級 – 環境變量
找到要修改的變量將其值修改,此種方式永久有效
注意:
配置環境變量之後可以查看編譯器(javac.exe)和虛擬機(java.exe)版本,虛擬機版本不能低於編譯器。
使用哪個版本的編譯器和虛擬機取決於path環境變量,如果虛擬機版本過低,可以通過環境變量來修改。
編譯器版本查看方式:javac –version
虛擬機版本查看方式:java –version
1.3. 第一個Java程序
Ø 編寫源代碼
新建文本文檔,擴展名改爲.java,在文件中寫入代碼。
注意:
windows操作系統默認是隱藏已知文件擴展名的。
請測試新建一個文本文檔,如果看到的文件名是“新建文本文檔”而不是“新建文本文檔.txt”,那麼說明你的擴展名被隱藏了。
請選擇菜單欄中的工具 – 文件夾選項– 查看 – 下拉滾動條找到“隱藏已知文件擴展名” – 取消掉這一項。
Ø 編譯字節碼文件
a. 左鍵單機屏幕左下角開始– 運行– 輸入cmd啓動命令行窗口
b. 使用DOS命令進入源代碼所在目錄
c. 使用編譯器(javac.exe)編譯源代碼,javac 文件名.java,編譯後在該目錄中會出現擴展名爲class的字節碼文件
常用DOS命令:
跳轉到指定盤符: 盤符: 例:C: D: E:
跳轉到指定目錄: cd 目錄 例:cd Itcast\day01 cd Tencent\QQ\Bin
顯示當前目錄下文件: dir
跳轉到上級目錄: cd..
跳轉到根目錄: cd\
清屏: cls
Ø 運行程序
使用虛擬機(java.exe)運行class文件,java 文件名,注意不要加擴展名,因爲虛擬機只能運行class文件,擴展名省略不寫,如寫則報錯。
1.4. 進制
Ø 十進制
由0到9的數字組成,逢十進一
我們最常用的一種進制
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Ø 二進制
由0和1組成,逢二進一
計算機中存儲任何數據都是以二進制的形式進行存儲的
0 1 10 11 100 101 110 111 1000
Ø 八進制
由0到7的數字組成,逢八進一
八進制在程序中以0開頭
0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20
Ø 十六進制
由0到9的數字和A-F的字母組成,逢十六進一
十六進制在程序中以0x開頭
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20
Ø 進制轉換
a. 十進制轉二、八、十六進制
除法取餘,將要轉換的數除以進制數,記住餘數,再除以進制數,記住餘數,直到這個數等於0爲止,將所有餘數反轉就是對應的二進制表現形式。
b.二、八、十六進制轉十進制
乘法,將要轉換的數編號,編號從低位開始,從0開始,將每一位上的數乘以進制數的編號次方,最後將所有乘得的結果相加就是十進制表現形式。
c.二進制和八進制互轉
八進制的每一位對應二進制的三位。
d. 二進制和十六進制互轉
十六進制的每一位對應二進制的四位。
Ø 二進制負數
一個負數的二進制表現形式就是這個負數忽略符號的正數對應的二進制取反再加一。
計算機中存儲的二進制數最高位是0則是正數,是1則是負數。
1.5. 碼錶
ASCII:英文碼錶,每個字符佔1個字節。A是65,a是97
GB2312:兼容ASCII,包含中文,每個英文佔1個字節(正數),中文佔2個字節(2個負數)。
GBK:兼容GB2312,包含更多中文,每個英文佔1個字節(正數),中文佔2個字節(第一個負數、第二個可正可負)。
Unicode:國際碼錶,每個字符佔2個字節。Java中存儲字符類型就是使用的Unicode編碼。
UTF-8:國際碼錶,英文佔1個字節,中文佔3個字節。
2. Java語法
2.1. 基本格式
所有Java代碼都應該在一個class中。
Java是嚴格區分大小寫的。
Java是一種自由格式的語言。Java代碼分爲結構定義語句和功能執行語句,功能執行語句最後必須以分號結束。
2.2. 註釋
單行註釋和多行註釋是在程序用來標記一些特殊的文本,這些文本不參與編譯運行。
文檔註釋是Java中特有的一種註釋,它可以通過JDK中的工具(javadoc.exe)解析,生成幫助文檔。
文檔註釋: /** 註釋內容 */
2.3. 標識符
Ø 什麼是標識符
標識符可以理解爲程序中我們自定義的一些名字,包括:包名、類名、函數名、變量名、常量名。
Ø 標識符的命名規則
由大小寫字母、數字、下劃線(_)和美元符號($)組成,開頭不能是數字。不能使用關鍵字。推薦使用全英文。
Ø 標識符通用規範
類名、接口名:
所有單詞首字母大寫,駝峯式命名,例如:XxxYyyZzz
變量名、函數名:
第一個單詞首字母小寫,其他單詞首字母大寫,駝峯式命名,例如:xxxYyyZzz
常量名:
所有字母都大寫,單詞之間用下劃線分割,例如:XXX_YYY_ZZZ
包名:
全部小寫,例如:xxx.yyy.zzz
2.4. 關鍵字
catch |
||||
short |
||||
throws |
||||
注:java 無sizeof ,goto, const 關鍵字,但不能用goto const作爲變量名
2.5. 常量
Ø 整型
整數,4個字節。
Ø 長整型
整數,8個字節。以L結尾。
Ø 單精度浮點數
小數,4個字節。以F結尾。
Ø 雙精度浮點數
小數,8個字節。
Ø 布爾
只有兩個值,真(true)或假(false),1個字節。
Ø 字符
單個字符,2個字節。例如:'a', '中', '5', '\u0026' , '\u0027'
在字符常量中,斜槓(\)是一個特殊的字符,它的作用是用來轉義後面一個字符,這些字符通常是不可見的或者有特殊意義的。
'\r' 回車,回到一行的開始
'\n' 換行,換到下一行
'\t' 製表符,鍵盤上的Tab
'\b' 類似退格,鍵盤上的Backspace
以上字符都不可見,無法直接表示,所以用斜槓加上另外一個字符來表示。
'\'' 單引號,Java代碼中單引號表示字符的開始和結束,如果直接寫程序會認爲前兩個是一對,報錯。
'\"' 雙引號,Java代碼中雙引號表示字符串的開始和結尾,如果要寫一個包含雙引號的字符串那麼這個雙引號也需要轉義。
'\\' 斜槓,Java代碼中的斜槓是轉義字符,用來和後面一個字符配合使用,在真正需要用斜槓的時候那麼就要用另一個斜槓來轉義。
以上字符都有特殊意義,無法直接表示,所以用斜槓加上另外一個字符來表示。
Ø 字符串
由若干個字符組成的一串。可以是一個字符、多個字符、或者一個都沒有。字符串沒有固定大小。
Ø 空
null,只有這一個值,用來表示一個引用爲空。
2.6. 變量
int x = 5;
System.out.println(x);
x = 1 + 1;
System.out.println(x);
x = x + 1;
System.out.println(x);
上面的x就是一個變量,變量沒有固定的值,是在內存中開闢的一片空間。
Java中的變量中只能存儲同一種類型的值。
變量在被取值之前必須初始化(第一次給變量賦值)。
Ø 變量分類
a.基本數據類型: 8種
整數:
byte 1個字節,最小值:-128,最大值:127
short 2個字節,最小值:-32768,最大值:32767
int 4個字節,最小值:-2147483648,最大值:2147483647
long 8個字節,最小值:- 9223372036854775808,最大值:9223372036854775807
浮點數:
float 4個字節,最小值:1.4E-45,最大值:3.4028235E38
double 8個字節,最小值:4.9E-324,最大值:1.7976931348623157E308
字符:
char 2個字節,最小值:0,最大值:65535
布爾:
boolean 1個字節,true或false
b.引用數據類型:
類、接口、數組都是引用數據類型,除了8種基本數據類型,其他所有類型都是引用數據類型。
Ø 類型轉化
a. 自動類型轉換
在byte、short、char參與運算的時候會自動提升爲int,相當於將一個佔空間較小的值放入了一個較大的空間。
b. 強制類型轉換
可以將一個佔空間較大的值使用(類型)的形式強制放入一個較小的空間,有可能損失精度。
c. 字符串轉換
任何值和字符串相加都會得到字符串。
Ø 變量的作用域與生命週期
作用域:變量定義在哪一級大括號中,哪個大括號的範圍就是這個變量的作用域。相同的作用域中不能定義兩個同名變量。
生命週期:變量的生命週期從定義時開始,超出作用域後結束。變量生命週期以外不能使用。
2.7. 函數
Ø 函數的定義
函數就是一段有名字的代碼,可以完成某一特定功能。
如果有一段代碼要使用多次,我們可以給它起個名字,每次使用時通過名字調用,這樣就不用每次都寫一大段代碼了。
如果某個函數在執行的時候需要調用者傳入數據,那麼可以定義參數列表,用於接收數據。
如果函數運行之後需要返回給調用者數據,那麼需要指定返回值類型,並且用關鍵字return返回。
定義函數的3個必要條件:函數名、參數列表、返回值類型。如果不需要參數也要寫小括號,如果沒有返回值類型要寫void。
Ø 名詞解釋
形參:在定義函數時小括號中的參數,用來接收數據的參數。
實參:在調用函數時真正傳入的參數,傳遞給函數的數據。
參數類型:函數的參數的類型,一旦定義傳入時必須匹配。
返回值:函數運行結束後返回的值,使用return關鍵字返回。
返回值類型:函數運行結束後返回的值的類型,在類型非void情況下必須返回,而且必須類型匹配。
Ø 函數的重載
多個函數的函數名相同,參數列表不同(個數、順序、類型),這就是函數的重載。在調用函數的時候通過傳入的實參找到匹配的函數調用。
函數的重載和返回值類型無關。
2.8. 運算符
Ø 算數運算符
加號:在操作數字、字符、字符串時是不同的,兩個字符相加得到的是碼錶值,兩個字符串相加是將字符串連接在一起。
除號:整數在使用除號操作時,得到的結果仍爲整數(小數部分忽略)。
取模:模數的符號忽略不計,結果的正負取決於被模數。
自增:符號在前就是先運算後取值,符號在後則是先取值後運算。
習題:
a.System.out.println(3500 / 1000 * 1000);
b.某個培訓中心要爲新到的學員安排房間,假設共有x個學員,每個房間可以住6人,讓你用一個公式來計算他們要住的房間數?
Ø 賦值運算符
等於:可以多個連用,例如:x = y = z = 5;
加等於:x += 5; 相當於 x = x + 5;
面試題:
以下代碼正確的是? (多選)
a. byte b = 1 + 1;
b. byte b = 1; b = b + 1;
c. byte b = 1; b += 1;
d. byte b = 1; b = ++b;
Ø 比較運算符
比較運算符運行結束之後返回的都是boolean值。
注意運算符==不要寫成=
Ø 邏輯運算符
邏輯運算符運行結束之後返回的也是boolean值
& 兩邊都爲true結果才爲true,只要有一邊是false,結果就是false
| 兩邊都爲false結果才爲false,只要有一邊是true,結果就是true
^ 判斷兩邊是否不同,不同則爲true,相同則爲false
! 取反,!true結果是false,!fasle結果是true
&& 和&結果相同,具有短路效果,如果前半是false,表達式結果一定爲false,不運行後一半
|| 和||結果相同,具有短路效果,如果前半是true,表達式結果一定爲true,不運行後一半
分析以下程序運行結果:
int x = 1;
int y = 2;
System.out.println(x++ == y & ++x > y++);
System.out.println(x);
System.out.println(y);
int x = 1;
int y = 2;
System.out.println(x++ == y && ++x > y++);
System.out.println(x);
System.out.println(y);
int x = 1;
int y = 2;
System.out.println(x++ == y | ++x > y++);
System.out.println(x);
System.out.println(y);
int x = 1;
int y = 2;
System.out.println(x++ == y || ++x > y++);
System.out.println(x);
System.out.println(y);
&& 在前半是false的時候短路
|| 在前半是true的時候短路
Ø 位運算符
任何信息在計算機中都是以二進制的形式保存的,&、|、^除了可以作爲邏輯運算符,也可以做爲位算符。
它們對兩個操作數中的每一個二進制位都進行運算,0當做false,1當做true。
& 將兩個二進制數每一位進行&運算,兩邊都爲1結果才爲1,只要有一邊是0,結果就爲0。
| 將兩個二進制數每一位進行|運算,兩邊都爲0結果才爲0,只要有一邊是1,結果就爲1。
^ 將兩個二進制數每一位進行^運算,只要兩邊不同結果就爲1,相同則爲0。
我們可以對數據按二進制位進行移位操作,java的移位運算符有三種:
<< 左移 將二進制的每一位向左移,低位補0。左移幾位就相當於乘以2的幾次方。
>> 右移 將二進制的每一位向右移,原來高位是0就補0,原來高位是1就補1。右移幾位就相當於除以2的幾次方。
>>> 無無符號右移 將二進制的每一位向右移,高位補0。正數移動沒區別,負數移動後變爲正數。
練習:
a. 用&和>>來做十進制轉十六進制
b. 有兩個int型變量a和b,在不使用第三個變量的情況下交換兩個變量中的值
Ø 運算符優先級
思考一下代碼運行結果:
System.out.println(1 + 2 * 3);
System.out.println(false && true || true);
System.out.println(true || true && false);
int a = 2;
int b = a + 3 * a++;
System.out.println(b);
int a = 2;
int b = a++ + 3 * a;
System.out.println(b);
int a = 1;
int b = 2;
System.out.println(a+++b);
儘量寫簡單的表達式,遇到運算符優先級的問題使用括號解決。
2.9. 語句
Ø 順序結構
顧名思義,就是程序從上到下一行一行執行的結構,中間沒有判斷和跳轉,直到程序結束。
Ø 選擇結構
程序具備多個分支,通過條件判斷決定程序選擇那一條分支執行
a. if語句:
通過if...else if...else決定程序流程。
如果if中的條件滿足則執行其中語句,if未滿足則繼續判斷else if,如果滿足則執行,不滿足繼續判斷下一個else if,如果所有都不滿足,則執行else。
練習:
用if else語句判斷一個數是奇數還是偶數。
用戶輸入一個字符,用程序判斷是否爲小寫字母,如果是,請輸出“您輸入的字符是小寫字母”。
b. switch語句:
通過switch...case...default語句控制程序流程。
根據switch後括號中的值判斷運行哪一個case,這個值可以是byte、short、ch;ar、int。
default語句是可選的,如果所有case都不滿足,則會執行default。
一旦匹配到一個case,程序就會從這個case向下執行,執行完一個case之後不會跳過其他的case,如需跳過請使用break。
c. 三元運算符
語法:表達式 ? 結果1 : 結果2
如果表達式結尾爲true取結果1,爲false則取結果2。
注意三元運算符也是有短路的效果,根據表達式的結果,只運行冒號一邊的,另外一邊的不參與運行。
練習:
定義一個函數,接收兩個int參數,返回較大的一個。
Ø 循環結構
通過循環語句讓同一段代碼反覆執行多次,執行完畢程序纔會繼續往後運行
a. while
先判斷while中的表達式結果是否爲true,true則執行循環體,執行結束之後再次判斷,如果表達式結果爲false則跳出循環。
練習:
打印出0-9
打印出a-z
b. do...while
先執行一次循環體,然後判斷while中的表達式,如果是true繼續執行,如果是false則跳出循環。
練習:
編寫一個程序,這個程序不斷地讀取鍵盤上輸入的字符,直到讀到字符’q’時,程序結束。
c. for
for循環的括號中有三條語句,都是可選項。
語句1:這條語句會在整個循環開始之前執行,且僅運行一次,不參與循環。
語句2:必須是一個返回boolean值的表達式,如果寫了這個語句那麼每次循環開始之前會判斷,true則執行循環,false則不執行。沒寫則直接執行。
語句3:這條語句在每次循環體運行結束之後執行。
練習:
使用星號打印如下圖案
*****
*****
*****
*****
*****
i *
* 0 1
** 1 2
*** 2 3
**** 3 4
***** 4 5
i 空格 *
* 0 4 1
*** 1 3 3
***** 2 2 5
******* 3 1 7
********* 4 0 9
d. continue、break、return
continue:跳過一次循環,繼續執行下一次
break:結束循環
return:結束方法
2.10. 數組
Ø 什麼是數組
數組是一個類型一致,長度不可變的容器。可以通過索引操作容器中的每一個元素。
如果有多個類型相同的數據需要存儲,我們就可以將其定義爲一個數組,這樣做省去了創建多個變量的麻煩。
Ø 如何定義數組
int[] arr = {1,2,3};
定義int數組arr,長度爲3,其中3個元素分別爲1、2、3。這種方式只能在定義數組的時候使用。
int[] arr = new int[]{1,2,3};
定義int數組arr,長度爲3,其中3個元素分別爲1、2、3。可以再任何情況使用。
int[] arr = new int[3];
定義int數組arr,長度爲3。其中所有元素都爲默認值0。
Ø 訪問數組元素、遍歷數組
存在數組中的數據是有索引的,從0開始遞增,我們通過數組名和索引就可以操作其中每一個元素。例如:
System.out.println(arr[0]); // 打印數組中索引爲0的元素
arr[1] = 100; // 給數組中索引爲1的元素賦值爲100
數組的長度
數組可以使用length屬性獲取其長度。
遍歷數組
由於數組可以通過索引獲取每一個元素,又可以通過length獲取長度,那麼我們就可以定義循環來遍歷數組中的每一個元素了。
Ø 使用數組時的異常
如果訪問數組時索引越界(小於0或者大於length-1),會拋出異常:ArrayIndexOutOfBoundsExcepion
如果訪問數組的引用爲空(null),會拋出空指針異常:NullPointerException
Ø 數組練習
a. 定義一個函數,將數組中所有元素打印。要求打印成一行,每個元素之間以逗號分隔。
b. 定義一個函數,交換數組中的兩個元素。
c. 定義一個函數,找出數組中的最大數。
d. 定義一個函數,將數組中所有元素反轉。例如:{1, 2, 3} 反轉後爲 {3, 2, 1}。
e. 定義一個函數,對數組進行排序。
Ø 與數組操作相關函數
Arrays.toString() 查找幫助文檔Arrays類,學習使用此方法將字符串轉爲字符串形式。
將一個數組轉爲字符串表示形式
System.arraycopy() 查找幫助文檔System類,學習使用此方法拷貝數組中元素。
將一個數組中的某些元素拷貝到另一個數組的指定位置
Ø 多維數組
數組中的每一個元素都是數組,這樣的數組就是多維數組。
int[][] arr = { { 1, 2, 3 }, { 4, 5 }, { 6, 7, 8, 9 } };
定義二維數組arr, 其中有三個元素都是數組, 第一個數組3個元素, 第二個2個元素, 第三個4個元素.
int[][] arr = newint[][] { { 1, 2, 3 }, { 4, 5 }, { 6, 7, 8, 9 } };
定義二維數組arr, 其中有三個元素都是數組, 第一個數組3個元素, 第二個2個元素, 第三個4個元素.
int[][] arr = newint[3][3];
定義二維數組arr, 其中有三個元素都是數組, 每個小數組都是3個元素.
int[][] arr = newint[3][];
定義二維數組arr, 其中有三個元素都是數組, 每個小數組元素個數不確定.
2.11. 綜合練習
a. 編寫一個程序,程序接收鍵盤上輸入的三個數,並輸出這三個數的最大數。
b. 編寫一個程序,它先將鍵盤上輸入的一個字符串轉換成十進制整數,然後打印出這個十進制整數對應的二進制形式。
c. 使用移位方式將一個十進制數轉換爲十六進制。三種方式:
0-9之間的數值直接加上字符'0',9以上的數值減去10以後再加上字符'A'
定義一個數組,其中包含0-F這些字符,然後用要計算的數值作爲數組的索引號,即可獲得其對應的十六進制數據。
Character.forDigit靜態方法可以將一個十六進制的數字轉變成其對應的字符表示形式,例如,根據數值15返回字符'F'。
3. 面向對象
3.1. 面向對象概念
Ø 什麼是面向對象面向對象(Object Oriented)是一種思想,90年代以後軟件開發的主流思想。
由於現實社會是由各種各樣的事物所組成的,而我們編程又是在模擬現實社會,那麼在程序也要用一些東西來表示現實社會中的事物,這些東西就是程序中的對象。我們在程序中使用這些對象,對其特徵和行爲進行操作進行編程,這就是面向對象編程。
在使用面向對象編程思想之前,我們通常用面向過程的思想編程,先分析出解決問題的步驟,然後按照步驟一步一步實現。
Ø 面向對象編程的優點
提高代碼複用性。
使用者無需關心具體細節。
轉變程序員角色,更加符合人的思維習慣。
3.2. 類與對象
Ø 什麼是類
類是用來描述對象的。由於對象是虛擬出來的東西,是看不見摸不着的,我們需要在程序中使用對象,就需要用一種方式來描述對象,然後根據這個描述來創建對象。
Ø 類和對象的關係
對象是類的實例,類是對象的抽象。
Ø 怎麼定義類
將一系列特徵相似的對象的共同特徵及行爲抽取出來進行描述,寫在一個class中,用成員變量描述對象的特徵,用成員方法來描述對象的行爲。
class Person {
String name;
int age;
void speak(){
System.out.println("My name is " + name);
System.out.println("I am " + age + " years of age");
}
}
Ø 怎麼使用類創建對象
使用new關鍵字和指定類名來創建一個對象。
Ø 對象的產生
Person p = new Person();
這句話先在堆內存中創建了一個對象,然後棧內存中創建一個變量引用了對象的地址。
Ø 成員變量初始化
當一個對象被創建時,會對其中各種類型的成員變量自動進行初始化賦值。基本數據類型初始化值爲0,引用數據類型初始化值爲null。
Ø 對象的使用
當我們創建對象之後可以使用點語法來訪問對象的屬性和方法。例如:
Person p = new Person();
p.name = "張三"; // 訪問屬性(成員變量)
p.age = 20;
p.speak(); // 訪問方法
Ø 對象的生命週期
對象的生命週期從new關鍵字創建時開始,到沒有任何引用到達對象時結束(成爲垃圾)。
Ø 匿名對象
我們可以不定義變量引用對象,使用new關鍵字創建對象後直接使用,這樣的對象沒有名字,所以叫匿名對象。
匿名對象因爲沒有任何引用到達,在使用一次之後即成爲垃圾。
通常我們需要使用一個對象且只使用一次的時候,就可以使用匿名對象。比如將對象作爲一個參數傳遞給另外一個函數。
3.3. 封裝(Encapsulation)
Ø 什麼是封裝
封裝是指隱藏對象的屬性和一些實現細節,僅對外提供必須的訪問方式。
Ø 怎麼封裝
將所有屬性隱藏,提供公有方法對其訪問。
將不需要對外提供的方法隱藏。
Ø 封裝的優點
提高安全性:在訪問對象的屬性時候通過方法實現,在方法中可以進行校驗。隱藏不必要提供的方法避免錯誤的調用。
簡化編程:使用者無需關心對象內部具體實現細節,只要根據對象功能調用指定方法。
3.4. 構造函數(Constructor)
Ø 什麼是構造函數
構造函數(Constructor)是一個特殊的函數。
函數名和類名相同。
沒有返回值類型。注意:沒有返回值類型不等同於void,void也是一種返回值類型。不能使用return關鍵字返回任何值。
在使用new關鍵字創建對象之後自動調用。
Ø 構造函數的重載
構造函數的重載和普通函數相同,函數名相同,參數列表不同即可。
Ø 構造函數的調用
構造函數在new關鍵字創建對象時調用。
構造函數可以在該類其他構造函數的第一個語句使用this關鍵字調用。
Ø 所有類都有構造函數
每一個類都有構造函數,即使我們沒有顯式定義構造函數,也會生成一個默認無參的構造函數,其中沒有任何內容。
注意:這個自動生成的構造函數只在未定義任何構造函數時生成,如果我們定義了一個有參的構造函數,那麼就不會生成無參的了。
Ø 構造函數的訪問權限
在定義構造函數時,如無特殊需要,應使用public關鍵字修飾構造函數。
在一些特定情況下,我們不想讓別人創建該類對象,那麼可以使用private修飾構造函數,例如單態設計模式。
3.5. this關鍵字
this關鍵字除了在構造函數中調用其他構造函數以外,還可以當做一個引用使用。其用於方法中,哪個對象調用該方法,this就引用哪個對象。例如:
方法中局部變量和成員變量重名,我們想調用成員變量時就可以使用this.變量名形式訪問成員變量。
在方法中要將調用該方法的對象作爲參數傳遞給另一個方法時,可以將this作爲實參傳給該方法。
在內部類中訪問外部類的成員時,需要使用外部類名.this.成員名形式訪問。
3.6. 函數的參數傳遞
基本數據類型的變量作爲實參傳入函數之後,在函數中將形參改變,調用處的實參不變。
因爲基本數據類型的值是直接存在變量中,傳入函數之後函數中的形參也同樣存了一個值,這兩個值是沒有聯繫的,所以函數中將形參改變時修改的只是函數中的變量的值,和調用處的實參無關。
引用數據類型的變量作爲實參傳入函數之後,在函數中將形參改變,調用處的實參改變。
因爲引用數據類型變量中存儲的是地址,傳入函數之後函數中的形參存儲的也是同樣一個地址,函數中將這個形參改變時改變的都是同一個地址上的對象,所以一邊改變兩邊都變。
3.7. static關鍵字
static關鍵字用來修飾類的成員,被這個關鍵字修飾的成員都和類加載有關。
JVM運行時不會將所有類加載到內存,因爲無法確定程序中要使用哪些。類在第一次使用時加載,只加載一次。
Ø 靜態變量
用static修飾的變量就是靜態變量。
靜態變量在類加載後就初始化。
靜態變量被類的所有實例所共享。
靜態變量可以使用類名.變量名形式訪問。
如果在定義一個類的時候,發現一個成員變量需要被所有實例所共享,那麼這個成員變量就需要定義爲static的。
Ø 靜態方法
用static修飾的方法就是靜態方法。
靜態方法在類加載後就可以使用。
靜態方法可以使用類名.方法名形式訪問。
靜態方法不能直接訪問外部非靜態成員。
因爲外部非靜態成員必須在類創建對象之後才能使用,而靜態方法可以在沒創建對象時就使用。
如果要在靜態方法內部訪問外部非靜態成員,需要先創建該類對象,通過對象訪問。
靜態方法中不能使用this關鍵字。
因爲this是個引用,哪個對象調用方法就引用哪個對象。而靜態方法有可能不是被對象調用的,this無從引用。
如果一個方法不用訪問對象的非靜態成員,那麼就可以定義爲靜態的,這樣使用者就不需要創建對象,直接用類名調用。
靜態方法通常是作爲工具方法或者一個可以產生對象的方法被聲明,目的是爲了讓調用者更方便的使用,不必創建對象。
Ø 靜態代碼塊
用static修飾的代碼塊就是靜態代碼塊。
靜態代碼塊在類加載後執行。
靜態代碼塊和靜態方法相同,不能使用外部非靜態成員。
靜態代碼塊執行和靜態變量的初始化順序由代碼從上到下順序決定。
Ø 靜態內部類
用static修飾的內部類就是靜態內部類。
靜態內部類在類加載後就可以創建對象,無需創建外部類對象。
具體內容詳見3.18內部類
3.8. 垃圾回收
對象在沒有任何引用可以到達時,生命週期結束,成爲垃圾。
所有對象在被回收之前都會自動調用finalize()方法。
一個對象在成爲垃圾之後不會被馬上回收,JVM會檢測內存中的垃圾堆積到一定程度時纔會回收,如果我們不想等到這個時候纔回收,可以使用System.gc()方法來通知虛擬機回收垃圾。調用該方法之後JVM會開啓新線程做處理垃圾的工作,這需要一定時間。
3.9. 單態設計模式(SingletonPattern)
Ø 什麼是設計模式
在編程過程中我們經常會遇到一些典型的問題或需要完成某種特定需求,而這些問題和需求前人也曾經遇到過,他們經過大量理論總結和實踐驗證之後優選出的代碼結構、編程風格、以及解決問題的思考方式,這就是設計模式(Design pattern)。設計模式就像是經典的棋譜,不同的棋局,我們用不同的棋譜,免得我們自己再去思考和摸索。
Ø 單態(單例)設計模式
單態設計模式(Singleton pattern)就是要保證在整個程序中某個類只能存在一個對象,這個類不能再創建第二個對象。
Ø 單態設計模式的寫法
私有化構造函數,阻止創建新對象。
由於需要返回一個對象,那麼我們就需要在類內部自己創建一個對象,並使用成員變量記住它。
由於該類不能創建對象,所以這個成員變量不能是普通的成員變量,需要靜態,這樣在類加載之後就可以創建一個唯一的對象了。
我們不希望其他類修改這個成員變量,所以將其私有。
提供一個公有的方法用來獲取唯一的一個對象。
這個方法由於需要在不創建對象的情況下使用,所以需要靜態。
3.10. 繼承(Inherit)
Ø 什麼是繼承
在程序中,可以使用extends關鍵字可以讓一個類繼承另外一個類。
繼承的類爲子類(派生類),被繼承的類爲父類(超類, 基類)。
子類會自動繼承父類所有的方法和屬性。
Ø 爲什麼要使用繼承
當我們發現一個類的功能不行,方法不夠用時,就可以派生子類,增加方法。
當我們需要定義一個能實現某項特殊功能的類時,就可以使用繼承。
最終還是爲了一個目的,實現代碼的複用性。
當我們定義一個類時,發現另一個類的功能這個類都需要,而這個類又要增加一些新功能時,就可以使用extends關鍵字繼承那個類,這樣那個被繼承類的功能就都有了,不必重寫編寫代碼。這時只要在新的類中編寫新的功能即可,原有代碼就被複用了。
Ø 繼承的特點
Java只支持單繼承,不支持多繼承,但是可以多重繼承
因爲如果一個類繼承多個類,多個類中有相同的方法,子類調用該方法時就不知道該調用哪一個類中的方法了。
子類中可以使用super調用父類成員
super用法和this類似,this是誰調用該方法就引用誰,super是調用該方法的對象的父類對象。
Ø 向上轉型
把一個子類當做父類來用是可以的,因爲父類有的子類都有
把一個父類當做子類來用就不可以了,因爲子類有的父類不一定有
可以定義一個父類類型的變量來記住子類對象,這在程序中稱之爲向上轉型
Ø 強制類型轉換
把一個子類當做父類來用的時候,不能調用子類特有方法。
因爲編譯時編譯器會做語法檢查,看到變量是父類類型那麼就會到父類中查找是否有該方法,沒有則報錯。
這種情況下,就需要強制類型轉換,將父類類型強轉成子類類型。
以(子類名)變量名形式進行強制類型轉換
強制類型轉換時,無論類型是否匹配編譯都不會報錯,但如果類型不匹配運行會報錯,我們可以使用instanceof進行判斷,編譯時預知錯誤。
在子類當做父類來用時,不能調用特有方法,如果一定要調用,就需要強制類型轉換回子類。在做轉換時最好instanceof判斷一下類型是否匹配。
Ø 子類覆蓋(Override)父類方法
覆蓋方法必須和被覆蓋方法具有相同的方法名稱、參數列表和返回值類型。
子類的方法返回值類型可以是父類方法返回值類型的子類。
如果在子類中想調用父類中的那個被覆蓋的方法,我們可以用super.方法的格式。
如果直接調用方法,是在當前子類中先查找,如果子類有會調用子類的。使用super形式只在父類中查找,子類有沒有都不調用。
覆蓋方法時,不能使用比父類中被覆蓋的方法更嚴格的訪問權限。
因爲有可能將子類對象當做父類對象來使用,那麼能獲取到的父類對象中的方法在子類中必須都能獲取到。
覆蓋方法時,不能比父類拋出更多的異常。
子類只能比父類強,不能比父類弱。
重載(Overload)和重寫(Override)的區別:
重載是方法名相同,參數列表不同,和返回值類型無關。
重寫是方法名、參數列表、返回值類型全相同。
@Override 註解,可以檢查覆蓋是否成功
Ø 子類當做父類使用時需要注意
當我們在調用某個類的一個方法時,此方法聲明需要一個父類對象,這時我們可以將一個子類對象作爲實參傳遞過去,注意此時方法定義的形參爲父類,在方法中使用父類變量調用方法時,其實是調用子類的方法。
思考:上述情形下,在方法中用父類變量訪問屬性,訪問的是子類還是父類的屬性 ?
在把子類當做父類來用時,使用父類變量訪問方法,訪問的是子類的方法,因爲虛擬機會找到變量引用的地址,根據這個地址來訪問方法,這叫動態分配。
這種機制沒有被使用到類的成員變量上,如果用父類變量訪問屬性,那麼會直接找到父類的屬性,不會看地址是哪個對象。
Ø 繼承的應用細節
子類不繼承父類私有成員
父類中私有成員對外不可見,子類對象中無法訪問這些成員。
構造函數不被繼承
構造函數通常用來初始化類的成員變量,父類和子類的成員變量不同,初始化方式也不同,構造函數的名字也不同。
爲什麼只支持單繼承
如果一個類繼承多個類,那麼多個類中有相同的方法,調用時會引起歧義。
Ø 子類對象實例化過程
子類構造函數中可以使用super關鍵字調用父類構造函數。
在子類創建對象時一定會調用父類構造函數。即使沒有顯式調用,也會默認調用父類無參構造函數。
在子類中第一行用this關鍵字去調其他的構造方法,這時系統將不再自動調父類的。但其他構造函數中會調用父類構造函數。
在構造方法中this和super關鍵字只能出現一次,而且必須是第一個語句。
以後在設計類的時候,最好定義一個無參的構造方法,不然子類實例化的時候就容易出錯。
3.11. 對象的比較
在我們使用運算符“==”來比較兩個對象時,其實比較的是兩個對象的地址。如果運算符兩邊是同一個對象,地址相同則會等到true,只要是不同對象地址就會不同,返回false。
我們在編程過程中經常會比較兩個對象的屬性,這時我們就無法用“==”來比較了,因爲即使兩個對象所有屬性都相同但不是同一個對象“==”號比較後也會得到false。這種情況下我們一般會定義一個equals()方法來進行比較。
3.12. 文檔註釋
文檔註釋以“/**”開始,以“*/”標誌結束,相應的信息和批註所對應的位置很重要!類的說明應在類定義之前,方法的說明應在方法的定義之前。
使用文檔註釋修飾一個類的源代碼之後可以通過javadoc.exe來生成幫助文檔。
生成文檔的命令:
javadoc -d (目錄) -version –author (源文件)
批註參數來標記一些特殊的屬性及其相應的說明。
@author<作者姓名>
@version<版本信息>
@param<參數名稱><參數說明>
@return<返回值說明>
3.13. 組合設計模式(CompositePattern)
Ø 什麼時候用組合
組合是一種實現代碼複用的方式,當我們在定義一個類的時候需要用到另外一個類的方法時,就可以用組合。
Ø 怎麼用組合
定義一個所需要的類類型的成員變量
通過構造函數進行裝配,接收一個該類類型的對象,用成員變量引用
在需要使用另一個類的方法時通過成員變量訪問
Ø 組合的優點
如果兩個類沒有父子關係,不合適用繼承。
Java只支持單繼承,組合不佔用繼承位置。
3.14. 多態(Polymorphism)
Ø 什麼是多態
多態字面上的意思就是多種形態。在面嚮對象語言中,我們可以將函數的形參定義爲一個父類類型,而在真正調用該函數時這個父類類型的所有子類對象都可以傳入,根據傳入的子類對象不同函數可以運行處多種形態。
Ø 多態的特點
應用程序不必爲每一個派生類(子類)編寫功能調用,只需要對抽象基類進行處理即可。這一招叫“以不變應萬變”,可以大大提高程序的可複用性。
派生類的功能可以被基類的引用變量引用,這叫向後兼容,可以提高程序的可擴充性和可維護性。現在寫的程序可以調用將來寫的程序不足爲奇。
3.15. 抽象類
Ø 什麼是抽象類
使用abstract關鍵字修飾的類就是抽象類,抽象類不能new對象,原因在於抽象類含有抽象方法,不能被調用。
沒有方法體的方法爲抽象方法,使用abstract關鍵字修飾。
有抽象方法的類必須聲明爲抽象類,抽象類不一定含有抽象方法。
Ø 爲什麼要定義抽象類
如果有多個類具有相同的方法聲明,而方法的實現不一樣,這時就可以抽象出父類,將方法在父類中聲明
別人在學習我們的軟件時,只需要學習父類就知道子類有什麼方法
在設計軟件時,要盡力抽象父類,繼承關係以3~4層爲宜
3.16. final關鍵字
final標記的類不能被繼承。
final標記的方法不能被子類重寫。
final標記的變量即爲常量,只能賦值一次。注意引用數據類型和基本數據類型的區別。
使用public static final共同修飾的常量就是全局常量。通常全部字母大寫。
3.17. 模板設計模式(TemplatePattern)
Ø 爲什麼要使用模板方法設計模式
在解決一些問題或者設計一個軟件的時候,需要先定義一個模板,就相當於一種事先定義好的協議。
以後要做這系列的事情都按照這個模板來做。這樣就實現統一化管理。
Ø 如何實現模板方法設計模式
定義一個抽象的父類做爲模板,定義所有需要的方法
在父類中實現供外界調用的主方法,將方法聲明爲final
根據不同業務需求定義子類實現父類的抽象方法
3.18. 內部類(InnerClass)
Ø 類中的內部類
在類裏面定義的類稱之爲內部類(Inner Class),內部類是外部類的一個成員。
內部類必須創建外部類對象才能使用。
創建內部類對象時必須先創建一個外部類對象,通過一個外部類對象才能創建內部類對象。
外部類名.內部類名變量名 = new 外部類名().new 內部類名();
內部類可以直接訪問外部類的成員,而外部類不能直接訪問內部類的成員。訪問方式:外部類名.this.成員名
內部類可以訪問外部類成員,因爲在使用內部類時一定會有外部類對象,且只對應一個。
外部類不能訪問內部類成員,因爲在使用外部類時有可能還沒有創建內部類對象。
如果一定要在外部類中使用內部類成員,那麼需要創建內部類對象,通過對象來訪問。
內部類中不能定義靜態成員。
因爲內部類需要創建外部類對象才能使用,static的本意是不創建對象就能使用,這是矛盾的。
內部類的class文件名爲:外部類名.內部類名.class
Ø 方法中的內部類
一個類如果只在某個方法中使用,那麼可以在方法中定義。
定義在方法中的類只能在方法中使用,而且使用的代碼只能在聲明的代碼下面。
方法中的內部類只有在運行到類定義之後才能使用。
方法中定義的內部類不能訪問方法中定義的局部變量,除非這個局部變量被聲明爲final的。
在方法中定義的局部變量在方法運行結束之後生命週期結束,不能再被訪問。
方法中的內部類創建的對象有可能生命週期比這個局部變量長,例如這個對象被作爲返回值返回,那麼方法運行結束之後還可以訪問這個對象。
這時變量被銷燬了,對象還在,如果在對象的某個方法內訪問這個變量就訪問不到了。
我們需要使用final修飾這個變量,被final修飾的變量會一直存儲在內存中,方法運行結束之後不被銷燬。
方法中的內部類class文件名爲:外部類名$.編號內部類名.class
Ø 匿名內部類
如果一個類只使用一次,那麼可以定義爲匿名內部類。
使用 new 父類名(){類定義} 形式聲明,先創建一個指定類的子類,然後根據這個類創建一個對象。
匿名內部類的class文件名爲:外部類名$編號.class
Ø 靜態內部類
可以使用static修飾一個類中的內部類。
靜態內部類不用創建外部類對象就可以直接創建對象。
外部類名.內部類名變量名 = new 外部類名.內部類名();
靜態內部類可以定義靜態成員。
因爲靜態內部類可以直接使用,無需創建外部類對象。
靜態內部類中不能訪問外部非靜態成員。
因爲創建靜態內部類不需要外部類對象,也就是有可能沒有創建外部類對象,使用外部類成員必須有外部類對象。
3.19. 接口
Ø 什麼是接口
接口是一種特殊的抽象類,接口中聲明的所有方法都是抽象的
使用interface關鍵字修飾一個接口
Ø 接口的用法
我們可以定義一個類來實現接口,使用implements關鍵字
實現一個接口需要實現接口中所有的方法,抽象類除外
通常使用匿名內部類來實現一個接口
接口可以繼承接口,使用extends關鍵字。接口不能繼承抽象類,因爲抽象類中可能有不抽象的方法。
一個類可以實現多個接口,爲了實現多態
Ø 接口中的方法和變量
接口中定義的方法默認是公有的抽象的,被public abstract修飾
接口中定義的變量默認爲全局常量,使用public static final修飾
Ø abstract class和interface的區別
抽象類中可以有不抽象的方法,接口中全是抽象的方法
抽象類用extends繼承,接口用implements實現
抽象類中的變量和方法沒有默認修飾符,接口中的變量默認爲public static final的,接口中的方法默認爲public abstract
一個類只能繼承一個抽象類,一個類可以實現多個接口
Ø 什麼時候用抽象類,什麼時候用接口
如果能用接口,就不用抽象類,因爲別人實現接口可以不佔用繼承的位置。
如果定義一個抽象的父類,其中所有方法都是抽象的,那麼就定義爲接口。
如果定義一個抽象的父類的時候,需要有不抽象的方法,那麼只能定義爲抽象類。
3.20. 異常
Ø 什麼是異常
異常就是Java程序在運行過程中出現的錯誤。如程序要打開一個不存的文件、網絡連接中斷、操作數組越界、裝載一個不存在的類等。
Ø Throwable
Throwable表示Java中可被拋出的對象,它是所有錯誤和異常的父類
Throwable有兩個子類:Error、Exception
Error表示錯誤
Exception表示異常
RuntimeException表示運行時異常,是Exception的子類
Throwable |
Error
|
Exception |
|
|
子類 |
|
|
子類 |
RuntimeException |
|
|
子類 |
Ø 異常的分類
Error(錯誤)
由Java虛擬機生成並拋出,包括動態鏈接失敗、虛擬機錯誤等,程序對其不進行處理
Exception(異常)
所有異常類的父類,子類定義了各種各樣可能出現的異常事件,一般需要用戶顯式地聲明向外拋出或捕獲。
Runtime Exception(運行時異常)
一類特殊的異常,如被0除、數組角標越界等。產生比較頻繁,處理麻煩,如果每次都處理,會對程序可讀性和運行效率影響比較大,因此由系統檢測並將它們交給缺省的異常處理程序,用戶不必對其進行處理。這類異常不處理,編譯時不會報錯,只是在運行時出現錯誤時才報告異常,所以我們稱之爲運行時異常,所有RuntimeException的子類都是運行時異常。我們也可以對運行時異常進行處理。
編譯時異常
Exception中除了RuntimeException的子類,其他異常都是必須要處理的,如果不處理,編譯時會報錯,這些異常我們稱之爲編譯時異常。
Ø 異常的用法
處理異常
在程序中可以在方法後面使用throws關鍵字聲明向外拋出異常
對於編譯時異常,通常我們需要使用try……catch語句進行捕獲
finally可以結合try……catch使用,出現異常,finally裏面的代碼也會執行
異常的一些細節
如果父類方法中聲明拋出多個異常,那麼重寫(覆蓋)該方法只能拋出那些異常的一個子集,也就是說子類不能比父類拋出更多的異常。
如何處理多個異常
try語句與finally的嵌套使用
自定義異常
可以通過繼承Exception類來自定義一個異常
如果要定義一個運行時異常則需要繼承RuntimeException類
3.21. 包
Ø Java中常用的包
java.lang
包含一些Java語言的核心類,如String、Math、Integer、System和Thread,提供常用功能。
java.awt
包含了構成抽象窗口工具集(abstract window toolkits)的多個類,這些類被用來構建和管理應用程序的圖形用戶界面(GUI)。
java.net
包含執行與網絡相關的操作的類。
java.io
包含能提供多種輸入/輸出功能的類。
java.util
包含一些實用工具類,如定義系統特性、使用與日期日曆相關的函數。
Ø 定義帶包類
使用package語句加上包名來定義類所屬於的包,包名全部小寫
package語句爲Java源文件的第一條語句
如果一個類中沒有使用package語句,這個類爲缺省無包名
一個類如果想被其他包中的類引用,必須使用public關鍵字修飾。構造函數也需要public
如果一個類被聲明爲public,那麼必須和文件名同名
Ø 使用帶包的類
在使用帶包的類時需要使用全限定名(包名.類名)
在每次寫類名時都使用全限定名很麻煩,我們可以使用import導入包,之後再使用就無需寫包名了
星號*:導入一個包中所有類。優先匹配當前包中的類,如果當前包沒有再匹配導入包中的類。
具體類名:導入指定一個類。無論當前包中是否有同名類,都直接匹配導入的類。
無包的類可以使用有包的類,有包的類不能使用無包的類。
Ø 編譯運行帶包的類
編譯一個帶包的源文件,在生成class文件的同時需要生成包文件
編譯命令:javac –d <目錄> 源文件名.java
運行有包的類時需要加上包名
運行命令:java 包名.類名
3.22. jar文件
Ø 什麼是jar文件
jar文件是Java文件的一種壓縮格式
一般來講,我們會將一個軟件系統的所有class文件打成一個jar文件以供別人使用
當我們用到jar包中的類時,需要將jar文件的絕對路徑加到classpath當中
Ø 如何壓縮jar文件
將編譯好的帶包的class文件壓縮成一個jar文件稱爲打jar
打jar命令:jar cvf jar包名.jar 要打包的文件/文件夾
運行jar文件命令: java -jar jar文件名.jar
3.23. 訪問控制符
類的訪問控制符有兩種:
public關鍵字修飾:可以被所有的類訪問
缺省爲default:只能被同一包中的類訪問
3.24. 代碼編寫規範
標識符命名規則(駝峯式)
類名首字母大寫:XxxYyyZzz
變量名、方法名首字母小寫:xxxYyyZzz
包名全小寫:xxx.yyy.zzz
常量名全大寫:XXX_YYY_ZZZ
大括號的位置
大括號應成對出現,第一個大括號應在第一行語句後面
方法後面緊跟大括號,沒有空格
關鍵字(while、for、if)後面應留一個空格
賦值語句之間用分號分隔,分號後面應空一格
代碼折行應按照代碼等級對齊,運算符寫在下一行