CGBTN2109匯總復習
復習思路
先抓知識結構主干,再去慢慢補充細節拓展
遇到會的,快速回顧
遇到忘記或者是不會的,先記錄,后面自己復習的時候著重回顧
一階段學習路徑

1 基礎語法Basic
1.基礎環境配置
1. 安裝JDK
JDK:Java開發工具包,我們使用的版本是1.8
注意:一臺PC上可以安裝多個JDK,具體環境變數配置哪個JDK,哪個就生效
2. 環境變數的配置
JAVA_HOME : 配置的是JDK安裝的目錄
Path : 配置的是JDK的bin目錄,不新建的
CLASS_PATH:配置的是JDK的lib目錄
win+R鍵,在運行視窗輸入cmd
驗證命令為 : java -version 出現JDK版本號即為成功
3. 開發工具
eclipse IDEA
注意1:開發工具無需糾結,重要的是編程的思路,對于工具而言,選一個自己喜歡的就好,重要的是提高這個自己常用軟體的熟練度(快捷鍵 字體設定 配置JDK…面向百度進行開發)
注意2:大家在安裝的時候,不要選擇C盤系統盤,而且路徑中不要出現中文或者空格等等其他特殊符號,因為會出現一些未知的問題
2.JDK JRE JVM
JDK:Java開發工具包(Java Development Kit)–開發的最小單位
JRE:Java運行時環境(Java Runtime Environment)–運行的最小單位
JVM:Java虛擬機(Java Virtual Machine)–負責加載并運行.class位元組碼檔案
- 運行程序: 我們撰寫的原始碼是.java為后綴的,通過編譯生成的是.class位元組碼檔案,交給JVM來執行
- 跨平臺: 只要在不同的作業系統上安裝對應的JVM,就可以實作跨平臺:一份代碼 處處運行

2.語法基礎
1.關鍵字
50個全小寫的單詞,在Java有特殊的意義,還包含2個保留字const goto

2.識別符號
字母 數字 下劃線 美元符號組成,不能以數字開頭,區分大小寫,關鍵字+(true false null)也不可以用作識別符號,見名知意
UpperCamelCase大駝峰命名:
每個單詞的首字母都要大寫,比如類名:HelloWorld類名: Upper駝峰命名:每一個單詞的首字母都要大寫
LowerCamelCase小駝峰命名:
從第二個單詞的首字母才開始大寫,比如:方法名:nextLine() 變數名:deptName
3.注釋
單行注釋 //
多行注釋 / * * /
檔案注釋: /** */
還可以添加一些額外的資訊:作者 時間 版本 引數 回傳值型別
注釋可以注釋內容,被注釋的內容不執行,所以我們可以利用注釋手段對進行分段代碼測驗
4.變數
- 成員變數:類里方法外,類消失,成員變數才會消失
成員有自己的默認值,可以不手動賦值 - 區域變數:方法里/代碼塊里,當區域代碼結束,區域變數也隨之釋放
- 區域變數使用的時候,必須賦值,可以:
宣告的時候并且賦值 Cat cat = new Cat();
先宣告再賦值 Cat cat; cat = new Cat();
注意:基本型別保存的是值,參考型別保存的是地址值
- 變數的就近原則:離誰近 就使用誰
- 如果想指定本類的成員變數,使用this.變數名來指定
- 如果想指定父類的成員變數,使用super.變數名來指定
5.八大基本型別
Java的資料型別分為兩類:基本型別 + 參考型別

6.字面值規則:
- 整數型別的字面值型別是int
- 浮點型別的字面值型別是double
- byte short char 三種比int小的型別,可以在范圍內直接賦值
- 三種字面值后綴 : L D F
- 三種字面值前綴: 0b-二進制 0-八進制 0x-十六進制
- 練習題:進制的前綴
package cn.tedu.basic;
/*本類用于測驗型別的前綴*/
public class TestTypePre {
public static void main(String[] args) {
//10-2 10-1 10-0
System.out.println(100);//100-10的平方
//操作二進制-0b
//2-2 2-1 2-0
System.out.println(0b100);//4-2的平方
//操作八進制
//8-2 8-1 8-0
System.out.println(0100);//64-8的平方
//操作十六進制
//16-2 16-1 16-0
System.out.println(0x100);//256-16的平方
}
}
7.運算規則:
- 運算結果的資料型別與參與運算的最大型別保持一致 int+int->int double/int->double
- 整數運算會出現溢位的現象,一旦溢位,資料就不正確了(光年案例)
- byte short char三種比int小的型別,運算的時候需要先自動提升int型別,再參與運算
- 浮點數的特殊值:Infinity NaN
- 浮點數運算不精確的問題:解決方案:BigDecimal
注意1:不能使用double型別引數的構造方法,需要使用String型別的建構式,否則還會出現不精確的問題
注意2:除法運算時,如果出現除不盡的現象還會報錯,所以需要指定保留位數與舍入方式(四舍五入)
8.型別轉換
口訣:小轉大,直接轉 大轉小,強制轉 浮變整,小數沒
- 注意:布爾型別不參與型別轉換
- 注意:基本型別之間能否轉換,不取決于位元組數,位元組數只能做參考,取決于型別的取值范圍
- 注意:我們這里所說的是基本型別之間的轉換,參考型別之間的轉換取決于是否有繼承關系
比如:你可以說小貓是小動物,但是不能說小貓是小汽車,不然后面的這種錯誤的情況會報:型別轉換例外

- 練習題:型別之間的轉換與字面值規則
package cn.tedu.basic;
/*本類用來測驗型別轉換
* 1.byte1--short char2--int4--long8--float4--double8
* 2.小到大 直接轉:隱式轉換,可以直接轉換
* 大到小 強制轉:顯式轉換,需要強轉,注意發生資料溢位的問題
* 浮變整 小數沒:小數部分直接被舍棄
* 3.強制型別轉換的格式:目標型別 變數名 = (目標型別)要轉換型別的資料;
* */
public class TestTypeChage {
public static void main(String[] args) {
byte a = 10;
short b = a;//不會報錯,小轉大
int c = 1;
long d = c;//不會報錯,小轉大
float f = 3.1415f;
double e = f;//不會報錯,小轉大
long g = 8274624867L;
float h = g;//不會報錯,小轉大
System.out.println(h);
char i = 'a';
int j = i;//不會報錯,小轉大
System.out.println(j);//97
int a1 = 1;
byte b1 = 2;
//byte c1 = a1+b1;會報錯,大轉小
byte c1 = (byte) (a1+b1);//需要強制型別轉換
byte d1 = (byte) 128;
System.out.println(d1);//-128,需要強轉,注意發生資料溢位的問題
short e1 = 'a';
char f1 = 120;
System.out.println(e1);//97,列印的是編碼值
System.out.println(f1);//'x',列印是根據編碼找到的字符
float h1 = 32874.435F;
int i1 = (int) h1;//大轉小 強制轉
System.out.println(i1);//32874 浮變整 小數沒
}
}
9.運算子
- 普通的四則運算子 + - * / ,普通的四則運算,并不能直接改變變數本身的值,除非 i = i*10+8
- 取余 % 6%4=2 6%3=0(余數為0表示整除)
- 自增自減運算子
1)可以改變變數本身的值
2)前綴式: 符號在前,先改變變數本身的值(+1/-1),再使用(列印/參與運算…)
3)后綴式: 符號在后,先使用(列印/參與運算…),再改變變數本身的值(+1/-1)
4)注意:不管是前綴式還是后綴式,一定是會改變變數本身的值,區別在于執行的時機不同 - 比較運算子
- 比較運算子最終的結果是布爾型別的
- == 比較的是左右兩邊的值是否相等 !=比較的是左右兩邊的值是否不相等
- 練習題:==比較的練習
package cn.tedu.basic;
public class TestOperator {
public static void main(String[] args) {
//參考型別Cat型別的變數c1 c2 保存的是物件的地址值
Cat c1 = new Cat();
Cat c2 = new Cat();
int[] a1 = {1, 2, 3};
int[] a2 = {1, 2, 3};
int b1 = 4;
int b2 = 4;
boolean f1 = true;
boolean f2 = true;
/* == 如果比較的是基本型別,比較的是值,字面值,具體存的那個數*/
System.out.println(b1 == b2);//true
System.out.println(f1 == f2);//true
/* == 如果比較的是參考型別,比較的也是值,地址值*/
System.out.println(c1 == c2);//false
System.out.println(a1 == a2);//false
}
}
class Cat {
String name;
int age;
public void bark() {
System.out.println("小貓喜歡喵喵叫");
}
}
- 邏輯運算子
雙與/短路與/&& :
判斷邏輯與&一致,增加了短路的功能全真才真,有假則假
雙或/短路或/|| :
判斷邏輯與|一致,增加了短路的功能全假才假,有真則真
注意:我們這里所說的短路,是指在某些情況下,運算式后半部分就不用計算了,因為我們已經知道了結果,也就是被短路了,短路可以提高程式的性能,但是短路不一定會用到 - 三目運算子: 1 ? 2 : 3; 1是運算式,1真取2,1假取3
- 復合賦值運算子:+= -= *= /=是一種簡寫的形式,比較方便,運算時會自動進行型別轉換
- 賦值運算子: = ,右邊給左邊
- 拼接功能:+
- 位運算子 : 主要參與的是二進制的運算
&與:全真才真
| 或:全假才假
^異或:相同為0 不同為1
~ 非: 非0為1,非1為0 - 優先級控制:如果運算式的運算比較復雜,需要控制優先級,可以使用小括號
- 拓展:instanceof
10.流程控制
1.順序結構
順序結構中的代碼會按照順序一行一行向下執行所有的代碼陳述句,可以用來進行輸入 輸出 計算等的操作
但順序結構不可以完成先做判斷,再做選擇的流程
2.分支結構
- 單分支結構
if(判斷條件){
如果判斷條件的結果為true,就執行此處代碼,不符合條件,此處跳過
}
- 多分支結構
if(判斷條件){
如果判斷條件的結果為true,就執行此處的代碼
}else{
如果不符合條件,執行else處的代碼
}
- 嵌套分支結構
if(判斷條件1){
符合判斷條件1,執行此處代碼,不符合,繼續向下判斷
}else if(判斷條件2){
符合判斷條件2,執行此處代碼,不符合,繼續向下判斷
}else if(判斷條件3){
符合判斷條件3,執行此處代碼,不符合,繼續向下判斷
}else{
保底選項,以上條件均不符合的情況下,執行此處代碼
}
- 練習題:嵌套分支的練習
需求:提示并接收用戶輸入的月份,判斷并輸出屬于哪個季節
1-12月是合法資料 3~5春天 6~8夏天 9~11秋天 其他情況冬天
package cn.tedu.basic;
import java.util.Scanner;
/*本類用于復習分支結構*/
public class TestIf {
public static void main(String[] args) {
//1.提示并接收用戶輸入的月份
System.out.println("請輸入您要查看的月份:");
int month = new Scanner(System.in).nextInt();
//2.對用戶輸入的資料進行合法性檢測
/*如果if后的陳述句只有一句,大括號可以省略不寫*/
/*return關鍵字除了可以幫我們回傳方法的回傳值以外
* 還可以直接結束當前的方法,如果遇到了return,本方法會直接結束*/
// if(month <1 || month >12) return;
// System.out.println("今天天氣真不錯");//這句話是用來檢測main()有沒有結束
if(month <= 0 || month >12){
System.out.println("您輸入的月份不正確!應該是1-12月以內");
}else{
//3.判斷接收到的合法資料屬于哪個季節,并將結果輸出
if(month >=3 && month <=5){
System.out.println(month+"月是春天");
}else if(month >=6 && month <=8){
System.out.println(month+"月是夏天~");
}else if(month >=9 && month <=11){
System.out.println(month+"月是秋天");
}else{
System.out.println("冬天就要來啦,春天還會遠嗎?");
}
}
}
}
3.選擇結構
- 小括號中變數支持的型別:
byte short char int String enum 與4個基本型別對應的包裝類 - 注意: 如果配置了default默認選項,而且沒有任何的case被匹配到,就會執行default這個“保底選項”
- case的個數 是否加break 是否加default全部都是可選的,根據自己的具體業務做決定
- 小括號中變數的型別必須與case后value的型別一致
- 執行順序:先拿著變數的值,依次與每個case后的值做比較,如果相等,就執行case后的陳述句
若這個case后沒有break,就會繼續向下執行下一個case,如果一直沒有遇到break,就會發生穿透現象,包括default
switch (變數名){
case value1 : 操作1;break;//可選
case value2 : 操作2;break;//可選
case value3 : 操作3;break;//可選
case value4 : 操作4;break;//可選
default:保底選項;//可選
}
- 練習題:根據顏色推薦菜品switch
package cn.tedu.basic;
import java.util.Scanner;
/*本類用于復習switch*/
public class TestSwitch {
public static void main(String[] args) {
//需求:接收用戶今天輸入的顏色,推薦菜品
//1.提示并接收用戶輸入的顏色
System.out.println("請輸入您今天心情的顏色:");
String color = new Scanner(System.in).nextLine();
//2.完成選擇結構
switch (color) {
case "紅":
System.out.println("紅燒魚");
break;//為了避免穿透
case "黃":
System.out.println("菠蘿炒飯");
break;
case "橙":
System.out.println("水煮肉片");
break;
default:
System.out.println("哎呀,沒有識別到這個功能呢,正在開發中...");
}
}
}
4.回圈結構
可以幫我們多次重復的做某一件事
1.for回圈
for(開始條件;回圈條件;更改條件){
如果符合回圈條件,就會執行回圈體里的內容
}
注意1:寫法小竅門:從哪開始 到哪結束 回圈變數如何變化
注意2:for回圈能夠執行多少次,取決于回圈變數可以取到幾個值
2.嵌套for回圈
外層回圈控制的是輪數,內層回圈控制的是每一輪中執行的次數
對于圖形而言,外層回圈控制的是行數,內層回圈控制的是列數
for(開始條件;回圈條件;更改條件){//外層回圈
for(開始條件;回圈條件;更改條件){//內層回圈
回圈體
}
}
注意:外層回圈控制的是行數,內層回圈控制的是列數
注意:外層回圈控制的是輪數,內層回圈控制的是在這一輪中執行的次數
3.高效for回圈
for(遍歷到的元素的型別 遍歷到的元素的名字 :要遍歷的陣列/集合名){
回圈體
}
優點:寫法簡單,效率高
缺點:只能從頭到尾的遍歷資料,不能進行步長的選擇
4.while回圈
while(判斷條件){
如果符合判斷條件,繼續回圈
}
注意:常用來完成死回圈,但死回圈必須設定出口!
練習題:while回圈練習
package cn.tedu.basic;
/*本類用作while復習*/
public class TestWhile {
public static void main(String[] args) {
//需求1:通過while回圈列印10次"小可愛們中午好~"
f1();
//需求2:通過while回圈列印1 2 3 ... 10
f2();
//需求3:通過while回圈列印1 3 5 7 9 ... 99
f3();
//需求4:通過while計算:1+2+3+4+...+10
f4();
//需求5:通過while計算:2+4+6+8...+100
f5();
}
private static void f1() {
//需求1:通過while回圈列印10次"小可愛們中午好~"
//1.定義一個變數用來控制次數
int count = 1;
//2.定義一個回圈,結束條件是count>10
while (count <= 10) {
System.out.println("小可愛們中午好~");
count++;//注意count的值需要自增,不然就是一個死回圈
}
System.out.println(count);//11
}
private static void f2() {
//需求2:通過while回圈列印1 2 3 ... 10
int i = 1;
while (i <= 10) {
System.out.println(i);
i++;
}
}
private static void f3() {
//需求3:通過while回圈列印1 3 5 7 9 ... 99
int num = 1;
while (num < 100) {
System.out.println(num);
//num++;//12345...
//num += 2;
num = num + 2;//13579...
}
}
private static void f4() {
//需求4:通過while計算:1+2+3+4+...+10
int i = 1;//用于控制回圈,相當于回圈變數
int sum = 0;//用來保存求和的結果
while(i <= 10){
sum += i;
//sum = sum + i;
i++;
}
System.out.println("1到10累加的結果為:"+sum);
}
private static void f5() {
//需求5:通過while計算:2+4+6+8...+100
int i = 2;//回圈變數控制回圈
int sum = 0;//用來保存求和的結果
while(i <= 100){
sum += i;//累加
i += 2;//回圈變數遞增
}
System.out.println("2到100累加的結果為:"+sum);
}
}
5.do-while回圈
do-while回圈一定會執行一次,然后再判斷,如果符合條件,再執行后面的回圈
do{
回圈體
}while(判斷條件);
回圈之間的比較
- 如果明確知道回圈的次數/需要設定回圈變數的變化情況時–使用for回圈
- 如果想寫死回圈–while(true){}
- 如果需要先執行一次,再做判斷–do-while回圈
- 回圈之間是可以互相替換的,但是最好使用比較合適的回圈結構
11. 方法
- 格式: 修飾符 回傳值型別 方法名(引數串列){ 方法體 }
- 如何確定我們要呼叫哪個方法呢?方法名+引數串列
- 一個方法被呼叫,就會執行這個方法的功能,執行完畢就回傳到方法被呼叫的位置,在第幾行呼叫,程式就回傳到第幾行繼續向下執行
如果這個方法有回傳值,我們有兩種選擇:- 選擇只執行功能,不接識訓傳值,不再繼續使用這個方法的結果
- 選擇在方法呼叫的位置接收這個方法的回傳值,接收到的回傳值可以在方法外繼續使用

- 方法的多載:
在同一個類中出現多個方法名相同,但引數串列不同的方法
注意:方法是否構成多載,取決于引數串列中引數的個數與引數的型別,與引數的名字無關
多載的意義: 多載不是為了程式員方便,而是為了方便外界呼叫這個名字的方法時,不管傳入什么型別的引數,都可以匹配到對應的方法來執行,程式會更加的靈活 - 方法的傳值 : 如果方法的引數型別是基本型別,傳入的是實際的字面值,如果是參考型別,傳入的是地址值
形參:形式意義上的引數,比如方法引數串列的引數名,光看引數名是無法確定這個變數的值是多少的
實參:實際意義上的引數,比如我們的區域變數,比如成員變量,比如呼叫方法時傳入的數字 - 方法的重寫: 子類繼承了父類以后,想要在不改變父類代碼的情況下,實作功能的修改與拓展
重寫遵循的規則:兩同 兩小 一大
一大: 子類方法的修飾符權限 >= 父類方法的修飾符權限
兩同: 方法名與引數串列與父類方法保持一致
兩小: 子類方法的回傳值型別 <= 父類方法的回傳值型別,注意:這里的<=說的是繼承關系,不是值的大小
子類方法拋出的例外型別 <= 父類方法拋出的例外型別 - 四種權限修飾符

8.練習題:方法呼叫的順序
package cn.tedu.basic;
/*本類用于練習方法的呼叫*/
public class MethodDemo {
//1.創建入口函式
public static void main(String[] args) {
System.out.println("main() is start...");
m1();
System.out.println("main() is stop...");
}
//2.創建m1()
private static void m1() {
System.out.println("m1() is start...");
m2();
System.out.println("m1() is stop...");
}
//3.創建m2()
private static void m2() {
System.out.println("m2() is start...");
}
}
9.拓展 : 方法的遞回
12. 陣列
- 陣列的創建方式:
靜態創建 int[] a = {1,2,3,4,5};
靜態創建 int[] a = new int[]{1,2,3,4,5};
動態創建 int[] a = new int[5]; 后續可以動態的給陣列中的元素賦值
注意:不管是什么樣的創建方式,都需要指定陣列的型別與長度 - 我們可以通過陣列的下標來操作陣列中的元素
陣列下標從0開始,最大下標是陣列的長度-1,如果訪問到了不是這個陣列的下標,會出現陣列下標越界例外
比如:a[5]表示的就是陣列中的第6個元素 - 陣列的長度:陣列一旦創建,長度不可改變,長度指的是陣列中元素的個數a.length,并且陣列的長度允許為0:[ ]
- 陣列的創建程序:
- 根據陣列的型別與長度開辟一塊連續的記憶體空間
- 對陣列中的每個元素進行初始化,比如int陣列的默認值就是0
- 生成陣列唯一的一個地址值,交給應用型別變數a來保存
- 后續可以根據陣列的下標再來操作陣列中的具體元素
注意: 陣列名a這個變數,保存的是陣列的地址,不是陣列中的具體元素

- 陣列的工具類Arrays
- toString(陣列名) : 除了char型別的陣列以外,其他型別的陣列想要查看具體元素,需要使用本方法,否則列印的是地址值
- copyOf(原陣列名,新陣列的長度) : 用來實作陣列的復制 擴容 縮容
如果新陣列的長度 > 原陣列長度,就擴容,反之,則縮容,如果兩者長度一致,就是普通的復制陣列
注意:一定是創建了新陣列,而不是對原陣列的長度做操作
- 陣列的遍歷
一般習慣使用for回圈,回圈變數代表的就是陣列的下標,從0開始,最大值是a.length-1 - 冒泡排序 :
外層回圈控制比較的輪數,所以回圈變數從1到n-1輪,代表的是輪數
內層回圈控制的是這一輪中比較的次數,而且是陣列中兩個相鄰元素的比較,所以回圈變數代表的是陣列的下標 - 練習題: 陣列練習
需求1: 求陣列中所有元素之和
需求2: 求陣列中所有元素的最大值
package cn.tedu.basic;
/*本類用于復習陣列的操作*/
public class TestArray1 {
public static void main(String[] args) {
//需求1:求出陣列中所有的元素之和
f1();
//需求2:求出陣列中所有元素的最大值
f2();
//思考題:將陣列中所有元素逆序輸出
//舉例:原陣列:1 2 3 4 5
//輸出效果:5 4 3 2 1
}
private static void f2() {
//需求2:求出陣列中所有元素的最大值
//1.定義一個陣列
int[] a = {45, 8, 90, 34, 65, -9};
//2.定義一個變數,用來存盤結果,也就是陣列中所有元素的最大值
int max = a[0];//這個位置不能寫0,應該是陣列中第一個元素的值
//3.遍歷陣列,比較出最大值
for (int i = 0; i <= a.length - 1; i++) {
//4.判斷max與a[i]的大小
if(a[i] > max){
max = a[i];//讓max保存的一直都是目前遍歷到的最大值
}
}
//4.回圈結束,輸出陣列中的最大值
System.out.println("最大值為:"+max);
}
private static void f1() {
//需求1:求出陣列中所有的元素之和
//1.定義一個陣列
int[] a = {1, 2, 3, 4, 5};
//2.定義一個變數用來保存最終的結果
int sum = 0;
//3.用陣列的遍歷來進行資料的累加
//i:下標 0 a.length-1 ++
for (int i = 0; i <= a.length - 1; i++) {
sum += a[i];
}
System.out.println("陣列元素累計的和為:" + sum);
}
}
2 面向物件OOP
1.面向物件與面向程序
- 兩者都是一種編程的思想
- 面向物件強調的是事情的結果,我們通過物件完成對應的功能
- 面向程序強調的是事情的程序,我們做任何事情,都要親力親為,經過每一個步驟
- Java是一門面向物件的語言
2.類與物件
- 定義類通過關鍵字class來定義,類是一類事物的抽象,它是抽象的,它是模板
- 創建物件通過new關鍵字觸發建構式生成,物件是根據類創建出來的具體的內容
- 一個類可以創建出多個物件,物件是根據類的設計來創建的,所以物件具有類的所有屬性與功能
- 物件之間是相互獨立的,互不影響,我們把創建物件也稱作“實體化”
3.面向物件的三大特性:封裝
- 前提:為了保證資料的安全,也為了程式的使用者能夠按照我們預先設計好的方式來使用資源
- 封裝屬性:用private修飾我們的屬性
然后為屬性提供對應的getXxx()【獲取屬性值】與setXxx()【設定屬性值】 - 封裝方法:用private修飾方法,被修飾的方法只能在本類中使用,所以我們在本類的公共方法里呼叫這個私有方法
外界如果想要使用這個私有方法的功能,只需要呼叫這個公共方法就可以了
4.面向物件的三大特性:繼承
- 前提 :繼承可以實作程式的復用性,減少代碼的冗余
- 我們通過extends關鍵字建立子類與父類的繼承關系:格式:子類 extends 父類
- 繼承相當于子類把父類的功能復制了一份,包括私有資源
注意:雖然私有資源繼承了,但是私有資源不可用,原因是被private限制了訪問,私有資源只能在本類使用
注意:構造方法不能繼承,原因是:構造方法要求名字是本類的類名,我們不能在子類中出現父類名字的構造方法 - 繼承是可以傳遞的:爺爺的功能會傳給爸爸,爸爸的功能會傳給孫子
注意:爸爸從爺爺那里繼承的功能,也會一起傳給孫子 - Java的類是單繼承的:一個子類只能有一個父類,但是一個父類可以有多個子類
- 子類在繼承了父類以后,如果對父類的功能不滿意
可以在不修改父類功能的【滿足OCP原則】前提下,在子類中,重寫繼承過來的這個方法
重寫需要滿足的規則:兩同 兩小 一大,我們可以在重寫的方法上加@Override注解驗證是否寫對 - 繼承是一種is a的關系,強耦合,關聯性特別強,而且類的繼承機會只有一次,要謹慎使用
- 子類可以直接使用父類的所有非私有資源
5.面向物件的三大特性:多型
- 前提:為了忽略子型別之間的差異,統一看作父型別別,寫出更加通用的代碼
比如:把Cat看作Animal,把Dog看作Animal,把Bird看作Animal,如果方法需要設定傳入的引數,可以buy(Animal a)
比如:把算術例外、輸入不匹配例外都看作是Exception,統一捕獲處理,只寫一個解決方案 - 概念:在同一時刻,同一個物件,代表的型別不同,擁有多種形態
- 多型的要求:繼承 + 重寫
- 多型的口訣1:父類參考指向子類物件:父型別的參考型別變數保存的是子類物件的地址值
- 多型的口訣2:編譯看左邊,運行看右邊:
父類中定義的功能,子類才能使用,否則報錯
多型中,方法的定義看的是父類的,方法的實作看的是子類重寫后的功能

- 多型中資源的使用:
1)成員變數:使用的是父類的
2)成員方法:對于方法的定義看的都是父類的,對于方法實作,重寫后使用的是子類的
3)靜態資源:靜態資源屬于類資源,不存在重寫的概念,在哪個類中定義的,就屬于哪個類 - 向上造型與向下造型
1)這兩種都屬于多型,只不過是多型的兩種不同的表現形式
2)向上造型【最常用】
可以把不同的子型別都看作是父型別,比如Parent p = new Child();
比如:花木蘭替父從軍,被看作是父型別,并且花木蘭在從軍的時候,不能使用自己的特有功能,比如化妝
3)向下造型
前提:必須得先向上造型,才能向下造型
子類的參考指向子類的物件,但是這個子類物件之前被看作是父型別別,所以需要強制型別轉換
Parent p = new Child(); 然后:Child c = (Child) p;
比如:花木蘭已經替她爸打完仗了,想回家織布,那么這個時候,一直被看作是父型別的花木蘭必須經歷“解甲歸田”【強制型別轉換】這個程序,才能重新被看作成子型別別,使用子類的特有功能
為什么有向下造型:之前被看作是父型別別的子類物件,想使用子類的特有功能,那就需要向下造型
6.構造方法
- 格式 :修飾符 類名當做方法名(){ } 注意:與類同名且沒有回傳值型別
- 構造方法作用:用于創建物件,每次new物件時,都會觸發對應的建構式,new幾次,觸發幾次
- 一個類中默認存在無參構造,如果這個構造不被覆寫的話,我們可以不傳引數,直接創建這個類的物件
- 如果這個類中提供了其他的建構式,默認的無參構造會被覆寫,所以記得手動添加無參構
- 構造方法也存在多載的現象:無參構造 含參構造 全參構造【創建物件+給所有的屬性賦值】
7.this與super
- this代表的是本類,super代表的是父類
- 當本類的成員變數與區域變數同名時,我們可以通過this.變數名指定本類的成員變數
當父類的成員變數與子類的變數同名時,我們可以通過super.變數名指定父類的成員變數 - 我們可以在本類建構式的第一行
使用this();呼叫本類的無參構造 / 使用this(引數); 呼叫本類對應引數的構造方法
建構式的呼叫只有這一種方式,或者創建物件時被動觸發,不能在外面自己主動呼叫
建構式直接不能互相呼叫,否則會死回圈 - 我們可以在子類建構式的第一行
使用super();呼叫父類的無參構造 / 使用super(引數); 呼叫父類對應引數的構造方法
注意:子類默認呼叫super();父類的無參構造,如果父類沒有無參構造,需要手動指定呼叫哪個含參構造
8.物件創建的程序
- 前提:物件是根據類的設定來創建的,目前我們可以在類中添加很多的元素:
屬性 方法 靜態方法 構造代碼塊 靜態代碼塊 區域代碼塊 構造方法…所以不限制類里具體寫什么,取決于業務 - 物件創建的程序:Phone p = new Phone();
- 需要在堆記憶體中開辟一塊空間,用來存放物件
- 物件需要完成初始化,比如對應的屬性都有自己的對應型別的默認值
- 物件創建完畢后,會生成一個唯一的地址值用于區分不同的物件
- 將這個地址值交給參考型別變數來保存
- 后續如果想要使用這個類的功能,可以從參考型別變數中保存的地址值找到對應的物件做進一步的操作
- 匿名物件:new Phone();
匿名物件是沒有名字的物件,所以創建程序:- 需要在堆記憶體中開辟一塊空間,用來存放物件
- 物件需要完成初始化,比如對應的屬性都有自己的對應型別的默認值
- 物件創建完畢后,會生成一個唯一的地址值用于區分不同的物件
那么我們使用匿名物件只能使用一次,并且一次只能使用一個功能
new Phone().video();//創建匿名物件1,呼叫看直播的方法
new Phone().message();//創建匿名物件2,呼叫看發短信的方法
9.代碼塊與它們的執行順序
1.靜態代碼塊 static { }
位置:類里方法外
執行時機:隨著類的加載而加載,最先加載到記憶體,優先于物件進行加載,直到類小消失,它才會消失
作用:一般用來加載那些只需要加載一次并且第一時間就需要加載資源,稱作:初始化
2.構造代碼塊 { }
位置:類里方法外
執行時機:創建物件時執行,創建幾次,執行幾次,并且優先于構造方法執行
作用:用于提取所有構造方法的共性功能
3.區域代碼塊 { }
位置:方法里
執行時機:當其所處的方法被呼叫時才會執行
作用:用于限制變數的作用范圍,出了區域代碼塊就失效
4.代碼塊之間的順序:
靜態代碼塊 -> 構造代碼塊 -> 構造方法 -> 普通方法【如果普通方法里有區域代碼塊,區域代碼塊才會執行】
10 static
- 被static修飾的資源統稱為靜態資源,可以用來修飾變數、方法、代碼塊、內部類
- 靜態資源屬于類資源,隨著類的加載而加載,優先于物件進行加載,只加載一次
- 靜態資源可以不通過物件,使用類名直接呼叫,不需要創建物件
- 靜態資源只有一份,被全域所有物件共享
- 靜態的呼叫關系:靜態資源只能呼叫靜態資源
- 靜態資源是優先于物件的,所以靜態資源不能與this和super共用
11 final
- final表示最終
- 被final修飾的類是最終類,也稱作葉子結點,所以不能被繼承
- 被final修飾的方法是這個方法的最終實作,不能被重寫
- 被final修飾的是常量,值不可以被修改,注意常量定義時必須賦值
12 抽象
- 抽象的關鍵字是abstract
- 被abstract修飾的方法是抽象方法,抽象方法沒有方法體
- 如果一個類中出現了一個抽象方法,那么這個類必須被abstract修飾
- 關于抽象類的特點:
1)抽象類中的方法不做限制 : 全普 / 全抽 / 半普半抽
2)如果一個類中的方法都是普通方法,還要宣告成抽象類,為什么?
為了不讓外界創建本類的物件
3)抽象類不可以創建物件,所以常用于多型
4)抽象類中包含構造方法,但是不是為了自己創建物件時使用,而是為了子類的super()
5)抽象類中也是可以定義成員變數的 - 如果一個子類繼承了一個抽象父類,有兩種解決方案:
1)作為抽象子類:不實作/實作部分 抽象父類中的抽象方法 : ”躺平”
2)作為普通子類:實作抽象父類中的所有的抽象方法 : “父債子償” - 面向抽象進行編程:后天重構的結果
13 介面
- 介面不是類,定義介面的關鍵字interface
- 如果一個類想要實作介面中定義的規則,需要使用implments與介面建立實作關系
注意:如果有任何一個抽象方法沒有實作,那么這個類就是一個抽象子類 - Java8中介面里的所有方法都是抽象方法
- 介面中只有靜態常量,沒有普通變數,會自動拼接public static final
- 介面中的方法也可以簡寫,會自動拼接public abstract
- 介面不可以實體化
- 介面中也沒有構造方法,實作類呼叫的是它自己父類的構造方法,如果沒有明確指定父類,那就是Object的
- 介面更多的是規則的制定者,不做具體的實作
- 介面降低了程式的耦合性,更加方便專案的維護與拓展
- 介面是先天設計的結果,這樣可以省去后續的多次重構,節省資源
14 介面與類的復雜關系
1.類與類的關系
Java的類只支持單繼承,類與類就是繼承關系,并且一個子類只能有一個父類
class Son extends Father{ }
2.介面與介面的關系
Java的介面是不做限制的,可以多繼承
interface Inter1 extends Inter2{ } – Inter1是子介面 Inter2 是父介面
interface Inter1 extends Inter2,Inter3{ } – Inter1 是子介面 Inter2 和 Inter3 都是父介面
注意:如果是情況2的話,介面1的實作類需要實作這三個介面(Inter1,2,3)的所有抽象方法
3.介面與類的關系
Java中的類對于介面而言是多實作的,所以一個類可以實作多個介面
class InterImpl implements Inter1{}
class InterImpl implements Inter1,Inter2{}
4.介面與抽象類的區別
- 介面是一種用interface定義的型別
抽象類是一種用class定義的型別 - 介面中的方法都是抽象方法,還有默認方法與靜態方法
抽象類中的方法不做限制 - 介面中的都是靜態常量
抽象類中可以寫普通的成員變數 - 介面沒有構造方法,不可實體化
抽象類有構造方法,但是也不可以實體化 - 介面是先天設計的結果,抽象是后天重構的結果
- 介面可以多繼承
抽象類只能單繼承
15 例外
- 例外的繼承結構
例外層次結構中的根是Throwable
Error:目前我們編碼解決不了的問題
Exception:例外
編譯例外:未運行代碼就報錯了,強制要求處理
運行時例外RunTimeException:運行代碼才報錯,可以通過編譯,不強制要求處理 - 例外的解決方案
- 捕獲處理try-catch–自己解決
- 格式:
try{
可能會出現例外的代碼
}catch(預測的例外型別 例外的名字){
預先設計的,捕獲到例外的處理方案
}finally{
例外處理結構中一定會被執行到的代碼塊,常用來關流
}
- 向上拋出throws–交給別人解決,在方法定義的兩個小括號之間throws,可拋出多個例外,用逗號隔開
- 不能直接把例外拋給main(),因為呼叫main()是JVM,沒人解決了
注意:是否拋出例外取決于自己的業務,比如暫時不處理或者處理不了需要交給別人處理

16 內部類
- 我們可以把內部類看作是外部類的一個特殊的資源
- 內部類可以直接使用外部類的所有資源,包括私有資源
- 外部類如果想要使用內部類的資源,需要創建內部類的物件才能使用
- 物件的普通創建方式:
/*外部類名.內部類名 物件名 = 外部類物件.內部類物件*/
Outer.Inner oi = new Outer().new Inner();
成員內部類
位置:類里方法外
1)被private修飾
被私有化的內部類在main()中是沒有辦法直接創建其物件的
可以在私有內部類所處的外部類中,創建一個公共的方法供外界呼叫,這個方法用來回傳創建好的私有內部類物件
2) 被static修飾
靜態內部類可以不創建外部類物件,直接創建靜態內部類物件,格式:Outer3.Inner3 oi = new Outer3.Inner3();
如果靜態內部類中還有靜態方法,那么我們可以不創建物件
直接通過鏈式加載的方式呼叫:Outer3.Inner3.show2();//表示通過外部類名直接找到靜態內部類,再找到靜態方法
區域內部類
位置:方法里
直接創建外部類物件,呼叫區域內部類所處的方法,并不會觸發區域內部類的功能
需要在外部類中創建區域內部類的物件并且進行呼叫區域內部類的功能,才能觸發內部類的功能
匿名內部類
位置:可運行代碼中,比如 main()中
匿名內部類通常與匿名物件【沒有名字的物件】一起使用
格式:new Inter1(){ 我這個大括號其實是一個匿名內部類,我來實作方法 }.eat();
如果只是想使用一次介面/抽象類的某個功能,可以使用匿名內部類
匿名內部類+匿名物件的功能:創建實作類+實作方法+方法功能的一次呼叫【功能三合一】
3 基礎API
1. Object
- 是所有類的超類,Java中的類都直接或者間接的繼承了Object
- 如果一個類沒有明確指定父類,那么默認繼承Object
- Object處于java.lang包之下,不需要導包可以直接使用
- toString()–我們日常使用最頻繁的列印陳述句底層就呼叫了這個方法
如果沒有重寫這個方法,使用的是Object的默認實作,列印的是物件的地址值
如果重寫以后,以重寫的邏輯為準,比如String列印的是串的具體內容,比如ArrayList,列印的是[集合元素] - hashCode()–用于回傳物件對應的哈希碼值
如果是一個物件多次呼叫這個方法,回傳的是同一個哈希碼值
如果是不同的物件呼叫這個方法,應該回傳的是不同的哈希碼值 - equals()–用于比較當前物件與引數物件是否相等
重寫之前的默認實作比較的是兩個物件的地址值
重寫之后取決于重寫的邏輯,比如String比較的是兩個串的具體內容,比如自定義物件比較的是型別+屬性值 - equals()與hashCode()應該保持一致【要重寫都重寫】
解釋:equals()底層默認實作比較的是==比較,地址值,重寫后我們一般比較的是物件的型別+屬性值
hashCode()不同的物件生成的哈希碼值不同,那么與equals()的邏輯不匹配,所以也應該重寫
重寫后,是根據物件的型別與屬性值來生成哈希碼值,這樣二者就一致了
2. String
- String底層維護的是一個char[],而且String不可變,因為原始碼中的陣列被final修飾了
- 創建方式:
char[] vlaues = {‘a’,‘b’,‘c’}; String s = new String(values);
String s = “abc”;有高效的效果,因為串存在堆中的常量池,第二次使用時就不再新建了 - 常用方法:
int hashCode() 回傳此字串的哈希碼,
boolean equals(Object anObject) 將此字串與指定的物件比較,比較的是重寫后的串的具體內容
String toString() 回傳此物件本身(它已經是一個字串!),
int length() 回傳此字串的長度,
String toUpperCase() 所有字符都轉換為大寫,
String toLowerCase() 所有字符都轉換為小寫
boolean startsWith(String prefix) 測驗此字串是否以指定的元素開頭,
boolean endsWith(String suffix) 測驗此字串是否以指定的字串結束,
char charAt(int index) 回傳指定索引/下標處的 char 值/字符
int indexOf(int ch) 回傳指定字符在此字串中第一次出現處的索引,
int lastIndexOf(int ch) 回傳指定字符在此字串中最后一次出現處的索引,
String concat(String str) 將指定字串連接/拼接到此字串的結尾,注意:不會改變原串
String[] split(String regex) 根據給定元素來分隔此字串,
String trim() 回傳去除首尾空格的字串
byte[] getBytes() 把字串存盤到一個新的 byte 陣列中
String substring(int beginIndex) 回傳一個新子串,從指定下標處開始,包含指定下標
String substring(int beginIndex, int endIndex) 回傳一個新子串,從執定下標開始,到結束下標為止,但不包含結束下標
static String valueOf(int i) 把int轉成String
3. StringBuilder與StringBuffer
String的:
- 特點:
創建之后長度內容是不可變的,每次拼接字串,都會產生新的物件 - 優缺點:
優點:String類提供了豐富的關于操作字串的方法,比如:拼接、獲取對應下標處的字符、截取子串等等
缺點:在進行字串拼接+=的時候,效率比較低 - String轉StringBuilder:
String s = “abc”; StringBuilder sb = new StringBuilder(s);
StringBuilder的:
- 特點:
StringBuilder是一個長度可變的字串序列,在創建的時候,會有一個長度為16的默認空間
當拼接字串的時候,是在原物件的基礎之上進行拼接,如果長度不夠就擴容
所以StringBuilder在創建之后,對應的操作一直是用一個物件 - 創建方式:
StringBuilder sb = new StringBuilder();//創建一個長度為16的StringBuilder物件
StringBuilder sb = new StringBuilder(“abc”);//以指定字串內容為“abc”的方式創建一個StringBuilder物件 - 優缺點:
優點:在拼接的時候,不會產生新的物件,就避免了因為拼接頻繁生成物件的問題,提高了程式的效率,使用的是append()
缺點:對于字串的操作,不太方便 - StringBuilder轉String:
StringBuilder sb = new StringBuilder();
sb.append(“abc”);
String s = sb.toString();
總結一句話,拼接多用StringBuilder,用完轉回String用String豐富的方法
4. 包裝類
- 基本型別只存值,也沒有豐富的功能
所以包裝型別是對基本型別做了包裝,并提供了很多方便的方法,所以包裝類的物件是參考型別的物件 - 創建方式:
Integer i1 = Integer.valueOf();資料只要在-128~127有一個高效的效果
Integer i2 = new Integer(4);沒有高效的效果,只是創建了一個包裝類的物件
5. 自動裝箱與自動拆箱
- 自動裝箱:
編譯器會自動把基本型別int5,包裝成包裝型別Integer
然后交給Integer型別的參考型別變數i3來保存
自動裝底層發生的代碼:Integer.valueOf(5)
valueOf()的方向:int–>Integer - 自動拆箱:
編譯器會自動把包裝型別的i1拆掉”箱子“,變回基本型別的資料127
然后交給基本型別int的變數i4來保存
自動拆箱底層發生的代碼:i1.intValue();
intValue()的方向:Integer-> int
package cn.tedu.api;
/*本類用于測驗自動裝箱與自動拆箱*/
public class TestNumber2 {
public static void main(String[] args) {
//1.定義包裝型別的資料
Integer i1 = new Integer(127);
Integer i2 = Integer.valueOf(127);
//2.現在的方式:
Integer i3 = 5;//不會報錯,這個現象就是自動裝箱
int i4 = i1;//不會報錯,這個現象就是自動拆箱
}
}
4 高級API
1. IO流
- 學習方式:學習抽象父級的公共方法 學習子類流物件的創建方式
- 流的分類
根據方向:輸入流 輸出流
根據操作單位:位元組流 字符流
位元組輸入流InputStream:
InputStream--抽象父類--不能實體化
FileInputStream--檔案位元組輸入流-FIS
BufferedInputStream--高效位元組輸入流-BIS
FIS in = new FIS(new File(路徑));
FIS in = new FIS(路徑);
BIS in = new BIS( new FIS(new File(路徑)));
BIS in = new BIS(new FIS(路徑));
位元組輸出流OutputStream:
OutputStream--抽象父類,不能實體化
FileOutputStream--檔案位元組輸出流--FOS
BufferedOutputStream--高效位元組輸出流-BOS
FOS out = new FOS(new File(路徑));
FOS out = new FOS(路徑);
BOS out = new BOS(new FOS(new File(路徑)));
BOS out = new BOS(new FOS(路徑));
字符輸入流Reader:
Reader--抽象父類--不能實體化
FileReader--檔案字符輸入流-FR
BufferedReader--高效字符輸入流-BR
FR in = new FR(new File(路徑));
FR in = new FR(路徑);
BR in = new BR(new FR(new File(路徑)))
BR in = new BR(new FR(路徑));
字符輸出流Writer:
Writer--抽象父類,不能實體化
FileWriter--檔案字符輸出流--FW
BufferedWriter--高效字符輸出流--BW
FW out = new FW(File/File,append/String pathname/String pathname,append);
BW out = new BW(Writer--所以傳的是子類FW(上面那4種));
注意:這里的append引數表示流向檔案輸出資料的時候是追加還是覆寫,如果不寫,默認false是覆寫,如果改為true,表示追加
- 序列化與反序列化
序列化與反序列化的作用就是物件的保存與傳輸
序列化:把記憶體中的物件通過序列化流輸出到磁盤中(比如檔案里),使用的流是ObjectOutputStream【把資料寫出到檔案】
反序列化:通過反序列化流將磁盤中的資料恢復成物件,使用的流是ObjectInputStream【把之前寫到檔案里的資料讀到程式中】
注意1:一個類的物件如果想被序列化,那么這個類必須實作可序列化介面
實作這個介面的目的是相當于給這個類做了一個標記,標記可以序列化
注意2:序列化時會自動生成一個UID,表示當前序列化輸出的物件的版本資訊
反序列化時會拿著當前的UID與之前序列化輸出的UID做比較,一致,反序列化成功,不一致,報錯
注意3: 所以,標準操作是一次序列化對應一次反序列化
如果目標物件所在的類沒有做任何修改,一次序列化也可以對應多次反序列化(根本原因是UID沒變)
2. 集合
- 泛型
泛型通常與集合一起使用,用來約束集合中元素的型別
泛型< type >必須寫參考型別而不是基本型別
泛型方法 public static == < E > == void get(E[] e){},兩處位置都出現了泛型,缺一不可 - 集合被稱作Collection,是一個可以存放多個資料的容器,而且集合中提供了豐富的方法來操作集合中的元素
是集合層次的根介面,學習抽象父級的公共方法 - Collection集合方法總結
單個集合的操作:
boolean add(E e) 將指定元素添加到集合中
void clear() 清空集合
boolean contains(Object o) 判斷本集合是否包含指定的元素
boolean equals(Object o) 比較集合物件與引數物件o是否相等
int hashCode() 回傳本集合的哈希碼值
boolean isEmpty() 判斷本集合是否為空
boolean remove(Object o) 從本集合中移除指定元素o
int size() 回傳本集合中元素的個數
Object[] toArray() 將本集合轉為陣列
集合間的操作:
boolean addAll(Collection<> c) 將c集合中的所有元素添加到本集合中
boolean containsAll(Collection<> c) 判斷本集合是否包含c集合的所有元素
boolean removeAll(Collection<> c) 移除本集合中屬于引數集合c的所有元素
boolean retainAll(Collection<> c) 保留本集合與引數集合c的公共元素
集合的迭代:
Iterator iterator() 回傳本集合的迭代器
- List介面的特點
- List集合是有下標的
- List集合是有順序的
- List集合可以存放重復的資料
單個集合的操作:
void add(int index, E element) 在集合的指定下標index處插入指定元素element
E get(int index) 回傳本集合中指定下標index處的元素
E remove(int index) 移除本集合中指定下標index處的元素
E set(int index, E element) 用引數元素element替換集合中指定下標index處的元素
int indexOf(Object o) 判斷指定元素o在本集合中第一次出現的下標,如果不存在,回傳-1
int lastIndexOf(Object o) 判斷指定元素o在本集合中最后一次出現的下標,如果不存在,回傳-1
List subList(int fromIndex, int toIndex) 截取子集合,包含formidex處的元素,不包含toIndex處的元素
集合間的操作與集合的迭代
boolean addAll(int index, Collection<> c) 將引數集合c中的所有元素,插入到本集合中指定的下標index處
ListIterator listIterator() 回傳此串列元素的迭代器,這個是List自己的,不太常用,可以逆序迭代
- List介面的兩個常用實作類
ArrayList的特點:
1)底層的資料結構是陣列,記憶體空間是連續的
2)元素有下標,通常可以根據下標進行操作
3)增刪操作比較慢,查詢操作比較快【資料量大時】
LinkedList的特點:
1)底層的資料結構是鏈表,記憶體空間是不連續的
2)元素有下標,但是通常首尾節點操作比較多
3)增刪操作比較快,查詢操作比較慢【資料量大時】
注意:LinkedList查詢慢也不是都慢,首尾操作還是比較快的
簡單方法:
void addFirst(E e) 添加首元素
void addLast(E e) 添加尾元素
E removeFirst() 洗掉首元素
E removeLast() 洗掉尾元素
E getFirst() 獲取首元素
E getLast() 獲取尾元素
E element() 獲取首元素
功能一致但是名字不太好記的方法:
boolean offer(E e) 添加尾元素
boolean offerFirst(E e) 添加首元素
boolean offerLast(E e) 添加尾元素
E peek() 獲取首元素
E peekFirst() 獲取首元素
E peekLast() 獲取尾元素
E poll() 回傳并移除頭元素
E pollFirst() 回傳并移除頭元素
E pollLast() 回傳并移除尾元素
- Map介面
Map介面的特點- map集合的結構是:鍵值對、KEY與VALUE、Map.Entry<K,V>的映射關系
- map中key值不允許重復,如果重復,對應的value會被覆寫
- map中的映射關系是無序的
- map沒有自己的迭代器,所以迭代時通常需要轉成set集合來迭代
簡單方法:
void clear() 清空集合
boolean equals(Object o) 判斷集合物件與引數o是否相等
int hashCode() 回傳本集合的哈希碼值
boolean isEmpty() 判斷集合是否為空
int size() 回傳本集合中鍵值對的個數
map單個集合間的操作
boolean containsKey(Object key) 判斷map中是否包含指定的key
boolean containsValue(Object value) 判斷map中是否包含指定的value
V get(Object key) 根據指定的key回傳對應的value,如果不存在,回傳null
V remove(Object key) 洗掉本集合中引數key對應的鍵值對
V put(K key, V value) 向集合中添加映射關系(鍵值對)
void putAll(Map<> m) 向本集合中添加m集合的所有映射關系(鍵值對)
map的迭代
Collection values() 把本map中的Value值取出放入一個Collection中并回傳這個Collection
Set keySet() 把本map中的Key值取出放入一個Set集合中并回傳這個Set集合
Set<Map.Entry<K,V>> entrySet()
把本map中的每一對KV都看成是一個Entry,把所有的Entry取出放入一個Set集合中并回傳這個Set集合
- Set介面
Set介面的特點- set集合沒有重復的元素
- set集合的元素是無序的
- set集合可以存null值,并且null最多有一個
- 我們自定義物件如果想去重,需要在自定義類中添加重寫的equals()與hashCode()
- 集合學習的方法
學習父級的公共方法,學習子類的創建方式,學習各種集合的特點- 關于List大多都是與下標有關的操作
- 關于Set通常都是去重的操作
- 關于map通常都是映射關系,也就是鍵值對
- API要常練習,方法互相之間沒有任何關系,用哪個,查哪個
3. 行程與執行緒
-
程式:資料與指令的集合,而且程式是靜態的
-
行程:運行中的程式,給程式加入了時間的概念,不同時間行程有不同的狀態,行程是動態的,代表OS中正在運行的程式
行程有獨立性,動態性,并發性 -
并行:相對來順澩比較充足,多個CPU同時并發處理多個不同的行程
-
串行:相對來順澩不太充足,多個資源同時搶占公共資源,比如CPU
-
執行緒:執行緒是OS能夠進行運算調度的最小單位
一個行程可以擁有多個執行緒,當然,也可以只擁有一個執行緒,只有一個執行緒的行程稱作單執行緒程式
注意:每個執行緒也有自己獨立的記憶體空間,當然也有一部分共享區域用來保存共享的資料 -
執行緒的幾種狀態以及執行緒狀態之間的切換
1)新建狀態:創建執行緒物件,申請PCB,對應的是new執行緒物件
2)就緒狀態/可運行狀態:萬事俱備,只欠CPU,剛剛創建好的執行緒物件所有資源已經準備好,并且加入到了就緒佇列之中
唯有等待作業系統的調度,只要分配了CPU,也就是時間片,當前執行緒可立即執行,對應的是start()
注意:呼叫start()并不會立即執行執行緒物件,這個是由OS的調度規則決定的,我們控制不了
3)執行/運行狀態:就緒佇列中的執行緒物件被OS選中,分配了時間片,正在執行
注意:只有就緒狀態才能變成運行狀態
4)阻塞狀態:執行緒在執行程序中遇到了問題,比如鎖阻塞、休眠阻塞、等待阻塞…
注意:我們的阻塞狀態,等問題解決了以后/獲取了臨界資源【要搶占的公共資源】后
是加入到就緒佇列中的,轉為就緒狀態,而不是轉為運行狀態直接執行
5)終止狀態:執行緒成功執行完畢,釋放資源,歸還PCB
6)執行緒的掛起:正在運行中的執行緒,由于CPU分配的時間片已經用完,所以需要凍結當前執行緒運行的狀態與各項資訊
把它插入到就緒佇列中,直到下次這個執行緒被調度執行時,重新恢復現場,繼續執行

-
多執行緒編程實作方案一:extends Thread繼承方式
1)自定義一個多執行緒類用來繼承Thread類
2)重寫run()里的業務【這個業務是自定義的】
3)創建執行緒物件【子類物件】
4)通過剛剛創建好的自定義執行緒類物件呼叫start()
注意1:不能呼叫run(),因為這樣呼叫只會把run()看作一個普通的方法,并不會以多執行緒的方式啟動程式
而且呼叫start()時,底層JVM會自動呼叫run(),執行我們自定義的業務
注意2:我們除了可以呼叫默認的父類無參構造以外,還可以呼叫Thread(String name),給自定義的執行緒物件起名字,相當于super(name); -
多執行緒編程實作方案二:implements Runnable 實作方式
1)自定義一個類實作介面Runnable
2) 實作介面中唯一一個抽象方法run()
3) 創建介面實作類的物件,這個物件是作為我們的目標業務物件【因為這個自定義類中包含了我們的業務】
4)創建Thread類執行緒物件,呼叫的建構式是Thread(Runnable target)
5)通過創建好的Thread類執行緒物件呼叫start(),以多執行緒的方式啟動同一個業務target
注意1:由于Runnable是一個介面,無法創建物件,所以我們傳入的目標業務類,也就是介面實作類的物件
注意2:只有呼叫start()才能把執行緒物件加入到就緒佇列中,以多執行緒的方式啟動,但是:
介面實作類與介面都沒有這個start(),所以我們需要創建Thread類的物件來呼叫start(),并把介面實作類物件交給Thread(target);
大家可以理解成“抱大腿”,創建的是Thread類的執行緒物件,我們只需要把業務告訴Thread類的物件就好啦
使用方式二的優勢:
1)耦合性不強,沒有繼承,后續仍然可以繼承別的類
2)采用的是實作介面的方式,后續仍然可以實作別的介面
3)可以給所有的執行緒物件統一業務,業務是保持一致的
4)面向介面編程,有利于我們寫出更加優雅的代碼 -
多執行緒編程實作方案三:Executors 創建執行緒池的方式
1)創建執行緒池的工具類:Executors.newFixedThreadPool(int n);可以創建包含最多n個執行緒的執行緒池物件
2)創建好的執行緒池物件:ExecutorService
使用pool.excute()來講執行緒池中的執行緒以多執行緒的方式啟動,每次呼叫都會將一個執行緒物件加入到就緒佇列之中
這個執行緒池物件負責: 新建/啟動/關閉執行緒,而我們主要負責的是自定義的業務
注意:執行緒池是不關閉的,實作的效果就是執行緒池中執行緒物件的隨取隨用,這樣就避免了頻繁的創建與銷毀執行緒,不會造成資源浪費
3)合理利用執行緒池可以擁有的優勢:
1. 降低系統的資源消耗:減少系統創建與銷毀執行緒物件的次數,每個執行緒都可以重復利用,執行多次任務
2. 提高回應速度:當任務到達時,任務可以不用等待執行緒創建就能立即執行
3. 提高執行緒的可管理性:可以根據系統的承受能力,調整執行緒池中執行緒的數目
防止因為創建多個執行緒消耗過多的記憶體導致服務器的崩潰
【每個執行緒大約需要1MB的記憶體,執行緒開的越多,消耗的記憶體也就越大,最后死機】 -
多執行緒資料安全隱患的解決方案
1)出現資料安全問題的原因:多執行緒程式 + 多個執行緒擁有共享資料 + 多條陳述句操作共享資料
2)解決:加鎖synchronized同步關鍵字
1. 同步方法【不太常用】,格式:在方法的定義上加synchronized
2. 同步代碼塊,格式:
synchronized(唯一的鎖物件){
可能會出現資料不安全問題的所有代碼
}
注意1:鎖物件必須唯一!!!
比如:如果是實作介面的方式,只需要創建一個介面實作類物件,而這個物件當做的是目標業務物件,類中定義的鎖物件自然也只有一個
比如:如果是繼承Thread類的方式,我們需要創建多個子類物件作為執行緒物件
那這個時候不同的執行緒物件間應該共享同一把鎖,所以需要把鎖設定為靜態,被全域所有物件共享
而且建議,此種方式使用的鎖物件是本類的位元組碼物件:類名.class
注意2:加鎖時,同步代碼塊的范圍需要考慮, 不能太大,太大效率太低;也不能太小,太小鎖不住
注意3:加鎖時,鎖物件的型別不做限制,只要保證鎖物件唯一即可
同步與異步
異步:是多個執行緒搶占資源的效果,不排隊,所以效率高,但是資料不安全
同步:每次只有一個執行緒獨占資源,排隊,所以效率低,但是安全,為了安全必要應該犧牲一部分資源
4. 注解
- JDK自帶的注解(5個)
要求大家掌握的是@Override注解,這個注解可以加在方法上,用來表示這是一個重寫的方法 - 元注解5個
元注解是用來定義其他注解的注解,也就是說,注解的語法與JAVA不同,是靠注解來定義的
定義注解的格式:@interface 注解名
可以根據元注解對注解進行設定:
表示被描述的注解可以使用的位置:值可以多選 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
表示被描述的注解的宣告周期:注意值只能3選1 @Retention(RentionPolicy.RUNTIME/SOURCE/CLASS) - 自定義注解
我們也可以根據自己的需求來定義個性化的注解:使用@interface 注解名來定義的,主要使用的就是上面的兩個元注解
除此之外,我們還可以給注解加功能,比如注解的屬性:
格式:屬性型別 屬性名(); 比如:int age();
注意:定義了注解的普通屬性以后,使用注解時必須給屬性賦值,格式:@Rice(age=10)
如果給屬性設定了默認值,那么使用注解時就不需要給屬性賦值了,格式:int age() default 0;
我們還可以給注解添加特殊的屬性value,注意這個屬性名字必須是value,型別不作限制
注意:特殊屬性如果不設定默認值,使用注解時也需要賦值,不過賦值可以簡寫,比如@Rice(“apple”)
特殊屬性賦予默認值后,就可以直接使用注解了,賦予默認值的格式:String value() default “apple”;
注意:如果有多個屬性,并且都沒有賦予默認值,那么使用注解時的格式:@Rice(value=“apple”,age=10)
5. 設計模式
- 概念:是一些前人總結出來的值得學習的編程“套路”,設計模式一共有23種
- 單例設計模式:確保代碼中本類的實體只有一個
- 實作思路:
方案一:餓漢式
1)把本類的構造方法私有化–為了不讓外界呼叫建構式來創建物件
2)通過本類的構造方法創建物件,并把這個物件也私有化,為了防止外界呼叫
3)提供公共的全域訪問點向外界回傳本類的唯一的一個物件
注意:公共方法需要設定成靜態–需要跳過物件,通過類名直接呼叫這個回傳本類物件的公共方法
物件也需要設定成靜態的–這個物件需要在靜態方法中被回傳,而靜態只能呼叫靜態
方案二:懶漢式
==延遲加載的思想:==我們有的時候有些資源并不是需要第一時間就創建出來,所以需要延遲到需要時再創建,這樣既可以提升性能,又可以節省資源
1)把本類的構造方法私有化–為了不讓外界呼叫建構式來創建物件
2)創建了一個本型別別的參考型別變數【這個變數后續用來保存創建出來的物件的地址值】
3)提供公共的全域訪問點向外界回傳本類的唯一的一個物件
注意:這個公共的方法里,需要做判斷
如果參考型別的變數值為null,說明:之前沒有創建過本類物件–創建后再賦值給參考型別變數,并把它回傳
如果參考型別的變數值不為null,說明:
之前有創建過本類物件,這個參考型別變數保存就是地址值,本次不再新建物件,直接回傳
6. 反射
- 反射的概念
反射是Java這門語言中比較有特點的一個特征,反射非常強大,我們可以通過反射獲取目標類當中的資源,甚至是私有資源
不僅僅如此,我們甚至還可以使用資源,并且創建物件,所以反射是一個經常被使用到的技術
開發程序中,我們有的時候并不能拿到源代碼,但是又需要使用資源,那這個時候反射的出現就很有必要了 - 反射需要用到的API
獲取位元組碼物件
Class.forName(“類的全路徑”); 注意:傳入的是類的全路徑名,包含包名.類名,而且會拋出例外
類名.class 注意:這個寫法需要自己手動接一下獲取到的位元組碼物件,不能用快捷方式的
物件.getClass(); 注意:經常與匿名物件一起使用
獲取包名 類名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//類名
clazz.getName()//完整類名
獲取成員變數定義資訊
getFields()//獲取所有公開的成員變數,包括繼承變數
getDeclaredFields()//獲取本類定義的成員變數,包括私有,但不包括繼承的變數
getField(變數名)
getDeclaredField(變數名)
獲取構造方法定義資訊
getConstructor(引數型別串列)//獲取公開的構造方法
getConstructors()//獲取所有的公開的構造方法
getDeclaredConstructors()//獲取所有的構造方法,包括私有
getDeclaredConstructor(int.class,String.class)
獲取方法定義資訊
getMethods()//獲取所有可見的方法,包括繼承的方法
getMethod(方法名,引數型別串列)
getDeclaredMethods()//獲取本類定義的的方法,包括私有,不包括繼承的方法
getDeclaredMethod(方法名,int.class,String.class)
反射新建實體
clazz.newInstance();//執行無參構造創建物件
clazz.getConstructor(int.class,String.class)//要先獲取構造方法
c.newInstance(666,”海綿寶寶”);//通過獲取到的建構式物件,創建目標類物件
反射呼叫成員變數
clazz.getDeclaredField(變數名);//獲取變數
field.setAccessible(true);//使私有成員允許訪問
field.set(實體,值);//為指定實體的變數賦值,靜態變數,第一引數給null
field.get(實體);//訪問指定實體變數的值,靜態變數,第一引數給null
反射呼叫成員方法
Method m = Clazz.getDeclaredMethod(方法名,引數型別串列);
m.setAccessible(true);//使私有方法允許被呼叫
m.invoke(實體,引數資料);//讓指定實體來執行該方法
- 關于反射的學習方式
如果能夠直接操作原始碼,就不需要使用反射
反射經常用于原始碼與三大框架底層之中,熟練掌握有助于編程思想的提高
反射的思路與正常使用略有區別,所以需要多練習,掌握這些寫法
重點參考筆記中的8個單元測驗方法
反射技術詳細筆記
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/342201.html
標籤:java
