Java入門到精通的學(xué)習(xí)筆記:零基礎(chǔ)如何快速學(xué)會(huì)Java開發(fā)?

背景知識(shí)
Java 相關(guān)概念
- JavaSE (Java Standard Edition): 基礎(chǔ)版,用于開發(fā)桌面應(yīng)用程序。
- JavaEE (Java Enterprise Edition): 企業(yè)版,用于開發(fā)企業(yè)級(jí)應(yīng)用程序。
- JavaME (Java Micro Edition): 微型版,用于開發(fā)嵌入式系統(tǒng)和移動(dòng)設(shè)備應(yīng)用程序。
編譯與運(yùn)行
編譯階段:
- 源文件:
.java文件。 - 字節(jié)碼文件:
.class文件。 - 編譯工具:
javac.exe,用于將.java文件編譯為.class文件。- 命令:
javac 文件名.java - 編譯包:
javac -d 編譯后存放路徑 java源文件路徑
- 命令:
- 源文件:
運(yùn)行階段:
- 運(yùn)行工具:
java.exe,用于運(yùn)行.class文件。- 命令:
java 類名(不帶.class后綴)
- 命令:
- JVM (Java Virtual Machine): Java 虛擬機(jī),負(fù)責(zé)執(zhí)行字節(jié)碼文件。
- 運(yùn)行工具:
開發(fā)環(huán)境
- JDK (Java Development Kit): Java 開發(fā)工具包,包含編譯器、調(diào)試器等開發(fā)工具。
- JRE (Java Runtime Environment): Java 運(yùn)行環(huán)境,包含 JVM 和運(yùn)行 Java 程序所需的庫(kù)。
- JVM (Java Virtual Machine): Java 虛擬機(jī),負(fù)責(zé)執(zhí)行字節(jié)碼文件。
工具與格式
- native2ascii: 用于將 Unicode 字符轉(zhuǎn)換為
\u表示的 ASCII 格式。 - UML (Unified Modeling Language): 面向?qū)ο笤O(shè)計(jì)圖,用于表示類、接口、繼承、實(shí)現(xiàn)等關(guān)系。
- 空心箭頭: 指向父類(繼承)。
- 空心虛線箭頭: 指向接口(實(shí)現(xiàn))。
- 實(shí)心實(shí)線箭頭: 表示關(guān)聯(lián)關(guān)系。
注釋
- 單行注釋:
// - 多行注釋:
/* */ - 文檔注釋:
/** */,用于生成幫助文檔。
類與方法結(jié)構(gòu)
類體 {
方法體 {
java語(yǔ)句;
}
}
總結(jié)
- JavaSE 是基礎(chǔ)版,JavaEE 是企業(yè)版,JavaME 是微型版。
- 編譯 使用
javac,運(yùn)行 使用java。 - JDK 是開發(fā)工具包,JRE 是運(yùn)行環(huán)境,JVM 是虛擬機(jī)。
- UML 用于面向?qū)ο笤O(shè)計(jì),注釋 用于代碼說(shuō)明。
- 類與方法 的基本結(jié)構(gòu)如上所示。
Java SE API 和文檔
一、集成開發(fā)環(huán)境(IDEA)
以下是用戶提供的快捷鍵和組織方式的總結(jié):
組織方式
- Project(工程): 最高層級(jí),包含多個(gè)模塊。
- Module(模塊): 工程下的子模塊,包含多個(gè)包。
- Package(包): 模塊下的子包,用于組織類和資源。
字體設(shè)置
- 路徑:
File -> Settings -> Font
用于調(diào)整編輯器的字體樣式和大小。
快捷鍵分類總結(jié)
導(dǎo)航與操作
展開/移動(dòng)列表:
- 左右箭頭: 展開或折疊列表。
- 上下箭頭: 在列表中移動(dòng)。
切換與定位:
- Alt+左右箭頭: 切換 Java 程序。
- Alt+上下箭頭: 在方法間快速移動(dòng)。
- Alt+標(biāo)號(hào): 打開標(biāo)號(hào)窗口。
- Ctrl+G: 定位到文件的某一行。
- Ctrl+點(diǎn)擊: 切換源碼。
- Ctrl+H: 查看實(shí)現(xiàn)類。
查找與搜索:
- Ctrl+Shift+N: 查找文件。
- Ctrl+N: 查找類文件。
- Ctrl+F12: 在當(dāng)前類中查找一個(gè)方法。
編輯與格式化
代碼編輯:
- Ctrl+Y: 刪除一行。
- Shift+F6: 重命名。
- Alt+拖動(dòng): 一次編輯多行。
- Ctrl+Alt+T: 將選中的代碼放在
TRY{}、IF{}、ELSE{}中。
代碼提示與自動(dòng)補(bǔ)全:
- Ctrl+空格: 代碼提示。
- Ctrl+P: 方法參數(shù)提示。
- Ctrl+J: 自動(dòng)代碼。
- Ctrl+Alt+Space: 類名或接口名提示。
格式化與優(yōu)化:
- Ctrl+Alt+L: 格式化代碼。
- Ctrl+Alt+I: 自動(dòng)縮進(jìn)。
- Ctrl+Alt+O: 優(yōu)化導(dǎo)入的類和包。
運(yùn)行與糾錯(cuò)
運(yùn)行程序:
- Ctrl+Shift+F10: 運(yùn)行當(dāng)前程序。
糾錯(cuò)與提示:
- Alt+回車: 糾錯(cuò)提示。
窗口操作
- 全屏模式:
- Ctrl+Shift+F12: 切換全屏模式。
總結(jié)
- 組織方式: 工程 -> 模塊 -> 包,層級(jí)清晰,便于管理。
- 快捷鍵:
- 導(dǎo)航與查找:快速定位文件、類、方法。
- 編輯與格式化:提高代碼編寫效率。
- 運(yùn)行與糾錯(cuò):快速運(yùn)行程序并修復(fù)錯(cuò)誤。
- 窗口操作:優(yōu)化開發(fā)環(huán)境布局。
二、JVM內(nèi)存劃分
局部變量在方法體中聲明,運(yùn)行階段內(nèi)存在棧中分配
方法區(qū)內(nèi)存:字節(jié)碼文件在加載 的時(shí)候?qū)⑵浞旁诜椒▍^(qū)之中(最先有數(shù)據(jù),調(diào)用方法時(shí)在棧內(nèi)分配空間)
堆內(nèi)存(heap):new對(duì)象(成員變量中的實(shí)例變量(一個(gè)對(duì)象一份)在java對(duì)象內(nèi)部存儲(chǔ)),只能通過(guò)引用調(diào)用操作
棧(stack)內(nèi)存:棧幀永遠(yuǎn)指向棧頂元素,棧頂元素處于活躍狀態(tài),先進(jìn)后出,后進(jìn)先出(存儲(chǔ)局部變量)

內(nèi)存區(qū)域與數(shù)據(jù)存儲(chǔ)
堆內(nèi)存(Heap):
- 存儲(chǔ)實(shí)例變量(對(duì)象屬性)。
- 每個(gè) JVM 實(shí)例只有一個(gè)堆內(nèi)存,所有線程共享。
- 垃圾回收器(GC)主要針對(duì)堆內(nèi)存進(jìn)行回收。
方法區(qū)(Method Area):
- 存儲(chǔ)靜態(tài)變量(類變量)和類元數(shù)據(jù)(如類信息、常量池等)。
- 每個(gè) JVM 實(shí)例只有一個(gè)方法區(qū),所有線程共享。
- 方法區(qū)是最先有數(shù)據(jù)的內(nèi)存區(qū)域,因?yàn)轭惣虞d時(shí)靜態(tài)變量和類信息會(huì)初始化。
棧內(nèi)存(Stack):
- 存儲(chǔ)局部變量和方法調(diào)用棧幀。
- 每個(gè)線程有一個(gè)獨(dú)立的棧內(nèi)存,線程私有。
- 棧內(nèi)存是使用最頻繁的內(nèi)存區(qū)域,因?yàn)榉椒ㄕ{(diào)用和局部變量的生命周期較短。
變量存儲(chǔ)位置
局部變量:
- 存儲(chǔ)在棧內(nèi)存中。
- 生命周期與方法調(diào)用一致,方法結(jié)束時(shí)局部變量會(huì)被銷毀。
實(shí)例變量:
- 存儲(chǔ)在堆內(nèi)存中。
- 生命周期與對(duì)象一致,對(duì)象被垃圾回收時(shí)實(shí)例變量會(huì)被銷毀。
靜態(tài)變量:
- 存儲(chǔ)在方法區(qū)中。
- 生命周期與類一致,類卸載時(shí)靜態(tài)變量會(huì)被銷毀。
垃圾回收器(GC)
主要目標(biāo):
- 垃圾回收器主要針對(duì)堆內(nèi)存進(jìn)行回收,清理不再使用的對(duì)象。
- 棧內(nèi)存和方法區(qū)的垃圾回收機(jī)制與堆內(nèi)存不同。
特點(diǎn):
- 堆內(nèi)存是垃圾回收的主要區(qū)域,因?yàn)閷?duì)象生命周期較長(zhǎng)且占用內(nèi)存較大。
- 棧內(nèi)存和方法區(qū)的垃圾回收效率較高,因?yàn)樗鼈兊纳芷谳^短且數(shù)據(jù)量相對(duì)較小。
三、關(guān)鍵字:
類與關(guān)鍵字
public:- 表示公開的類,類名必須與文件名一致,且一個(gè)文件中只能有一個(gè)
public類。
- 表示公開的類,類名必須與文件名一致,且一個(gè)文件中只能有一個(gè)
class:- 用于定義一個(gè)類。
static:- 表示靜態(tài)的,修飾的成員變量或方法屬于類級(jí)別,不依賴于對(duì)象。
- 靜態(tài)變量在類加載時(shí)初始化,存儲(chǔ)在方法區(qū)內(nèi)存中。
- 靜態(tài)方法不能訪問(wèn)實(shí)例變量或?qū)嵗椒?,需要通過(guò)對(duì)象訪問(wèn)。
break:- 用于跳出循環(huán)或
switch語(yǔ)句。
- 用于跳出循環(huán)或
continue:- 用于跳過(guò)當(dāng)前循環(huán)的剩余部分,直接進(jìn)入下一次循環(huán)。
- 語(yǔ)法:
continue 循環(huán)名稱;或循環(huán)名稱:。
this:- 表示當(dāng)前對(duì)象的引用。
- 用于區(qū)分局部變量和實(shí)例變量,或在構(gòu)造方法中調(diào)用其他構(gòu)造方法(
this(實(shí)參))。 - 不能用于靜態(tài)方法中。
native:- 用于調(diào)用 JVM 本地程序。
輸入與輸出
System.out.println():- 控制臺(tái)輸出,
println表示輸出并換行。
- 控制臺(tái)輸出,
鍵盤輸入:
- 創(chuàng)建鍵盤掃描器對(duì)象:
java.util.Scanner s = new java.util.Scanner(System.in); - 字符串輸入:
String user = s.next(); - 整數(shù)輸入:
int num = s.nextInt();
- 創(chuàng)建鍵盤掃描器對(duì)象:
final關(guān)鍵字
修飾類:
- 類不能被繼承。
修飾方法:
- 方法不能被重寫。
修飾變量:
- 變量不能被修改。
- 修飾的成員變量必須手動(dòng)賦值。
- 修飾的引用一旦指向一個(gè)對(duì)象,就不能指向其他對(duì)象,但所指向的內(nèi)存可以修改。
常量:
- 定義常量:
public static final 類型 常量名 = 值; - 命名規(guī)則:全部大寫,用下劃線分隔。
- 定義常量:
super關(guān)鍵字
作用:
- 代表當(dāng)前對(duì)象的父類型特征。
- 用于訪問(wèn)父類的屬性、方法或調(diào)用父類的構(gòu)造方法。
語(yǔ)法:
- 訪問(wèn)父類屬性或方法:
super. - 調(diào)用父類構(gòu)造方法:
super()
- 訪問(wèn)父類屬性或方法:
規(guī)則:
- 不能用于靜態(tài)方法中。
- 如果父類和子類有同名屬性,訪問(wèn)父類屬性時(shí)不能省略
super。 - 構(gòu)造方法的第一行如果沒(méi)有
this()或super(),默認(rèn)會(huì)調(diào)用super()。
static關(guān)鍵字
靜態(tài)變量:
- 屬于類級(jí)別,不依賴于對(duì)象,類加載時(shí)初始化。
靜態(tài)方法:
- 類級(jí)別的方法,不能訪問(wèn)實(shí)例變量或?qū)嵗椒ā?/li>
靜態(tài)代碼塊:
- 在類加載時(shí)執(zhí)行,只執(zhí)行一次。
- 語(yǔ)法:
static {}
實(shí)例代碼塊:
- 在構(gòu)造方法執(zhí)行之前執(zhí)行,用于對(duì)象初始化。
包與導(dǎo)入
package:- 用于管理類,命名規(guī)則:公司域名倒序.項(xiàng)目名.模塊名.功能名。
- 語(yǔ)法:
package 包名;
import:- 用于導(dǎo)入包中的類。
- 語(yǔ)法:
import 包名.類名;或import 包名.*; java.lang.*是核心語(yǔ)言包,無(wú)需導(dǎo)入。
快捷鍵:
Ctrl+Shift+O:自動(dòng)導(dǎo)入。
訪問(wèn)控制權(quán)限修飾符
private:- 私有訪問(wèn)權(quán)限,只能在本類中訪問(wèn)。
default:- 默認(rèn)訪問(wèn)權(quán)限,可以被本包中的其他類訪問(wèn)。
protected:- 受保護(hù)的訪問(wèn)權(quán)限,可以被本包及不同包的子類訪問(wèn)。
public:- 公共訪問(wèn)權(quán)限,可以在任何地方訪問(wèn)。
類的修飾符:
- 類只能使用
public或默認(rèn)修飾符(缺?。?,內(nèi)部類除外。
- 類只能使用
總結(jié)
- 類與關(guān)鍵字:
public、class、static、this、super等關(guān)鍵字的作用與用法。 - 輸入與輸出:控制臺(tái)輸出與鍵盤輸入的基本操作。
final:用于修飾類、方法、變量,表示不可修改。static:修飾類級(jí)別的成員,與對(duì)象無(wú)關(guān)。- 包與導(dǎo)入:
package和import的使用及命名規(guī)則。 - 訪問(wèn)控制權(quán)限:
private、default、protected、public的訪問(wèn)范圍。
四、Java基礎(chǔ)
以下是用戶提供的內(nèi)容的總結(jié):
標(biāo)識(shí)符
定義:
- 用戶有權(quán)命名的單詞,包括類名、方法名、常量名、變量名、接口名等。
命名規(guī)則:
- 類名、接口名: 首字母大寫,后面每個(gè)單詞首字母大寫(大駝峰命名法)。
- 方法名、變量名: 首字母小寫,后面每個(gè)單詞首字母大寫(小駝峰命名法)。
- 常量名: 全部大寫,單詞間用下劃線分隔。
字面值
- 定義: 數(shù)據(jù)本身,如數(shù)字、字符串等,通常以紫色顯示。
變量
局部變量:
- 定義在方法體內(nèi),沒(méi)有默認(rèn)值,必須手動(dòng)初始化。
- 生命周期與方法調(diào)用一致。
成員變量:
- 定義在類體內(nèi),有默認(rèn)值(數(shù)值類型為 0,布爾類型為
false,引用類型為null)。 - 分為實(shí)例變量和靜態(tài)變量。
- 定義在類體內(nèi),有默認(rèn)值(數(shù)值類型為 0,布爾類型為
實(shí)例變量:
- 不帶
static關(guān)鍵字,屬于對(duì)象級(jí)別。 - 必須通過(guò)對(duì)象引用訪問(wèn)(
引用.變量名)。 - 存儲(chǔ)在堆內(nèi)存中。
- 不帶
靜態(tài)變量:
- 帶
static關(guān)鍵字,屬于類級(jí)別。 - 在類加載時(shí)初始化,存儲(chǔ)在方法區(qū)內(nèi)存中。
- 通過(guò)類名訪問(wèn)(
類名.變量名)。
- 帶
引用
- 定義: 是一個(gè)變量,可以是實(shí)例變量或局部變量。
- 實(shí)例變量:
類名 引用 = new 類名(); - 局部變量:
引用 變量名 = new 引用();
- 實(shí)例變量:
數(shù)據(jù)類型
基本數(shù)據(jù)類型:
- 整數(shù)型:
byte(1 字節(jié))、short(2 字節(jié))、int(4 字節(jié))、long(8 字節(jié),后綴L)。 - 浮點(diǎn)型:
float(4 字節(jié))、double(8 字節(jié))。 - 布爾型:
boolean(1 字節(jié))。 - 字符型:
char(2 字節(jié))。
- 整數(shù)型:
引用數(shù)據(jù)類型:
- 字符串:
String,不可變,存儲(chǔ)在方法區(qū)字符串池中。
- 字符串:
比較:
- 基本數(shù)據(jù)類型使用
==判斷相等。 - 引用數(shù)據(jù)類型(包括
String)使用equals判斷相等。
- 基本數(shù)據(jù)類型使用
字符編碼
- 發(fā)展順序: ASCII < ISO-8859-1 < GB2312 < GBK < GB18030 < Big5 < Unicode(統(tǒng)一全球編碼)。
位運(yùn)算符
- 邏輯異或(^): 兩邊不一樣為真。
- 短路與(&&): 左邊為假時(shí)直接返回假。
- 按位與(&): 將操作數(shù)轉(zhuǎn)換為二進(jìn)制后按位與。
- 短路或(||): 左邊為真時(shí)直接返回真。
- 左移(<<): 二進(jìn)制數(shù)據(jù)左移,相當(dāng)于乘以 2 的 N 次方。
- 右移:
- 帶符號(hào)右移(>>): 正數(shù)用 0 填充,負(fù)數(shù)用 1 填充。
- 無(wú)符號(hào)右移(>>>): 無(wú)論正負(fù)都用 0 填充。
- 按位取反(~): 將二進(jìn)制每一位取反,結(jié)果為
-(n+1)。
方法(函數(shù))
定義:
[修飾符列表] 返回值類型 方法名(形參列表) { 方法體; return; // return 后不能跟語(yǔ)句 }調(diào)用:
類名.方法名(實(shí)參列表);實(shí)例方法: 不帶
static,需要對(duì)象參與。靜態(tài)方法: 帶
static,與對(duì)象無(wú)關(guān)。
方法重載(Overload)
- 定義: 在同一類中,方法名相同但參數(shù)列表不同。
- 特點(diǎn): 與返回值類型和修飾符列表無(wú)關(guān)。
方法遞歸
- 定義: 方法調(diào)用自身,每次遞歸都會(huì)分配新的內(nèi)存空間(壓棧)。

示例:
public static int sum(int n) { if (n == 1) { return 1; } return n + sum(n - 1); }
方法覆蓋(Override)
- 定義: 發(fā)生在繼承關(guān)系中,子類重寫父類的方法。
- 規(guī)則:
- 方法名、返回值類型、形參列表必須與父類一致。
- 訪問(wèn)權(quán)限不能比父類更低,拋出異常不能更多。
- 限制:
- 私有方法、構(gòu)造方法不能覆蓋。
- 靜態(tài)方法不存在覆蓋。
總結(jié)
- 標(biāo)識(shí)符: 命名規(guī)則與用途。
- 變量: 局部變量、實(shí)例變量、靜態(tài)變量的定義與存儲(chǔ)位置。
- 數(shù)據(jù)類型: 基本數(shù)據(jù)類型與引用數(shù)據(jù)類型的區(qū)別。
- 位運(yùn)算符: 各種位運(yùn)算符的作用與用法。
- 方法: 定義、調(diào)用、重載、遞歸與覆蓋的規(guī)則與特點(diǎn)。
五、Java 控制流與 Lambda 表達(dá)式
1.控制流語(yǔ)句
If-Else 語(yǔ)句:
if (條件) { // 語(yǔ)句 } else if (表達(dá)式) { // 語(yǔ)句 } else { // 語(yǔ)句 }Switch 語(yǔ)句:
switch (關(guān)鍵詞) { case 關(guān)鍵詞: // java語(yǔ)句 break; default: // 默認(rèn)語(yǔ)句 }For 循環(huán):
for (初始表達(dá)式; 布爾表達(dá)式; 更新循環(huán)體) { // 循環(huán)體 }增強(qiáng) For 循環(huán)(For Each):
for (元素類型 變量名 : 數(shù)組或集合) { System.out.println(變量名); }While 循環(huán):
while (表達(dá)式) { // 循環(huán)體 }Do-While 循環(huán):
do { // 循環(huán)體 } while (布爾表達(dá)式);
2.Java 標(biāo)簽
- 標(biāo)簽用于控制嵌套循環(huán)的跳轉(zhuǎn)和中斷。
- 語(yǔ)法:
label: - 用法:
continue label;:跳過(guò)當(dāng)前循環(huán),繼續(xù)執(zhí)行標(biāo)簽處的循環(huán)。break label;:結(jié)束標(biāo)簽處的循環(huán),執(zhí)行循環(huán)后的代碼。
3.Lambda 表達(dá)式
實(shí)現(xiàn) Runnable:
// Java 8 之前 new Thread(new Runnable() { @Override public void run() { System.out.println("Before Java8, too much code for too little to do"); } }).start(); // Java 8 方式 new Thread(() -> System.out.println("In Java8, Lambda expression rocks !!")).start();事件處理:
// Java 8 之前 JButton show = new JButton("Show"); show.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Event handling without lambda expression is boring"); } }); // Java 8 方式 show.addActionListener((e) -> { System.out.println("Light, Camera, Action !! Lambda expressions Rocks"); });列表迭代:
// Java 8 之前 List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API"); for (String feature : features) { System.out.println(feature); } // Java 8 之后 features.forEach(n -> System.out.println(n)); // 使用方法引用 features.forEach(System.out::println);Map 和 Reduce:
// 不使用 lambda 表達(dá)式 List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); for (Integer cost : costBeforeTax) { double price = cost + .12 * cost; System.out.println(price); } // 使用 lambda 表達(dá)式 costBeforeTax.stream().map((cost) -> cost + .12 * cost).forEach(System.out::println); // 使用 reduce 計(jì)算總和 double bill = costBeforeTax.stream().map((cost) -> cost + .12 * cost).reduce((sum, cost) -> sum + cost).get(); System.out.println("Total : " + bill);對(duì)列表的每個(gè)元素應(yīng)用函數(shù):
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.", "Canada"); String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", ")); System.out.println(G7Countries);計(jì)算集合元素的最大值、最小值、總和以及平均值:
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29); IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("Highest prime number in List : " + stats.getMax()); System.out.println("Lowest prime number in List : " + stats.getMin()); System.out.println("Sum of all prime numbers : " + stats.getSum()); System.out.println("Average of all prime numbers : " + stats.getAverage());
總結(jié)
- 控制流語(yǔ)句:用于控制程序的執(zhí)行流程,包括條件判斷、循環(huán)等。
- Java 標(biāo)簽:用于控制嵌套循環(huán)的跳轉(zhuǎn)和中斷。
- Lambda 表達(dá)式:簡(jiǎn)化了匿名類的使用,使代碼更簡(jiǎn)潔,特別是在實(shí)現(xiàn)函數(shù)式接口(如
Runnable、ActionListener)時(shí)非常有用。 - Stream API:提供了強(qiáng)大的集合操作功能,如
map、reduce、forEach等,使得對(duì)集合的處理更加高效和簡(jiǎn)潔。
六、面向?qū)ο?/h2>
面向過(guò)程與面向?qū)ο蟮膶?duì)比
面向過(guò)程:
- 因果關(guān)系:關(guān)注問(wèn)題的具體步驟和流程。
- 具體過(guò)程:強(qiáng)調(diào)如何一步步解決問(wèn)題。
- 耦合度高:各個(gè)模塊之間依賴性強(qiáng),修改一個(gè)模塊可能會(huì)影響其他模塊。
- 軟件拓展性差:由于耦合度高,系統(tǒng)的擴(kuò)展和維護(hù)較為困難。
面向?qū)ο?/strong>:
- 分類對(duì)象:將問(wèn)題分解為多個(gè)對(duì)象,每個(gè)對(duì)象負(fù)責(zé)特定的功能。
- 關(guān)系層度低:對(duì)象之間的依賴關(guān)系較弱,耦合度低。
- 關(guān)注對(duì)象功能:關(guān)注對(duì)象能完成哪些功能,而不是具體的實(shí)現(xiàn)步驟。
- 三大特征:
- 封裝性:將復(fù)雜的事務(wù)封裝起來(lái),只保留簡(jiǎn)單的操作入口。封裝后形成獨(dú)立的對(duì)象,提高了代碼的復(fù)用性、適應(yīng)性和安全性。
- 繼承性:實(shí)現(xiàn)代碼復(fù)用,最重要的是支持多態(tài)和方法覆蓋。
- 多態(tài)性:父類型的引用可以指向子類型對(duì)象,降低程序耦合度,提高擴(kuò)展力。
面向?qū)ο蟮姆治雠c設(shè)計(jì)
- 面向?qū)ο蟮姆治觯∣OA):分析問(wèn)題域,識(shí)別對(duì)象及其關(guān)系。
- 面向?qū)ο蟮脑O(shè)計(jì)(OOD):設(shè)計(jì)對(duì)象的結(jié)構(gòu)和行為,定義類及其關(guān)系。
- 面向?qū)ο蟮木幊蹋∣OP):使用編程語(yǔ)言實(shí)現(xiàn)設(shè)計(jì),創(chuàng)建對(duì)象并實(shí)現(xiàn)其功能。
類與對(duì)象
- 類:高度抽象的對(duì)象的集合,是一個(gè)模板。
- 靜態(tài)代碼塊:類加載時(shí)執(zhí)行。
- 實(shí)例代碼塊:實(shí)例化時(shí)執(zhí)行。
- 靜態(tài)變量:類級(jí)別的變量。
- 實(shí)例變量:對(duì)象級(jí)別的變量,存儲(chǔ)在堆內(nèi)存中。
- 構(gòu)造方法:創(chuàng)建對(duì)象時(shí)調(diào)用,用于初始化實(shí)例變量。
- 靜態(tài)方法:類級(jí)別的方法。
- 實(shí)例方法:對(duì)象級(jí)別的方法。
- 成員變量:對(duì)象的屬性,描述對(duì)象的狀態(tài)。
- 成員方法:對(duì)象的行為,描述對(duì)象的動(dòng)作。
- 對(duì)象:類的具體實(shí)例。
- 創(chuàng)建對(duì)象:
類名 對(duì)象名稱 = new 類名(); - 使用對(duì)象:
對(duì)象名稱.屬性名或對(duì)象名稱.方法名() - 修改對(duì)象:
引用.變量名 = 值 - 引用與對(duì)象:引用保存了對(duì)象的地址,指向堆內(nèi)存中的對(duì)象。多個(gè)引用可以指向同一個(gè)對(duì)象,但一個(gè)引用只能指向一個(gè)對(duì)象。
- 創(chuàng)建對(duì)象:
User u=new User(); Address a=new Address(); u.addr=a; Print(u.addr.city); A.city=”天津”; Print(u.addr.city);

封裝
- 私有化屬性:使用
private關(guān)鍵字將屬性私有化。 - 提供操作入口:通過(guò)
getter和setter方法提供對(duì)屬性的訪問(wèn)和修改。- 讀取屬性:
public 數(shù)據(jù)類型 get屬性名() { return 屬性; } - 修改屬性:
public void set屬性名(數(shù)據(jù)類型 屬性) { this.屬性 = 屬性; }
- 讀取屬性:
- 業(yè)務(wù)邏輯控制:在
setter方法中添加業(yè)務(wù)邏輯進(jìn)行安全控制。
構(gòu)造方法
- 作用:創(chuàng)建對(duì)象并初始化實(shí)例變量。
- 語(yǔ)法:
修飾符 構(gòu)造方法名(形參) { 構(gòu)造方法體; this.實(shí)例變量 = 形參; } - 特點(diǎn):沒(méi)有返回值類型,方法名與類名一致,不能使用
return返回值,但可以使用return結(jié)束方法。 - 調(diào)用:
new 構(gòu)造方法名(實(shí)參) - 缺省構(gòu)造器:如果沒(méi)有定義構(gòu)造方法,編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的缺省構(gòu)造器。
繼承
- 語(yǔ)法:
[修飾符列表] class 子類名 extends 父類名 { 類體 = 屬性 + 方法 } - 單繼承:Java中類只能繼承一個(gè)父類。
- 繼承關(guān)系:
- 父類:也稱為基類、超類、
superclass。 - 子類:也稱為派生類、
subclass。
- 父類:也稱為基類、超類、
- 不可繼承:私有的屬性和方法、構(gòu)造方法。
- 間接繼承:通過(guò)繼承鏈,子類可以間接繼承父類的父類。
- 默認(rèn)繼承:如果沒(méi)有顯式繼承任何類,默認(rèn)繼承
java.lang.Object類。 - super關(guān)鍵字:用于調(diào)用父類的屬性、方法和構(gòu)造方法。
多態(tài)
- 向上轉(zhuǎn)型(Upcasting):子類轉(zhuǎn)換為父類型,自動(dòng)類型轉(zhuǎn)換。
- 語(yǔ)法:
父類 引用 = new 子類(); - 特點(diǎn):編譯通過(guò),運(yùn)行沒(méi)有問(wèn)題。
- 語(yǔ)法:
- 向下轉(zhuǎn)型(Downcasting):父類轉(zhuǎn)換為子類,強(qiáng)制類型轉(zhuǎn)換。
- 語(yǔ)法:
子類 引用 = (子類) 父類引用; - 特點(diǎn):存在隱患,可能導(dǎo)致
ClassCastException異常。
- 語(yǔ)法:
- 動(dòng)態(tài)綁定:父類型引用指向子類型對(duì)象,調(diào)用方法時(shí)實(shí)際執(zhí)行的是子類的方法。
- instanceof運(yùn)算符:用于在強(qiáng)制轉(zhuǎn)換前檢查對(duì)象的類型,避免
ClassCastException異常。- 語(yǔ)法:
引用 instanceof 數(shù)據(jù)類型名 - 返回值:布爾類型,
true表示引用指向的對(duì)象是后面的數(shù)據(jù)類型,false表示不是。
- 語(yǔ)法:
以下是關(guān)于 抽象類 和 接口 的總結(jié):
抽象類
定義:
- 使用
abstract關(guān)鍵字修飾的類,是類的進(jìn)一步抽象。 - 屬于引用數(shù)據(jù)類型。
- 使用
語(yǔ)法:
[修飾符列表] abstract class 類名 {}特點(diǎn):
- 不能使用
private或final修飾。 - 抽象類可以包含抽象方法和非抽象方法。
- 抽象類的子類可以是抽象類或非抽象類。
- 不能實(shí)例化(不能創(chuàng)建對(duì)象),但可以有構(gòu)造方法,供子類使用。
- 不能使用
抽象方法:
- 使用
abstract關(guān)鍵字修飾,無(wú)方法體。 - 語(yǔ)法:
[修飾符列表] abstract 返回值類型 方法名(); - 包含抽象方法的類一定是抽象類。
- 使用
規(guī)則:
- 抽象類不一定有抽象方法,但抽象方法必須出現(xiàn)在抽象類中。
- 非抽象類繼承抽象類時(shí),必須實(shí)現(xiàn)所有抽象方法。
接口
定義:
- 使用
interface關(guān)鍵字定義,是完全抽象的(特殊的抽象類)。 - 屬于引用數(shù)據(jù)類型。
- 使用
語(yǔ)法:
[修飾符列表] interface 接口名 {}特點(diǎn):
- 接口中只能包含常量和抽象方法(默認(rèn)
public static final和public abstract,修飾符可省略)。 - 支持多繼承,一個(gè)接口可以繼承多個(gè)接口。
- 接口不能繼承抽象類。
- 接口中只能包含常量和抽象方法(默認(rèn)
方法類型:
- 抽象方法:
abstract修飾(可省略)。 - 默認(rèn)方法:
default修飾,提供默認(rèn)實(shí)現(xiàn)。 - 靜態(tài)方法:
static修飾,通過(guò)接口名調(diào)用。
- 抽象方法:
實(shí)現(xiàn):
- 類通過(guò)
implements關(guān)鍵字實(shí)現(xiàn)接口。 - 非抽象類實(shí)現(xiàn)接口時(shí),必須重寫所有抽象方法。
- 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。
- 類通過(guò)
多態(tài):
- 接口支持多態(tài):
父類型引用指向子類對(duì)象。 - 示例:
接口名 引用 = new 實(shí)現(xiàn)類();
- 接口支持多態(tài):
作用:
- 解耦合:調(diào)用者面向接口調(diào)用,實(shí)現(xiàn)者面向接口編寫實(shí)現(xiàn)。
- 擴(kuò)展性強(qiáng):接口+多態(tài)可以降低程序耦合度。
抽象類與接口的區(qū)別
| 特性 | 抽象類 | 接口 |
|---|---|---|
| 抽象程度 | 半抽象(可以包含具體方法) | 完全抽象(只能包含抽象方法) |
| 構(gòu)造方法 | 有構(gòu)造方法,供子類使用 | 無(wú)構(gòu)造方法 |
| 繼承 | 單繼承(一個(gè)類只能繼承一個(gè)抽象類) | 支持多繼承(一個(gè)類可以實(shí)現(xiàn)多個(gè)接口) |
| 內(nèi)容 | 可以包含抽象方法和非抽象方法 | 只能包含常量和抽象方法 |
| 用途 | 抽象行為和數(shù)據(jù) | 主要抽象行為 |
| 實(shí)例化 | 不能實(shí)例化 | 不能實(shí)例化 |
開發(fā)中的選擇
抽象類:
- 當(dāng)多個(gè)類有共同的屬性和行為,且需要部分具體實(shí)現(xiàn)時(shí)使用。
- 適合定義“是什么”(
is-a關(guān)系)。
接口:
- 當(dāng)需要定義一組行為規(guī)范,且不關(guān)心具體實(shí)現(xiàn)時(shí)使用。
- 適合定義“能做什么”(
like-a關(guān)系)。
示例
抽象類:
abstract class Animal { abstract void sound(); void sleep() { System.out.println("Sleeping..."); } }接口:
interface Flyable { void fly(); }實(shí)現(xiàn)與繼承:
class Bird extends Animal implements Flyable { @Override void sound() { System.out.println("Chirp..."); } @Override public void fly() { System.out.println("Flying..."); } }
總結(jié)
面向?qū)ο缶幊?/strong>通過(guò)封裝、繼承和多態(tài)三大特征,提高了代碼的復(fù)用性、擴(kuò)展性和維護(hù)性。
類與對(duì)象是面向?qū)ο缶幊痰幕A(chǔ),類是對(duì)對(duì)象的抽象,對(duì)象是類的實(shí)例。
封裝通過(guò)私有化屬性和提供操作入口,增強(qiáng)了代碼的安全性和可控性。
繼承實(shí)現(xiàn)了代碼的復(fù)用,并支持多態(tài)和方法覆蓋。
多態(tài)通過(guò)向上轉(zhuǎn)型和向下轉(zhuǎn)型,降低了程序的耦合度,提高了擴(kuò)展力。
面向抽象編程,而不是面向具體,可以進(jìn)一步降低耦合度,提高系統(tǒng)的靈活性和可擴(kuò)展性。
抽象類 用于定義類的共有特征,支持部分具體實(shí)現(xiàn)。
接口 用于定義行為規(guī)范,支持多繼承和解耦合。
在實(shí)際開發(fā)中,根據(jù)需求選擇抽象類或接口,合理使用可以提高代碼的擴(kuò)展性和可維護(hù)性。
七、類庫(kù)
源碼、字節(jié)碼與幫助文檔
源碼:
- 理解程序:源碼是程序員編寫的原始代碼,用于理解程序的邏輯和功能。
字節(jié)碼:
- 程序開發(fā)使用:字節(jié)碼是源碼編譯后的中間代碼,由JVM執(zhí)行。它是跨平臺(tái)的,可以在任何支持JVM的系統(tǒng)上運(yùn)行。
幫助文檔:
- 對(duì)開發(fā)提供幫助:幫助文檔是開發(fā)者的參考指南,通常通過(guò)
javadoc生成。 - 注意使用版本同一:確保使用的幫助文檔與代碼版本一致,避免因版本差異導(dǎo)致的錯(cuò)誤。
- 對(duì)開發(fā)提供幫助:幫助文檔是開發(fā)者的參考指南,通常通過(guò)
Object類(根類)
Object是Java中所有類的根類,提供了一些核心方法:
protected Object clone():- 負(fù)責(zé)對(duì)象克隆,返回對(duì)象的副本。
boolean equals(Object obj):- 判斷兩個(gè)對(duì)象是否相等。默認(rèn)比較引用地址,通常需要重寫以比較對(duì)象內(nèi)容。
int hashCode():- 返回對(duì)象的哈希代碼值,用于哈希表等數(shù)據(jù)結(jié)構(gòu)。
String toString():- 返回對(duì)象的字符串表示形式。默認(rèn)返回類名@哈希值,通常需要重寫以提供更有意義的信息。
protected void finalize() throws Throwable:- 垃圾回收器負(fù)責(zé)調(diào)用,用于對(duì)象銷毀前的清理工作。
System.gc():- 建議啟動(dòng)垃圾回收器,但不保證立即執(zhí)行。
System類
System類提供了一些系統(tǒng)級(jí)別的操作:
System.gc():- 建議啟動(dòng)垃圾回收器。
System.out:- 靜態(tài)變量,用于控制臺(tái)輸出。
System.out.print():- 輸出打印不換行。
System.out.println():- 換行輸出。
System.currentTimeMillis():- 獲取自1970年1月1日00:00:00到當(dāng)前系統(tǒng)時(shí)間的總毫秒數(shù)。
System.exit(0):- 退出JVM。
Arrays類
Arrays是數(shù)組工具類,提供了一些常用方法:
Arrays.sort(arr):- 對(duì)數(shù)組進(jìn)行排序。
Arrays.binarySearch(arr, key):- 使用二分法查找元素,不存在時(shí)返回-1。
String類
String類用于操作字符串,提供了豐富的構(gòu)造方法和方法:
構(gòu)造方法:
String(byte[] byte):將字節(jié)數(shù)組轉(zhuǎn)換為字符串。String(char[] char):將字符數(shù)組轉(zhuǎn)換為字符串。String(String string):復(fù)制字符串。
常用方法:
char charAt(int index):返回指定索引的字符。int compareTo(String string):字典比較大小。boolean contains(String string):判斷是否包含指定字符串。boolean endsWith(String string):判斷是否以指定字符串結(jié)尾。boolean startsWith(String prefix):判斷是否以指定前綴開頭。boolean equals(Object anObject):比較字符串內(nèi)容。boolean equalsIgnoreCase(String anotherString):忽略大小寫比較。byte[] getBytes():將字符串轉(zhuǎn)換為字節(jié)數(shù)組。int indexOf(String str):返回子字符串第一次出現(xiàn)的索引。int lastIndexOf(String str):返回子字符串最后一次出現(xiàn)的索引。boolean isEmpty():判斷字符串是否為空。String replace(CharSequence target, CharSequence replacement):替換字符串。String substring(int beginIndex):截取字符串。char[] toCharArray():將字符串轉(zhuǎn)換為字符數(shù)組。String toLowerCase():將字符串轉(zhuǎn)換為小寫。String toUpperCase():將字符串轉(zhuǎn)換為大寫。String[] split(String regex):按正則表達(dá)式拆分字符串。String trim():去除前后空白。static String valueOf():將其他類型轉(zhuǎn)換為字符串。
StringBuffer與StringBuilder
StringBuffer:
- 線程安全,適用于多線程環(huán)境。
- 常用方法:
append()、reverse()。
StringBuilder:
- 非線程安全,性能優(yōu)于
StringBuffer。
- 非線程安全,性能優(yōu)于
包裝類
包裝類用于將基本數(shù)據(jù)類型轉(zhuǎn)換為對(duì)象:
- 常用包裝類:
Integer、Character等。
- 常用方法:
int intValue():拆箱,將包裝類轉(zhuǎn)換為基本類型。static int parseInt(String s):將字符串轉(zhuǎn)換為整數(shù)。

日期相關(guān)類
java.util.Date:- 表示日期和時(shí)間。
SimpleDateFormat:- 用于格式化日期。
- 常用方法:
format()、parse()。
數(shù)字相關(guān)類
DecimalFormat:- 用于格式化數(shù)字。
BigDecimal:- 用于高精度計(jì)算,適用于財(cái)務(wù)數(shù)據(jù)。
Random:- 用于生成隨機(jī)數(shù)。
枚舉(Enum)
枚舉是一種特殊的類,用于定義一組常量:
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
內(nèi)部類
成員內(nèi)部類:
- 定義在類中,可以訪問(wèn)外部類的所有成員。
局部?jī)?nèi)部類:
- 定義在方法中,只能在該方法內(nèi)訪問(wèn)。
靜態(tài)內(nèi)部類:
- 使用
static修飾,只能訪問(wèn)外部類的靜態(tài)成員。
- 使用
匿名內(nèi)部類:
- 沒(méi)有名稱的內(nèi)部類,通常用于實(shí)現(xiàn)接口或抽象類。
總結(jié)
- 源碼是理解程序的基礎(chǔ),字節(jié)碼是程序運(yùn)行的關(guān)鍵,幫助文檔是開發(fā)的指南。
- Object是Java的根類,提供了對(duì)象的基本操作。
- System類提供了系統(tǒng)級(jí)別的操作,如垃圾回收、時(shí)間獲取等。
- String類用于操作字符串,提供了豐富的構(gòu)造方法和方法。
- StringBuffer和StringBuilder用于字符串的拼接和修改,前者線程安全,后者性能更優(yōu)。
- 包裝類用于將基本數(shù)據(jù)類型轉(zhuǎn)換為對(duì)象。
- 日期相關(guān)類用于處理日期和時(shí)間。
- 內(nèi)部類提供了更靈活的代碼組織方式。
八、數(shù)組
一維數(shù)組
定義:
- 數(shù)組是引用數(shù)據(jù)類型,存儲(chǔ)在堆內(nèi)存中。
- 可以存儲(chǔ)各種數(shù)據(jù)類型,但不能直接存儲(chǔ)對(duì)象,存儲(chǔ)的是對(duì)象的引用(內(nèi)存地址)。
特點(diǎn):
- 數(shù)組元素類型統(tǒng)一,最后一個(gè)下標(biāo)為
length - 1。 - 帶有
length屬性,用于獲取數(shù)組長(zhǎng)度。
- 數(shù)組元素類型統(tǒng)一,最后一個(gè)下標(biāo)為
優(yōu)點(diǎn):
- 查詢、查找、檢索某個(gè)下標(biāo)元素效率極高(內(nèi)存連續(xù),類型相同)。
缺點(diǎn):
- 隨機(jī)增刪元素效率較低。
- 不能存儲(chǔ)大數(shù)據(jù)量。
定義與初始化:
靜態(tài)初始化:
數(shù)據(jù)類型[] 數(shù)組名 = {元素1, 元素2, ...};動(dòng)態(tài)初始化:
數(shù)據(jù)類型[] 數(shù)組名 = new 數(shù)據(jù)類型[長(zhǎng)度];
賦值:
數(shù)組名[下標(biāo)] = 值;
遍歷:
使用
for循環(huán)或增強(qiáng)for循環(huán):for (int i = 0; i < 數(shù)組名.length; i++) { System.out.println(數(shù)組名[i]); }
方法參數(shù):
數(shù)組可以作為方法的參數(shù):
void 方法名(數(shù)據(jù)類型[] 數(shù)組名) {}
main 方法的數(shù)組參數(shù):
main方法的參數(shù)是一個(gè)字符串?dāng)?shù)組,用于接收命令行參數(shù):public static void main(String[] args) {}
存儲(chǔ)對(duì)象:
數(shù)組可以存儲(chǔ)對(duì)象的引用:
類名[] 數(shù)組名 = new 類名[長(zhǎng)度]; 數(shù)組名[0] = new 類名();
數(shù)組擴(kuò)容:
新建一個(gè)大數(shù)組,然后將原數(shù)組拷貝過(guò)去:
int[] newArray = new int[原數(shù)組.length * 2]; System.arraycopy(原數(shù)組, 0, newArray, 0, 原數(shù)組.length);
數(shù)組拷貝:
使用
System.arraycopy方法:System.arraycopy(原數(shù)組, 原起點(diǎn), 目標(biāo)數(shù)組, 目標(biāo)下標(biāo), 長(zhǎng)度);
二維數(shù)組
定義:
- 二維數(shù)組是數(shù)組的數(shù)組,可以看作是一個(gè)表格。
語(yǔ)法:
數(shù)據(jù)類型[][] 數(shù)組名 = new 數(shù)據(jù)類型[行數(shù)][列數(shù)];
初始化:
靜態(tài)初始化:
數(shù)據(jù)類型[][] 數(shù)組名 = {{元素1, 元素2}, {元素3, 元素4}};動(dòng)態(tài)初始化:
數(shù)據(jù)類型[][] 數(shù)組名 = new 數(shù)據(jù)類型[行數(shù)][列數(shù)];
遍歷:
使用嵌套
for循環(huán):for (int i = 0; i < 數(shù)組名.length; i++) { for (int j = 0; j < 數(shù)組名[i].length; j++) { System.out.println(數(shù)組名[i][j]); } }
總結(jié)
一維數(shù)組:
- 適用于存儲(chǔ)一組相同類型的數(shù)據(jù)。
- 查詢效率高,增刪效率低。
- 可以通過(guò)
length屬性獲取長(zhǎng)度。 - 支持靜態(tài)初始化和動(dòng)態(tài)初始化。
二維數(shù)組:
- 適用于存儲(chǔ)表格型數(shù)據(jù)。
- 可以看作是一維數(shù)組的數(shù)組。
- 支持靜態(tài)初始化和動(dòng)態(tài)初始化。
數(shù)組的優(yōu)缺點(diǎn):
- 優(yōu)點(diǎn):查詢效率高,內(nèi)存連續(xù)。
- 缺點(diǎn):增刪效率低,不能存儲(chǔ)大數(shù)據(jù)量。
數(shù)組的應(yīng)用場(chǎng)景:
- 存儲(chǔ)一組固定長(zhǎng)度的數(shù)據(jù)。
- 存儲(chǔ)對(duì)象引用。
- 存儲(chǔ)表格型數(shù)據(jù)(二維數(shù)組)。
示例
一維數(shù)組:
int[] arr = {1, 2, 3, 4, 5}; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); }二維數(shù)組:
int[][] arr = {{1, 2}, {3, 4}}; for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { System.out.println(arr[i][j]); } }數(shù)組存儲(chǔ)對(duì)象:
Animal[] animals = new Animal[2]; animals[0] = new Cat(); animals[1] = new Dog();
數(shù)組擴(kuò)容:
int[] src = {1, 2, 3}; int[] dest = new int[src.length * 2]; System.arraycopy(src, 0, dest, 0, src.length);
通過(guò)合理使用數(shù)組,可以高效地存儲(chǔ)和操作數(shù)據(jù),但需要注意其增刪效率較低的缺點(diǎn)。
九、算法
以下是常見(jiàn) 排序算法 和 查找算法 的思想總結(jié),并附帶 Java 實(shí)例:
排序算法
冒泡排序(Bubble Sort):
思想:重復(fù)遍歷數(shù)組,每次比較相鄰元素,如果順序錯(cuò)誤則交換,直到?jīng)]有需要交換的元素。
時(shí)間復(fù)雜度:O(n²)。
Java 實(shí)現(xiàn):
public static void bubbleSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }
選擇排序(Selection Sort):
思想:每次從未排序部分選擇最小元素,放到已排序部分的末尾。
時(shí)間復(fù)雜度:O(n²)。
Java 實(shí)現(xiàn):
public static void selectionSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { int minIndex = i; for (int j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } int temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } }
插入排序(Insertion Sort):
思想:將未排序部分的元素逐個(gè)插入到已排序部分的正確位置。
時(shí)間復(fù)雜度:O(n²)。
Java 實(shí)現(xiàn):
public static void insertionSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int key = arr[i]; int j = i - 1; while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = key; } }
快速排序(Quick Sort):
思想:選擇一個(gè)基準(zhǔn)元素,將數(shù)組分為兩部分,左邊小于基準(zhǔn),右邊大于基準(zhǔn),遞歸排序。
時(shí)間復(fù)雜度:O(n log n)。
Java 實(shí)現(xiàn):
public static void quickSort(int[] arr, int low, int high) { if (low < high) { int pivot = partition(arr, low, high); quickSort(arr, low, pivot - 1); quickSort(arr, pivot + 1, high); } } private static int partition(int[] arr, int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] < pivot) { i++; int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } int temp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp; return i + 1; }
歸并排序(Merge Sort):
思想:將數(shù)組分成兩半,分別排序,然后合并。
時(shí)間復(fù)雜度:O(n log n)。
Java 實(shí)現(xiàn):
public static void mergeSort(int[] arr, int left, int right) { if (left < right) { int mid = (left + right) / 2; mergeSort(arr, left, mid); mergeSort(arr, mid + 1, right); merge(arr, left, mid, right); } } private static void merge(int[] arr, int left, int mid, int right) { int[] temp = new int[right - left + 1]; int i = left, j = mid + 1, k = 0; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } while (i <= mid) { temp[k++] = arr[i++]; } while (j <= right) { temp[k++] = arr[j++]; } for (int p = 0; p < temp.length; p++) { arr[left + p] = temp[p]; } }
查找算法
線性查找(Linear Search):
思想:從頭到尾遍歷數(shù)組,逐個(gè)比較,找到目標(biāo)元素。
時(shí)間復(fù)雜度:O(n)。
Java 實(shí)現(xiàn):
public static int linearSearch(int[] arr, int target) { for (int i = 0; i < arr.length; i++) { if (arr[i] == target) { return i; } } return -1; }
二分查找(Binary Search):
思想:在有序數(shù)組中,每次取中間元素與目標(biāo)比較,縮小查找范圍。
時(shí)間復(fù)雜度:O(log n)。
Java 實(shí)現(xiàn):
public static int binarySearch(int[] arr, int target) { int left = 0, right = arr.length - 1; while (left <= right) { int mid = (left + right) / 2; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; }
總結(jié)
排序算法:
- 冒泡排序:簡(jiǎn)單但效率低,適合小規(guī)模數(shù)據(jù)。
- 選擇排序:每次選擇最小元素,適合小規(guī)模數(shù)據(jù)。
- 插入排序:適合部分有序的數(shù)據(jù)。
- 快速排序:高效,適合大規(guī)模數(shù)據(jù)。
- 歸并排序:穩(wěn)定且高效,適合大規(guī)模數(shù)據(jù)。
查找算法:
- 線性查找:適合無(wú)序數(shù)據(jù)。
- 二分查找:適合有序數(shù)據(jù),效率高。
選擇依據(jù):
- 數(shù)據(jù)規(guī)模、是否有序、穩(wěn)定性要求等。
示例
public class Main {
public static void main(String[] args) {
int[] arr = {5, 3, 8, 4, 2};
bubbleSort(arr);
System.out.println("冒泡排序結(jié)果: " + Arrays.toString(arr));
int[] arr2 = {5, 3, 8, 4, 2};
quickSort(arr2, 0, arr2.length - 1);
System.out.println("快速排序結(jié)果: " + Arrays.toString(arr2));
int target = 4;
int index = binarySearch(arr2, target);
System.out.println("二分查找結(jié)果: " + (index != -1 ? "找到,下標(biāo)為 " + index : "未找到"));
}
}
輸出:
冒泡排序結(jié)果: [2, 3, 4, 5, 8] 快速排序結(jié)果: [2, 3, 4, 5, 8] 二分查找結(jié)果: 找到,下標(biāo)為 2
通過(guò)合理選擇排序和查找算法,可以高效地處理數(shù)據(jù)。
十、異常
1. 異常的基本概念
- 異常在 Java 中以類的方式存在,每個(gè)異常類都可以創(chuàng)建異常對(duì)象。
- 方法覆蓋規(guī)則:子類重寫父類方法時(shí),不能拋出比父類方法更高的異常(運(yùn)行時(shí)異常
RuntimeException除外)。 - 異常的分類:
java.lang.Throwable:異常的父類,有兩個(gè)子類:Error:錯(cuò)誤,通常是系統(tǒng)級(jí)錯(cuò)誤(如OutOfMemoryError),不可處理,只能退出程序。Exception:異常,所有異常都是在運(yùn)行階段發(fā)生的。Exception的直接子類:編譯時(shí)異常(受檢異常CheckedException),需要在編寫程序時(shí)預(yù)處理。RuntimeException:運(yùn)行時(shí)異常,通常由程序邏輯錯(cuò)誤引起,不需要顯式處理。
2. 常見(jiàn)運(yùn)行時(shí)異常
NullPointerException:空指針異常,嘗試訪問(wèn)null對(duì)象的成員。ArrayIndexOutOfBoundsException:數(shù)組下標(biāo)越界異常。ClassCastException:類型轉(zhuǎn)換異常,嘗試將對(duì)象強(qiáng)制轉(zhuǎn)換為不兼容的類型。NumberFormatException:數(shù)字轉(zhuǎn)換異常,嘗試將非數(shù)字字符串轉(zhuǎn)換為數(shù)字。
3. 異常處理方式
throws關(guān)鍵字:在方法聲明位置使用,將異常拋給調(diào)用者處理。
示例:
public void readFile() throws IOException { // 可能拋出 IOException 的代碼 }
try-catch-finally語(yǔ)句:捕獲并處理異常。
示例:
try { // 可能拋出異常的代碼 } catch (NullPointerException e) { System.out.println("空指針異常: " + e.getMessage()); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("數(shù)組下標(biāo)越界: " + e.getMessage()); } finally { // 無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行的代碼 System.out.println("finally 塊執(zhí)行"); }
4. 常用異常方法
getMessage():獲取異常的簡(jiǎn)單描述信息(通常是構(gòu)造方法的參數(shù))。printStackTrace():打印異常的堆棧追蹤信息(異步線程中常用)。
5. 自定義異常
步驟:
- 編寫一個(gè)類繼承
Exception(受檢異常)或RuntimeException(運(yùn)行時(shí)異常)。 - 提供兩個(gè)構(gòu)造方法:一個(gè)無(wú)參,一個(gè)有參。
- 使用
throw手動(dòng)拋出異常。
- 編寫一個(gè)類繼承
示例:
// 自定義異常類 public class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } } // 使用自定義異常 public class Test { public static void main(String[] args) { try { throw new MyException("自定義異常發(fā)生"); } catch (MyException e) { System.out.println(e.getMessage()); } } }
6. 異常處理的最佳實(shí)踐
- 明確異常類型:捕獲具體異常,而不是直接捕獲
Exception。 - 合理使用
finally:用于釋放資源(如關(guān)閉文件、數(shù)據(jù)庫(kù)連接等)。 - 避免空指針異常:在使用對(duì)象前進(jìn)行
null檢查。 - 日志記錄:使用日志框架(如
Log4j或SLF4J)記錄異常信息,便于排查問(wèn)題。
總結(jié)
- 異常分類:
Error和Exception,其中Exception分為編譯時(shí)異常和運(yùn)行時(shí)異常。 - 處理方式:
throws拋給調(diào)用者,try-catch-finally捕獲并處理。 - 自定義異常:繼承
Exception或RuntimeException,提供構(gòu)造方法,使用throw拋出。 - 最佳實(shí)踐:明確異常類型,合理使用
finally,避免空指針異常,記錄日志。
十一、I/O
I/O(輸入/輸出)概述
I/O(Input/Output)是指應(yīng)用程序與外部設(shè)備(如磁盤、網(wǎng)絡(luò)、鍵盤、顯示器等)之間的數(shù)據(jù)交互。Java通過(guò)java.io包提供了豐富的I/O類庫(kù),支持文件操作、字節(jié)流、字符流等功能。
File類
File類是java.io包中唯一代表磁盤文件本身的對(duì)象,用于操作文件和目錄。
構(gòu)造方法
File(String path):- 根據(jù)路徑創(chuàng)建
File對(duì)象。
- 根據(jù)路徑創(chuàng)建
File(String parent, String child):- 根據(jù)父路徑和子路徑(包括文件名)創(chuàng)建
File對(duì)象。
- 根據(jù)父路徑和子路徑(包括文件名)創(chuàng)建
File(File parent, String child):- 根據(jù)
File對(duì)象表示的父路徑和子路徑創(chuàng)建File對(duì)象。
- 根據(jù)
注意:路徑分隔符可以使用\\(Windows)或/(Unix/Linux)。
常用方法
boolean exists():- 判斷文件或目錄是否存在。
boolean delete():- 刪除文件或目錄。
boolean createNewFile():- 如果文件不存在,則創(chuàng)建一個(gè)新文件。
String getName():- 返回文件或目錄的名稱。
String getPath():- 返回文件或目錄的路徑。
String getAbsolutePath():- 返回文件或目錄的絕對(duì)路徑。
boolean canRead():- 判斷文件是否可讀。
boolean canWrite():- 判斷文件是否可寫。
boolean isFile():- 判斷是否為文件。
boolean isDirectory():- 判斷是否為目錄。
long length():- 返回文件內(nèi)容的長(zhǎng)度(字節(jié)數(shù))。
String[] list():- 返回目錄內(nèi)所有文件和子目錄的名稱。
File[] listFiles():- 返回目錄內(nèi)所有文件和子目錄的
File對(duì)象。
- 返回目錄內(nèi)所有文件和子目錄的
createTempFile(String prefix, String suffix):- 創(chuàng)建臨時(shí)文件。
deleteOnExit():- JVM退出時(shí)自動(dòng)刪除文件。
字節(jié)流
字節(jié)流用于處理二進(jìn)制數(shù)據(jù)(如圖片、音頻、視頻等),以字節(jié)為單位進(jìn)行讀寫操作。

字節(jié)輸入流(InputStream)
InputStream是字節(jié)輸入流的抽象類,用于從源(如文件、網(wǎng)絡(luò)等)讀取數(shù)據(jù)。
常用方法:
int read():- 逐個(gè)字節(jié)讀取,返回讀取的字節(jié)值(0-255),如果到達(dá)流末尾則返回-1。
int read(byte[] b):- 將數(shù)據(jù)讀取到字節(jié)數(shù)組
b中,返回實(shí)際讀取的字節(jié)數(shù)。
- 將數(shù)據(jù)讀取到字節(jié)數(shù)組
int read(byte[] b, int off, int len):- 從偏移量
off開始,讀取len個(gè)字節(jié)到數(shù)組b中,返回實(shí)際讀取的字節(jié)數(shù)。
- 從偏移量
void close():- 關(guān)閉流,釋放資源。
字節(jié)輸出流(OutputStream)
OutputStream是字節(jié)輸出流的抽象類,用于將數(shù)據(jù)寫入目標(biāo)(如文件、網(wǎng)絡(luò)等)。
常用方法:
void write(int b):- 逐個(gè)字節(jié)寫入。
void write(byte[] b):- 將字節(jié)數(shù)組
b中的數(shù)據(jù)寫入。
- 將字節(jié)數(shù)組
void write(byte[] b, int off, int len):- 從偏移量
off開始,寫入len個(gè)字節(jié)。
- 從偏移量
void flush():- 強(qiáng)制將緩沖區(qū)中的數(shù)據(jù)寫入目標(biāo)。
void close():- 關(guān)閉流,釋放資源。
具體實(shí)現(xiàn)類
FileInputStream:- 用于從文件中讀取字節(jié)數(shù)據(jù)。
FileOutputStream:- 用于將字節(jié)數(shù)據(jù)寫入文件。
拓展總結(jié)
文件操作:
- 使用
File類可以創(chuàng)建、刪除、重命名文件,判斷文件是否存在,查詢文件屬性等。
- 使用
字節(jié)流:
- 字節(jié)流適用于處理二進(jìn)制數(shù)據(jù),
InputStream和OutputStream是字節(jié)流的抽象基類。 FileInputStream和FileOutputStream是常用的字節(jié)流實(shí)現(xiàn)類,用于文件的讀寫操作。
- 字節(jié)流適用于處理二進(jìn)制數(shù)據(jù),
流的使用注意事項(xiàng):
- 使用流時(shí),務(wù)必在操作完成后調(diào)用
close()方法關(guān)閉流,釋放系統(tǒng)資源。 - 對(duì)于輸出流,可以調(diào)用
flush()方法強(qiáng)制將緩沖區(qū)中的數(shù)據(jù)寫入目標(biāo)。
- 使用流時(shí),務(wù)必在操作完成后調(diào)用
臨時(shí)文件:
- 使用
createTempFile()方法可以創(chuàng)建臨時(shí)文件,deleteOnExit()方法可以確保JVM退出時(shí)自動(dòng)刪除臨時(shí)文件。
- 使用
路徑處理:
- 路徑分隔符可以使用
\\(Windows)或/(Unix/Linux),Java會(huì)自動(dòng)處理。
- 路徑分隔符可以使用
性能優(yōu)化:
- 對(duì)于大文件的讀寫,建議使用緩沖區(qū)(如
BufferedInputStream和BufferedOutputStream)來(lái)提高性能。
- 對(duì)于大文件的讀寫,建議使用緩沖區(qū)(如
示例代碼
文件操作
File file = new File("test.txt");
if (!file.exists()) {
file.createNewFile(); // 創(chuàng)建文件
}
System.out.println("文件名稱: " + file.getName());
System.out.println("文件路徑: " + file.getAbsolutePath());
file.delete(); // 刪除文件
字節(jié)流讀寫
// 寫入文件
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
fos.write("Hello, World!".getBytes());
fos.flush();
}
// 讀取文件
try (FileInputStream fis = new FileInputStream("output.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
}
通過(guò)掌握這些核心概念和類庫(kù),可以高效地處理文件操作和字節(jié)流讀寫。
字符流總結(jié)
字符流是Java I/O中用于處理文本數(shù)據(jù)的流,它以字符為單位進(jìn)行讀寫操作。與字節(jié)流不同,字符流專門用于處理字符數(shù)據(jù)(如文本文件),并且支持字符編碼(如UTF-8、GBK等),能夠正確處理多字節(jié)字符。
字符流概述
字符流的核心類是Reader和Writer,它們分別是字符輸入流和字符輸出流的抽象基類。字符流的主要特點(diǎn)包括:
- 以字符為單位:
- 字符流以字符為單位讀寫數(shù)據(jù),適合處理文本文件。
- 支持字符編碼:
- 字符流可以正確處理字符編碼,避免亂碼問(wèn)題。
- 高效讀寫:
- 字符流通常與緩沖區(qū)結(jié)合使用(如
BufferedReader和BufferedWriter),提高讀寫效率。
- 字符流通常與緩沖區(qū)結(jié)合使用(如

字符輸入流(Reader)
Reader是字符輸入流的抽象類,用于從源(如文件、字符串等)讀取字符數(shù)據(jù)。
常用方法
int read():- 讀取單個(gè)字符,返回字符的Unicode值(0-65535),如果到達(dá)流末尾則返回-1。
int read(char[] cbuf):- 將字符數(shù)據(jù)讀取到字符數(shù)組
cbuf中,返回實(shí)際讀取的字符數(shù)。
- 將字符數(shù)據(jù)讀取到字符數(shù)組
int read(char[] cbuf, int off, int len):- 從偏移量
off開始,讀取len個(gè)字符到數(shù)組cbuf中,返回實(shí)際讀取的字符數(shù)。
- 從偏移量
void close():- 關(guān)閉流,釋放資源。
具體實(shí)現(xiàn)類
FileReader:- 用于從文件中讀取字符數(shù)據(jù)。
BufferedReader:- 帶有緩沖區(qū)的字符輸入流,提供
readLine()方法逐行讀取文本。
- 帶有緩沖區(qū)的字符輸入流,提供
InputStreamReader:- 將字節(jié)流轉(zhuǎn)換為字符流,支持指定字符編碼。
字符輸出流(Writer)
Writer是字符輸出流的抽象類,用于將字符數(shù)據(jù)寫入目標(biāo)(如文件、控制臺(tái)等)。
常用方法
void write(int c):- 寫入單個(gè)字符。
void write(char[] cbuf):- 寫入字符數(shù)組
cbuf中的數(shù)據(jù)。
- 寫入字符數(shù)組
void write(char[] cbuf, int off, int len):- 從偏移量
off開始,寫入len個(gè)字符。
- 從偏移量
void write(String str):- 寫入字符串
str。
- 寫入字符串
void write(String str, int off, int len):- 從偏移量
off開始,寫入len個(gè)字符。
- 從偏移量
void flush():- 強(qiáng)制將緩沖區(qū)中的數(shù)據(jù)寫入目標(biāo)。
void close():- 關(guān)閉流,釋放資源。
具體實(shí)現(xiàn)類
FileWriter:- 用于將字符數(shù)據(jù)寫入文件。
BufferedWriter:- 帶有緩沖區(qū)的字符輸出流,提供
newLine()方法寫入換行符。
- 帶有緩沖區(qū)的字符輸出流,提供
OutputStreamWriter:- 將字節(jié)流轉(zhuǎn)換為字符流,支持指定字符編碼。
字符流與字節(jié)流的區(qū)別
單位不同:
- 字節(jié)流以字節(jié)為單位,適合處理二進(jìn)制數(shù)據(jù)。
- 字符流以字符為單位,適合處理文本數(shù)據(jù)。
編碼支持:
- 字節(jié)流不涉及字符編碼,直接處理字節(jié)數(shù)據(jù)。
- 字符流支持字符編碼,能夠正確處理多字節(jié)字符。
性能優(yōu)化:
- 字符流通常與緩沖區(qū)結(jié)合使用,提高讀寫效率。
示例代碼
字符流讀寫文件
// 寫入文件
try (FileWriter fw = new FileWriter("output.txt");
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write("Hello, World!");
bw.newLine(); // 寫入換行符
bw.write("This is a test.");
}
// 讀取文件
try (FileReader fr = new FileReader("output.txt");
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
使用指定編碼讀寫文件
// 寫入文件(指定編碼為UTF-8)
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8");
BufferedWriter bw = new BufferedWriter(osw)) {
bw.write("你好,世界!");
}
// 讀取文件(指定編碼為UTF-8)
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("output.txt"), "UTF-8");
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
總結(jié)
字符流適用場(chǎng)景:
- 處理文本文件、字符串等字符數(shù)據(jù)。
核心類:
Reader和Writer是字符流的抽象基類。FileReader、BufferedReader、FileWriter、BufferedWriter是常用的實(shí)現(xiàn)類。
字符編碼:
- 使用
InputStreamReader和OutputStreamWriter可以指定字符編碼,避免亂碼問(wèn)題。
- 使用
性能優(yōu)化:
- 使用
BufferedReader和BufferedWriter可以提高讀寫效率。
- 使用
流關(guān)閉:
- 使用
try-with-resources語(yǔ)法確保流被正確關(guān)閉,釋放資源。
- 使用
通過(guò)掌握字符流的核心概念和類庫(kù),可以高效地處理文本數(shù)據(jù)的讀寫操作。
十二、集合
集合是Java中用于存儲(chǔ)和管理一組對(duì)象的容器。它提供了一種更靈活、更高效的方式來(lái)操作數(shù)據(jù)集合。以下是集合的核心概念和總結(jié):
集合的特點(diǎn)
容器性質(zhì):
- 集合是一個(gè)容器,可以容納其他類型的數(shù)據(jù)。
- 集合不能直接存儲(chǔ)基本數(shù)據(jù)類型(如
int、char等),也不能直接存儲(chǔ)對(duì)象,存儲(chǔ)的是Java對(duì)象的內(nèi)存地址(引用)。
數(shù)據(jù)結(jié)構(gòu):
- 不同的集合對(duì)應(yīng)不同的數(shù)據(jù)結(jié)構(gòu)(如數(shù)組、鏈表、哈希表、二叉樹等)。
- 使用不同的集合等同于使用了不同的數(shù)據(jù)結(jié)構(gòu)。
包位置:
- 所有的集合類都位于
java.util包中。
- 所有的集合類都位于
集合的層次結(jié)構(gòu)
超級(jí)父接口:
Iterable<T>:- 所有集合都是可迭代的,即可以通過(guò)迭代器遍歷集合中的元素。
- 方法:
Iterator<T> iterator():返回集合的迭代器。
單個(gè)元素集合的父接口:
Collection<E>:- 表示存儲(chǔ)單個(gè)元素的集合的超級(jí)接口。
- 子接口包括:
List、Set、Queue等。
鍵值對(duì)集合的父接口:
Map<K,V>:- 表示存儲(chǔ)鍵值對(duì)的集合,獨(dú)立于
Collection體系。
- 表示存儲(chǔ)鍵值對(duì)的集合,獨(dú)立于
集合的實(shí)現(xiàn)類總結(jié)
1.List接口的實(shí)現(xiàn)類:
ArrayList:- 底層是數(shù)組,查詢快,增刪慢。
- 非線程安全。
LinkedList:- 底層是雙向鏈表,增刪快,查詢慢。
- 非線程安全。
Vector:- 底層是數(shù)組,線程安全,但效率較低,使用較少。
2.Set接口的實(shí)現(xiàn)類:
HashSet:- 底層是
HashMap,元素存儲(chǔ)在HashMap的key部分。 - 無(wú)序且不允許重復(fù)。
- 底層是
TreeSet:- 底層是
TreeMap,元素存儲(chǔ)在TreeMap的key部分。 - 元素自動(dòng)按大小順序排序。
- 底層是
3.Map接口的實(shí)現(xiàn)類:
HashMap:- 底層是哈希表,非線程安全。
- 允許
null鍵和null值。
Hashtable:- 底層是哈希表,線程安全,但效率較低,使用較少。
- 不允許
null鍵和null值。
Properties:- 底層是哈希表,線程安全。
key和value只能存儲(chǔ)字符串(String)。
TreeMap:- 底層是二叉樹。
key自動(dòng)按照大小順序排序。
集合的選擇
需要存儲(chǔ)單個(gè)元素:
- 如果需要有序且允許重復(fù),使用
List:- 查詢多,增刪少:
ArrayList。 - 增刪多,查詢少:
LinkedList。
- 查詢多,增刪少:
- 如果不需要重復(fù)元素,使用
Set:- 無(wú)序:
HashSet。 - 有序:
TreeSet。
- 無(wú)序:
- 如果需要有序且允許重復(fù),使用
需要存儲(chǔ)鍵值對(duì):
- 非線程安全:
HashMap。 - 線程安全:
Hashtable或Properties。 - 需要排序:
TreeMap。
- 非線程安全:
線程安全:
- 如果需要線程安全,可以使用
Vector、Hashtable或Properties,但效率較低。 - 推薦使用
Collections.synchronizedList()或ConcurrentHashMap等并發(fā)集合。
- 如果需要線程安全,可以使用
總結(jié)
集合的核心:
- 集合是存儲(chǔ)和管理一組對(duì)象的容器,存儲(chǔ)的是對(duì)象的內(nèi)存地址。
- 不同的集合對(duì)應(yīng)不同的數(shù)據(jù)結(jié)構(gòu),選擇合適的集合可以提高程序效率。
常用集合:
List:有序且允許重復(fù),常用ArrayList和LinkedList。Set:無(wú)序且不允許重復(fù),常用HashSet和TreeSet。Map:存儲(chǔ)鍵值對(duì),常用HashMap、TreeMap和Properties。
線程安全:
- 線程安全的集合有
Vector、Hashtable和Properties,但效率較低。 - 推薦使用并發(fā)集合(如
ConcurrentHashMap)來(lái)實(shí)現(xiàn)線程安全。
- 線程安全的集合有
通過(guò)掌握集合的核心概念和常用實(shí)現(xiàn)類,可以更高效地處理數(shù)據(jù)集合,并根據(jù)需求選擇合適的集合類型。
List 集合存儲(chǔ)元素的特點(diǎn):
有序可重復(fù)
有序:存進(jìn)去的順序和取出的順序相同,每一個(gè)元素都有下標(biāo)
可重復(fù):存進(jìn)去1,可以再存儲(chǔ)一個(gè)1
Set 集合存儲(chǔ)元素的特點(diǎn)(Map的Key):
無(wú)序不可重復(fù)
無(wú)序:存進(jìn)去的順序和取出的順序不一定相同,另外 Set 集合中元素沒(méi)有下標(biāo)(哈希表的存儲(chǔ))
不可重復(fù):存進(jìn)去1,不能再存儲(chǔ)1了(哈希表的覆蓋)
SortedSet( SortedMap )集合存儲(chǔ)元素特點(diǎn):
首先是無(wú)序不可重復(fù)的,但是 SortedSet 集合中的元素是可排序的
無(wú)序:存進(jìn)去的順序和取出的順序不一定相同,另外 Set 集合中元素沒(méi)有下標(biāo)
不可重復(fù):存進(jìn)去1,不能再存儲(chǔ)1了
可排序:可以按照大小順序排列。
Map 集合的 key ,就是一個(gè) Set 集合。
往 Set 集合中放數(shù)據(jù),實(shí)際上放到了 Map 集合的 key 部分。
InterfaceCollection
沒(méi)有使用泛型前可以存儲(chǔ)Object的所有子類型
- Boolean add(E e) 添加元素
- Object[] toArray() 轉(zhuǎn)化成數(shù)組(使用不多)
- Int size() 返回此集合中元素的數(shù)目。
- Boolean contains(Object o) 如果此集合包含指定的元素(存放在集合中的類型,需要重寫equals方法)
- Void clear() 從此集合中刪除所有元素
- Boolean equals(Object o) 將指定的對(duì)象與此集合進(jìn)行比較以實(shí)現(xiàn)相等性(內(nèi)存地址)
- Boolean remove(Object o) 從此集合中刪除指定元素的單個(gè)實(shí)例
- Boolean isEmpty() 如果此集合不包含任何元素(判空)則返回。true
Iterator<E> iterator() ***:**不管存進(jìn)去什么,拿出來(lái)都是Object,取出來(lái)還是原類型
返回此集合中元素的迭代器**,Collection通用,Map集合不能用**
只要集合結(jié)構(gòu)發(fā)生改變迭代器一定要重新獲取
- default void forEachRemaining(Consumer<? super E> action) 對(duì)每個(gè)剩余元素執(zhí)行給定的操作,直到所有元素都已處理完畢或該操作引發(fā)異常。
- Boolean hasNext() 如果迭代具有更多元素,則返回。true
- Object next() 返回迭代中的下一個(gè)元素。(返回object)
- default void remove() 從基礎(chǔ)集合中刪除此迭代器返回的最后一個(gè)元素(可選操作)。
Interface List有序可重復(fù),Collection子接口
- void add(int index, E element) 在此列表中的指定位置插入指定的元素
- E get(int index) 返回此列表中指定位置處的元素
- E set(int index, E element) 將此列表中指定位置的元素替換為指定的元素
- int indexOf(Object o) 返回此列表中指定元素的第一次出現(xiàn)的索引,如果此列表不包含該元素,則返回 -1
- int lastIndexOf(Object o) 返回此列表中指定元素的最后一次出現(xiàn)的索引,如果此列表不包含該元素,則返回 -1。
- E remove(int index) 刪除此列表中指定位置的元素
Class ArrayList 非線程安全數(shù)組,初始化容量10,底層object數(shù)組
構(gòu)造方法:
- ArrayList() 構(gòu)造初始容量為 10 的空列表(底層先創(chuàng)建了一個(gè)長(zhǎng)度為0的數(shù)組,添加元素是初始化為10,自動(dòng)擴(kuò)容1.5倍)
- ArrayList(int initialCapacity) 構(gòu)造具有指定初始容量的空列表(建議提前估計(jì),減少擴(kuò)容)
- ArrayList(Collection<? extends E> c) 構(gòu)造一個(gè)列表,其中包含指定集合的元素,并按集合的迭代器返回這些元素的順序排列。
方法:同List方法
Class LinkedList 雙向鏈表,隨機(jī)增刪效率高,檢索效率低
Class Vector 線程安全數(shù)組,默認(rèn)10,擴(kuò)容翻倍**(不經(jīng)常使用)**
轉(zhuǎn)換:使用集合工具類:java.util.Collections.synchronizedList(集合)
Interface Set無(wú)序不可重復(fù)存儲(chǔ)Map的Key
Class HashSet 哈希表(底層HashMap)
需要重寫hashCode和equals方法,其他方法參見(jiàn)HashMap
Interface SortedSet無(wú)序不可重復(fù)可排序
Class TreeSet 二叉樹(底層TreeMap Key部分)無(wú)序不可重復(fù)可排序
Key值自定義類需要實(shí)現(xiàn)java.long.Comparable接口或者創(chuàng)建比較器對(duì)象
class user implements Comparable<user>{ //自定義類需要實(shí)現(xiàn)接口
int age;
public user(int age) {
this.age = age;
}
@Override
public String toString() {
return "user{" + "age=" + age + '}';
}
@Override //重寫比較規(guī)則
public int compareTo(user o) {
return this.age-o.age; //返回==0,value覆蓋,返回大于0 到右子樹,返回小于0到左子樹
}
}
Interface Map<K,V>Map主接口(和Collection沒(méi)有繼承關(guān)系)
以Key和Value存儲(chǔ)數(shù)據(jù)都是引用數(shù)據(jù)類型,都存儲(chǔ)內(nèi)存地址,Key是主導(dǎo)
- V put(K key, V value) 添加鍵值對(duì)(Key元素需要重新hashCode和equals方法)(Key可以為空,只有一個(gè))
- void clear() 清空Map集合
- V get(Object key) 通過(guò)key獲取value(key元素需要重新hashCode和equals方法)
- boolean containsKey(Object key) 判斷Map是否包含某個(gè)key(底層equals)
- boolean containsValue(Object value) 判斷Map是否包含某個(gè)value(底層equals)
- boolean isEmpty() 判斷Map集合元素個(gè)數(shù)是否為零
- Set<K> keySet() 獲取Map集合所有的Key(是個(gè)set集合)
- V remove(Object key) 通過(guò)key刪除鍵值對(duì)
- Collection<V> values() 獲取Map集合中鍵值對(duì)所有value(返回Collection)
- int size() 獲取Map集合所有的鍵值對(duì)個(gè)數(shù)
Set<Map.Entry<Integer,String>>set1=m.entrySet(); //使用方法
Iterator<Map.Entry<Integer,String>> it=set1.iterator(); //獲取迭代器
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
System.out.println(entry); //直接遍歷
Integer key = entry.getKey(); //獲取鍵
String value = entry.getValue(); //獲取值
System.out.println(key + "=" + value); //分開遍歷
for(Map.Entry<Integer,String> node:set1) //效率較高,適合大數(shù)據(jù),直接獲取
System.out.println(node); //組合遍歷
Class HashMap<K,V> 哈希表 非線程安全(初始化容量16[必須是2的倍數(shù)],默認(rèn)加載因子0.75)
Key元素類型需要重新hashCode和equals方法
JDK8新特性:當(dāng)單向鏈表長(zhǎng)度超過(guò)8后數(shù)據(jù)結(jié)構(gòu)會(huì)變成紅黑樹數(shù)據(jù)結(jié)構(gòu),當(dāng)紅黑樹小于6,會(huì)變回鏈表
構(gòu)造 函數(shù) 描述
- HashMap() 使用默認(rèn)初始容量 (16) ,默認(rèn)負(fù)載系數(shù) (0.75)
- HashMap(int initialCapacity) 指定的初始容量,默認(rèn)負(fù)載系數(shù) 初始容量必須是2的倍數(shù):達(dá)到散列均勻,提高存取效率
- HashMap(int initialCapacity, float loadFactor) 指定初始容量和負(fù)載系數(shù)
- HashMap(Map<? extends K,? extends V> m)
Class Hashtable<K,V> 哈希表 線程安全(synchronized) Key不可以為空*(不常用)**
初始化容量11,默認(rèn)加載因子0.75f,擴(kuò)容:原容量*2+1
Class Properties 屬性類 繼承Hashtable類 僅支持String
- Object setProperty(String key, String value) 存
- String getProperty(String key) 取
- String getProperty(String key, String defaultValue) 當(dāng)key值為NULL時(shí),返回def的值;當(dāng)key值不為NULL時(shí),返回key的值
Interface SortedMap<K,V>
Class TreeMap<K,V> 二叉樹 可排序集合(中序遍歷)
Key值自定義類需要實(shí)現(xiàn)java.long.Comparable接口或者創(chuàng)建比較器對(duì)象(類或者匿名內(nèi)部類)
Class Collections 集合工具類
- synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(線程安全)映射。
- synchronizedList(List list) 返回由指定列表支持的同步(線程安全)列表。**
**synchronizedCollection(Collection c) 返回由指定集合支持的同步(線程安全)集合 - sort(List list, Comparator<? super T> c) 根據(jù)指定比較器引發(fā)的順序?qū)χ付斜磉M(jìn)行排序。
十三、泛型
1. 泛型概述
引入時(shí)間:JDK 5.0 之后的新特性。
作用:
- 統(tǒng)一集合中元素的類型,避免類型轉(zhuǎn)換錯(cuò)誤。
- 只在程序編譯階段起作用,編譯后會(huì)進(jìn)行類型擦除(Type Erasure)。
語(yǔ)法:
在創(chuàng)建對(duì)象時(shí),前后兩段添加泛型類型。
示例:
List<String> list = new ArrayList<String>();
2. 泛型的優(yōu)點(diǎn)
- 類型安全:編譯時(shí)檢查類型,避免運(yùn)行時(shí)類型轉(zhuǎn)換錯(cuò)誤。
- 代碼復(fù)用:可以編寫通用的類和方法,適用于多種類型。
- 代碼簡(jiǎn)潔:減少?gòu)?qiáng)制類型轉(zhuǎn)換的代碼。
3. 泛型的缺點(diǎn)
- 導(dǎo)致集合存儲(chǔ)缺少多樣性:泛型限制了集合中元素的類型,無(wú)法存儲(chǔ)多種類型的對(duì)象。
- 類型擦除:泛型信息在編譯后會(huì)被擦除,運(yùn)行時(shí)無(wú)法獲取泛型的具體類型。
4. 自動(dòng)推斷機(jī)制(鉆石表達(dá)式)
引入時(shí)間:JDK 7 新特性。
作用:自動(dòng)推斷泛型類型,簡(jiǎn)化代碼。
語(yǔ)法:只寫前面的泛型類型,后面的泛型類型可以省略。
示例:
List<String> list = new ArrayList<>();
5. 自定義泛型
泛型類:
在定義類時(shí)添加
<T>,T是類型參數(shù)。示例:
public class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } }使用:
Box<String> box = new Box<>(); box.setValue("Hello"); String value = box.getValue();
泛型方法:
在定義方法時(shí)添加
<T>,T是類型參數(shù)。示例:
public <T> void printArray(T[] array) { for (T element : array) { System.out.println(element); } }使用:
Integer[] intArray = {1, 2, 3}; printArray(intArray);
6. 泛型的通配符
<?>:表示任意類型。<? extends T>:表示T或其子類型(上界通配符)。<? super T>:表示T或其父類型(下界通配符)。示例:
public void printList(List<?> list) { for (Object element : list) { System.out.println(element); } }
7. 泛型的限制
- 不能使用基本類型:泛型類型必須是引用類型(如
Integer而不是int)。 - 不能創(chuàng)建泛型數(shù)組:例如
new T[10]是非法的。 - 不能實(shí)例化泛型類型:例如
new T()是非法的。
8. 泛型的應(yīng)用場(chǎng)景
- 集合框架:如
List<T>、Map<K, V>等。 - 工具類:如
Comparator<T>、Comparable<T>等。 - 自定義數(shù)據(jù)結(jié)構(gòu):如棧、隊(duì)列、鏈表等。
總結(jié)與拓展
- 泛型的作用:統(tǒng)一集合中元素的類型,提高代碼的安全性和復(fù)用性。
- 自動(dòng)推斷機(jī)制:JDK 7 引入的鉆石表達(dá)式簡(jiǎn)化了泛型代碼。
- 自定義泛型:通過(guò)泛型類和泛型方法實(shí)現(xiàn)通用代碼。
- 通配符:
<?>、<? extends T>、<? super T>提供了更靈活的類型約束。 - 限制:泛型不能使用基本類型、不能創(chuàng)建泛型數(shù)組、不能實(shí)例化泛型類型。
十四、多線程
進(jìn)程是:一個(gè)應(yīng)用程序(1個(gè)進(jìn)程是一個(gè)軟件)
獨(dú)立性:系統(tǒng)分配資源和調(diào)度資源的獨(dú)立單位
動(dòng)態(tài)性:進(jìn)程實(shí)質(zhì)是程序的一次執(zhí)行過(guò)程,進(jìn)程是動(dòng)態(tài)產(chǎn)生,動(dòng)態(tài)消亡的
并發(fā)性:任何進(jìn)程都可以同其他進(jìn)程一起并發(fā)執(zhí)行
線程是:一個(gè)進(jìn)程中的執(zhí)行場(chǎng)景/執(zhí)行單元,是進(jìn)程中單個(gè)順序控制流,是一條執(zhí)行路徑。
并行:同一時(shí)刻,多個(gè)指令在多個(gè)CPU上同時(shí)執(zhí)行
并發(fā):同一時(shí)刻,多個(gè)指令在單個(gè)CPU交替執(zhí)行
線程狀態(tài)轉(zhuǎn)換

1、新建狀態(tài)(New):新創(chuàng)建了一個(gè)線程對(duì)象。
2、就緒狀態(tài)(Runnable):線程對(duì)象創(chuàng)建后,其他線程調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,變得可運(yùn)行,等待獲取CPU的使用權(quán)。
3、運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
4、阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
(一)、等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,JVM會(huì)把該線程放入等待池中。(wait會(huì)釋放持有的鎖)
(二)、同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM會(huì)把該線程放入鎖池中。
(三)、其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)
當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。(注意,sleep是不會(huì)釋放持有的鎖)
5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
線程構(gòu)造方法:
| 構(gòu)造方法名 | 備注 |
|---|---|
| Thread() | |
| Thread(String name) | name為線程名字 |
| 創(chuàng)建線程第二種方式 | |
| Thread(Runnable target) | |
| Thread(Runnable target, String name) | name為線程名字 |
Java 中實(shí)現(xiàn)線程的三種方式總結(jié)
1. 繼承Thread類
實(shí)現(xiàn)方式:
- 編寫一個(gè)類,直接繼承
java.lang.Thread。 - 重寫
run()方法,定義線程執(zhí)行的任務(wù)。
- 編寫一個(gè)類,直接繼承
創(chuàng)建線程對(duì)象:
MyThread thread = new MyThread();
啟動(dòng)線程:
thread.start();
特點(diǎn):
- 簡(jiǎn)單易用,但 Java 是單繼承,繼承
Thread類后無(wú)法繼承其他類。
- 簡(jiǎn)單易用,但 Java 是單繼承,繼承
2. 實(shí)現(xiàn)Runnable接口
實(shí)現(xiàn)方式:
- 編寫一個(gè)類,實(shí)現(xiàn)
java.lang.Runnable接口。 - 實(shí)現(xiàn)
run()方法,定義線程執(zhí)行的任務(wù)。 - 通常使用匿名內(nèi)部類創(chuàng)建。
- 編寫一個(gè)類,實(shí)現(xiàn)
創(chuàng)建線程對(duì)象:
Runnable task = new MyRunnable(); Thread thread = new Thread(task);
啟動(dòng)線程:
thread.start();
特點(diǎn):
- 更靈活,可以避免單繼承的限制。
- 適合多個(gè)線程共享同一個(gè)任務(wù)。
3. 使用Callable和Future接口
實(shí)現(xiàn)方式:
- 編寫一個(gè)類,實(shí)現(xiàn)
java.util.concurrent.Callable接口。 - 實(shí)現(xiàn)
call()方法,定義線程執(zhí)行的任務(wù),并返回結(jié)果。
- 編寫一個(gè)類,實(shí)現(xiàn)
創(chuàng)建線程對(duì)象:
創(chuàng)建
Callable實(shí)現(xiàn)類的實(shí)例:Callable<Integer> task = new MyCallable();
使用
FutureTask包裝Callable對(duì)象:FutureTask<Integer> futureTask = new FutureTask<>(task);
使用
FutureTask對(duì)象作為Thread的target創(chuàng)建線程:Thread thread = new Thread(futureTask);
啟動(dòng)線程:
thread.start();
獲取結(jié)果:
Integer result = futureTask.get(); // 阻塞直到獲取結(jié)果
特點(diǎn):
call()方法可以有返回值和拋出異常。- 適合需要獲取線程執(zhí)行結(jié)果的場(chǎng)景。
Future接口的常用方法
cancel(boolean mayInterruptIfRunning):嘗試取消任務(wù)。get():獲取任務(wù)結(jié)果,阻塞直到任務(wù)完成。get(long timeout, TimeUnit unit):在指定時(shí)間內(nèi)獲取任務(wù)結(jié)果,超時(shí)拋出TimeoutException。isCancelled():判斷任務(wù)是否被取消。isDone():判斷任務(wù)是否完成。
三種方式的對(duì)比
| 方式 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|
繼承 Thread 類 | 簡(jiǎn)單易用 | 單繼承限制,無(wú)法繼承其他類 |
實(shí)現(xiàn) Runnable 接口 | 靈活,避免單繼承限制,適合多線程共享任務(wù) | 無(wú)法直接獲取線程執(zhí)行結(jié)果 |
使用 Callable 和 Future | 可以獲取線程執(zhí)行結(jié)果,支持異常處理,功能更強(qiáng)大 | 使用稍復(fù)雜,需要 FutureTask 包裝 |
總結(jié)
- 繼承
Thread類:適合簡(jiǎn)單的線程任務(wù),但受限于單繼承。 - 實(shí)現(xiàn)
Runnable接口:更靈活,適合多線程共享任務(wù)。 - 使用
Callable和Future:適合需要獲取線程執(zhí)行結(jié)果或處理異常的場(chǎng)景。
根據(jù)具體需求選擇合適的方式實(shí)現(xiàn)多線程編程。
獲取當(dāng)前線程對(duì)象、獲取線程對(duì)象名字、修改線程對(duì)象名字
| 方法名 | 作用 |
|---|---|
| static Thread currentThread() | 獲取當(dāng)前線程對(duì)象 |
| String getName() | 獲取線程對(duì)象名字 |
| void setName(String name) | 修改線程對(duì)象名字 |
關(guān)于線程的sleep方法
| 方法名 | 作用 |
|---|---|
| static void sleep(long millis) | 讓當(dāng)前線程休眠millis秒 |
關(guān)于線程中斷sleep()的方法
| 方法名 | 作用 |
|---|---|
| void interrupt() | 終止線程的睡眠 |
Java進(jìn)程的優(yōu)先級(jí)
| 常量名 | 備注 |
|---|---|
| static int MAX_PRIORITY | 最高優(yōu)先級(jí)(10) |
| static int MIN_PRIORITY | 最低優(yōu)先級(jí)(1) |
| static int NORM_PRIORITY | 默認(rèn)優(yōu)先級(jí)(5) |
方法:
| 方法名 | 作用 |
|---|---|
| int getPriority() | 獲得線程優(yōu)先級(jí) |
| void setPriority(int newPriority) | 設(shè)置線程優(yōu)先級(jí) |
| static void yield() | 讓位,當(dāng)前線程暫停,回到就緒狀態(tài),讓給其它線程。 |
| void join() | 將一個(gè)線程合并到當(dāng)前線程中,當(dāng)前線程受阻塞,加入的線程執(zhí)行直到結(jié)束 |
| void join(long millis) | 接上條,等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒 |
| void join(long millis, int nanos) | 接第一條,等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒 + nanos 納秒 |
多線程并發(fā)環(huán)境下,數(shù)據(jù)的安全問(wèn)題(重點(diǎn))
1.為什么這個(gè)是重點(diǎn)?
以后在開發(fā)中,我們的項(xiàng)目都是運(yùn)行在服務(wù)器當(dāng)中,而服務(wù)器已經(jīng)將線程的定義,線程對(duì)象的創(chuàng)建,線程的啟動(dòng)等,都已經(jīng)實(shí)現(xiàn)完了。這些代碼我們都不需要編寫。
最重要的是: 你要知道,你編寫的程序需要放到一個(gè)多線程的環(huán)境下運(yùn)行,你更需要關(guān)注的是這些數(shù)據(jù)在多線程并發(fā)的環(huán)境下是否是安全的。(重點(diǎn):★★★★★)
2.什么時(shí)候數(shù)據(jù)在多線程并發(fā)的環(huán)境下會(huì)存在安全問(wèn)題呢?★★★★★
滿足三個(gè)條件:
條件1:多線程并發(fā)。
條件2:有共享數(shù)據(jù)。
條件3:共享數(shù)據(jù)有修改的行為。
滿足以上3個(gè)條件之后,就會(huì)存在線程安全問(wèn)題。
3.怎么解決線程安全問(wèn)題呢?
當(dāng)多線程并發(fā)的環(huán)境下,有共享數(shù)據(jù),并且這個(gè)數(shù)據(jù)還會(huì)被修改,此時(shí)就存在線程安全問(wèn)題,怎么解決這個(gè)問(wèn)題?
線程排隊(duì)執(zhí)行。(不能并發(fā))。用排隊(duì)執(zhí)行解決線程安全問(wèn)題。
這種機(jī)制被稱為:線程同步機(jī)制。專業(yè)術(shù)語(yǔ)叫做:線程同步,實(shí)際上就是線程不能并發(fā)了,線程必須排隊(duì)執(zhí)行。
線程同步就是線程排隊(duì)了,線程排隊(duì)了就會(huì) 犧牲一部分效率 ,數(shù)據(jù)安全第一位,只有數(shù)據(jù)安全了,我們才可以談效率。數(shù)據(jù)不安全,沒(méi)有效率的事兒。
死鎖(DeadLock)
死鎖(Deadlock)是多線程編程中的一種常見(jiàn)問(wèn)題,指的是兩個(gè)或多個(gè)線程在執(zhí)行過(guò)程中,因?yàn)闋?zhēng)奪資源而造成的一種互相等待的現(xiàn)象,導(dǎo)致這些線程都無(wú)法繼續(xù)執(zhí)行下去。
死鎖代碼要會(huì)寫。一般面試官要求你會(huì)寫。只有會(huì)寫的,才會(huì)在以后的開發(fā)中注意這個(gè)事兒。因?yàn)樗梨i很難調(diào)試。
死鎖的四個(gè)必要條件
死鎖的發(fā)生必須同時(shí)滿足以下四個(gè)條件:
互斥條件(Mutual Exclusion):
- 資源一次只能被一個(gè)線程占用。
占有并等待(Hold and Wait):
- 線程已經(jīng)占有了至少一個(gè)資源,但又申請(qǐng)新的資源,而新的資源被其他線程占用。
不可搶占(No Preemption):
- 線程已占有的資源不能被其他線程強(qiáng)行搶占,必須由線程自己釋放。
循環(huán)等待(Circular Wait):
- 存在一個(gè)線程的等待循環(huán)鏈,每個(gè)線程都在等待下一個(gè)線程所占用的資源。
Java 中的死鎖示例
以下是一個(gè)經(jīng)典的死鎖代碼示例,展示了兩個(gè)線程互相等待對(duì)方釋放鎖的情況:
public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(100); // 模擬操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 1: Acquired lock 2!");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try {
Thread.sleep(100); // 模擬操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (lock1) {
System.out.println("Thread 2: Acquired lock 1!");
}
}
});
thread1.start();
thread2.start();
}
}
代碼分析
線程1:
- 先獲取
lock1,然后嘗試獲取lock2。 - 在獲取
lock2之前,線程1會(huì)休眠100毫秒。
- 先獲取
線程2:
- 先獲取
lock2,然后嘗試獲取lock1。 - 在獲取
lock1之前,線程2會(huì)休眠100毫秒。
- 先獲取
死鎖發(fā)生:
- 線程1持有
lock1并等待lock2。 - 線程2持有
lock2并等待lock1。 - 兩個(gè)線程互相等待,導(dǎo)致死鎖。
- 線程1持有
如何避免死鎖
避免嵌套鎖:
- 盡量不要在持有一個(gè)鎖的同時(shí)去申請(qǐng)另一個(gè)鎖。
按順序獲取鎖:
- 如果多個(gè)線程需要獲取多個(gè)鎖,確保它們以相同的順序獲取鎖。
使用超時(shí)機(jī)制:
- 在獲取鎖時(shí)設(shè)置超時(shí)時(shí)間,如果超時(shí)則釋放已持有的鎖并重試。
使用工具檢測(cè):
- 使用工具(如
jstack)檢測(cè)死鎖。
- 使用工具(如
死鎖的調(diào)試與檢測(cè)
使用
jstack:- 運(yùn)行程序后,使用
jstack命令查看線程狀態(tài),可以檢測(cè)到死鎖。
- 運(yùn)行程序后,使用
日志輸出:
- 在代碼中添加日志,記錄鎖的獲取和釋放情況。
使用工具:
- 使用IDE(如IntelliJ IDEA)或第三方工具(如VisualVM)檢測(cè)死鎖。
守護(hù)線程
在Java中,線程分為兩大類:用戶線程和守護(hù)線程。守護(hù)線程(Daemon Thread)是一種特殊的線程,它的生命周期依賴于用戶線程。當(dāng)所有的用戶線程結(jié)束時(shí),守護(hù)線程會(huì)自動(dòng)退出。
守護(hù)線程的特點(diǎn)
依賴用戶線程:
- 守護(hù)線程是為用戶線程提供服務(wù)的線程。
- 當(dāng)所有的用戶線程結(jié)束時(shí),守護(hù)線程會(huì)自動(dòng)退出。
典型代表:
- 垃圾回收線程(
GC)是Java中最典型的守護(hù)線程。
- 垃圾回收線程(
主線程是用戶線程:
main方法所在的線程是用戶線程。
死循環(huán):
- 守護(hù)線程通常是一個(gè)死循環(huán),持續(xù)執(zhí)行某些后臺(tái)任務(wù)。
守護(hù)線程的應(yīng)用場(chǎng)景
定時(shí)任務(wù):
- 例如,每天00:00自動(dòng)備份系統(tǒng)數(shù)據(jù)。
- 可以使用定時(shí)器(如
Timer或ScheduledExecutorService),并將定時(shí)任務(wù)設(shè)置為守護(hù)線程。
后臺(tái)監(jiān)控:
- 例如,監(jiān)控系統(tǒng)資源使用情況、日志清理等。
垃圾回收:
- Java的垃圾回收線程就是一個(gè)守護(hù)線程。
守護(hù)線程的設(shè)置
在Java中,可以通過(guò)setDaemon(boolean on)方法將一個(gè)線程設(shè)置為守護(hù)線程:
| 方法簽名 | 說(shuō)明 |
|---|---|
void setDaemon(boolean on) | on為true表示將線程設(shè)置為守護(hù)線程 |
注意:
- 必須在調(diào)用
start()方法之前設(shè)置守護(hù)線程,否則會(huì)拋出IllegalThreadStateException。 - 守護(hù)線程中創(chuàng)建的子線程默認(rèn)也是守護(hù)線程。
代碼示例
以下是一個(gè)守護(hù)線程的示例,展示了如何設(shè)置守護(hù)線程以及它的行為:
public class DaemonThreadExample {
public static void main(String[] args) {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("守護(hù)線程正在運(yùn)行...");
try {
Thread.sleep(1000); // 模擬任務(wù)執(zhí)行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 設(shè)置為守護(hù)線程
daemonThread.setDaemon(true);
// 啟動(dòng)守護(hù)線程
daemonThread.start();
// 主線程(用戶線程)執(zhí)行任務(wù)
System.out.println("主線程開始執(zhí)行...");
try {
Thread.sleep(5000); // 模擬主線程執(zhí)行任務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主線程執(zhí)行完畢,程序退出。");
}
}
代碼分析
守護(hù)線程:
- 守護(hù)線程是一個(gè)死循環(huán),每隔1秒輸出一條消息。
- 設(shè)置為守護(hù)線程后,當(dāng)主線程結(jié)束時(shí),守護(hù)線程會(huì)自動(dòng)退出。
主線程:
- 主線程執(zhí)行5秒后結(jié)束。
- 主線程結(jié)束后,守護(hù)線程也會(huì)自動(dòng)退出。
守護(hù)線程的注意事項(xiàng)
資源釋放:
- 守護(hù)線程中不要執(zhí)行關(guān)鍵任務(wù)(如文件寫入、數(shù)據(jù)庫(kù)操作等),因?yàn)樗耐顺鍪遣豢煽氐摹?/li>
線程優(yōu)先級(jí):
- 守護(hù)線程的優(yōu)先級(jí)通常較低,適合執(zhí)行后臺(tái)任務(wù)。
生命周期:
- 守護(hù)線程的生命周期依賴于用戶線程,不能獨(dú)立存在。
定時(shí)器的作用:
間隔特定的時(shí)間,執(zhí)行特定的程序。在實(shí)際的開發(fā)中,每隔多久執(zhí)行一段特定的程序,這種需求是很常見(jiàn)的,那么在java中其實(shí)可以采用多種方式實(shí)現(xiàn):
可以使用sleep方法,睡眠,設(shè)置睡眠時(shí)間,沒(méi)到這個(gè)時(shí)間點(diǎn)醒來(lái),執(zhí)行任務(wù)。這種方式是最原始的定時(shí)器。(比較low)
在java的類庫(kù)中已經(jīng)寫好了一個(gè)定時(shí)器:java.util.Timer,可以直接拿來(lái)用。
不過(guò),這種方式在目前的開發(fā)中也很少用,因?yàn)楝F(xiàn)在有很多高級(jí)框架都是支持定時(shí)任務(wù)的。
在實(shí)際的開發(fā)中,目前使用較多的是Spring框架中提供的SpringTask框架,這個(gè)框架只要進(jìn)行簡(jiǎn)單的配置,就可以完成定時(shí)器的任務(wù)。
| 構(gòu)造方法名 | 備注 |
|---|---|
| Timer() | 創(chuàng)建一個(gè)定時(shí)器 |
| Timer(boolean isDaemon) | isDaemon為true為守護(hù)線程定時(shí)器 |
| Timer(String name) | 創(chuàng)建一個(gè)定時(shí)器,其線程名字為name |
| Timer(String name, boolean isDaemon) | 結(jié)合2、3 |
| 方法名 | 作用 |
| void schedule(TimerTask task, Date firstTime, long period) | 安排指定的任務(wù)在指定的時(shí)間開始進(jìn)行重復(fù)的固定延遲執(zhí)行 |
| void cancel() | 終止定時(shí)器 |
關(guān)于Object類的wait()、notify()、notifyAll()方法
| 方法名 | 作用 |
|---|---|
| void wait() | 讓活動(dòng)在當(dāng)前對(duì)象的線程無(wú)限等待(釋放之前占有的鎖) |
| void notify() | 喚醒當(dāng)前對(duì)象正在等待的線程(只提示喚醒,不會(huì)釋放鎖) |
| void notifyAll() | 喚醒當(dāng)前對(duì)象全部正在等待的線程(只提示喚醒,不會(huì)釋放鎖) |
wait和notify方法不是線程對(duì)象的方法,是java中任何一個(gè)java對(duì)象都有的方法,因?yàn)檫@兩個(gè)方法是 Object類中自帶 的。
wait方法和notify方法不是通過(guò)線程對(duì)象調(diào)用
調(diào)用:
Object o = new Object();
o.wait();
總結(jié) ★★★★★(呼應(yīng)生產(chǎn)者消費(fèi)者模式)
1、wait和notify方法不是線程對(duì)象的方法,是普通java對(duì)象都有的方法。
2、wait方法和notify方法建立在 線程同步 的基礎(chǔ)之上。因?yàn)槎嗑€程要同時(shí)操作一個(gè)倉(cāng)庫(kù)。有線程安全問(wèn)題。
3、wait方法作用:o.wait() 讓正在o對(duì)象上活動(dòng)的線程t進(jìn)入等待狀態(tài),并且釋放掉t線程之前占有的o對(duì)象的鎖
4、notify方法作用:o.notify() 讓正在o對(duì)象上等待的線程喚醒,只是通知,不會(huì)釋放o對(duì)象上之前占有的鎖。
生產(chǎn)者消費(fèi)者模式(wait()和notify())
什么是“生產(chǎn)者和消費(fèi)者模式”?
生產(chǎn)線程負(fù)責(zé)生產(chǎn),消費(fèi)線程負(fù)責(zé)消費(fèi)。
生產(chǎn)線程和消費(fèi)線程要達(dá)到均衡。
這是一種特殊的業(yè)務(wù)需求,在這種特殊的情況下需要使用wait方法和notify方法。
模擬一個(gè)業(yè)務(wù)需求
倉(cāng)庫(kù)我們采用List集合。
List集合中假設(shè)只能存儲(chǔ)1個(gè)元素。
1個(gè)元素就表示倉(cāng)庫(kù)滿了。
如果List集合中元素個(gè)數(shù)是0,就表示倉(cāng)庫(kù)空了。
保證List集合中永遠(yuǎn)都是最多存儲(chǔ)1個(gè)元素。
必須做到這種效果:生產(chǎn)1個(gè)消費(fèi)1個(gè)。

十五、反射
1. Class 對(duì)象概述
- Class 對(duì)象:在 Java 中,每個(gè)類在加載到內(nèi)存時(shí)都會(huì)生成一個(gè)
Class對(duì)象,該對(duì)象存儲(chǔ)了類的所有信息(如方法、構(gòu)造函數(shù)、字段等)。 - 反射:通過(guò)
Class對(duì)象,可以在運(yùn)行時(shí)動(dòng)態(tài)獲取類的信息并操作類的成員(如調(diào)用方法、訪問(wèn)字段等)。
2. Class 對(duì)象的生成方式
類名.class:JVM 將類加載到內(nèi)存中,但不進(jìn)行初始化。
返回該類的
Class對(duì)象。示例:
Class<?> clazz = String.class;
Class.forName("包名.類名"):加載類并默認(rèn)進(jìn)行靜態(tài)初始化。
返回該類的
Class對(duì)象。示例:
Class<?> clazz = Class.forName("java.lang.String");
Class.forName("包名.類名", false, 類加載器):第二個(gè)參數(shù)為
false時(shí),不進(jìn)行初始化;為true時(shí),進(jìn)行初始化。示例:
Class<?> clazz = Class.forName("java.lang.String", false, ClassLoader.getSystemClassLoader());
實(shí)例對(duì)象.getClass():對(duì)類進(jìn)行靜態(tài)初始化和非靜態(tài)初始化。
返回運(yùn)行時(shí)實(shí)際對(duì)象所屬類的
Class對(duì)象。示例:
String str = "Hello"; Class<?> clazz = str.getClass();
3. Class 對(duì)象的特性
- 父子類 Class 對(duì)象不一致:
- 如果
A是B的子類,則A.class和B.class返回的Class對(duì)象不同。 - 如果
a是A的實(shí)例,則A.class和a.getClass()返回的Class對(duì)象一致。
- 如果
4. Class 類的常用方法
getName():返回類的全限定名(包名 + 類名)。getSuperclass():返回類的直接父類的Class對(duì)象。getInterfaces():返回類實(shí)現(xiàn)的所有接口的Class數(shù)組。isArray():判斷該類是否是數(shù)組類型。isEnum():判斷該類是否是枚舉類型。isInterface():判斷該類是否是接口。isPrimitive():判斷該類是否是基本類型(如int、boolean等)。isAssignableFrom(Class cls):判斷該類是否是cls的父類或父接口。getComponentType():如果該類是數(shù)組類型,返回?cái)?shù)組的組件類型。asSubclass(Class clazz):將當(dāng)前Class對(duì)象轉(zhuǎn)換為clazz的子類類型。
5.asSubclass方法的使用
作用:將當(dāng)前
Class對(duì)象轉(zhuǎn)換為指定類的子類類型。示例:
List<String> strList = new ArrayList<>(); Class<? extends List> strListCast = strList.getClass().asSubclass(List.class);
動(dòng)態(tài)加載時(shí)的應(yīng)用:
Class.forName("xxx.xxx.xxx").asSubclass(List.class).newInstance();- 如果
xxx.xxx.xxx是List的子類,則正常執(zhí)行;否則拋出ClassCastException。
- 如果
6. 靜態(tài)加載與動(dòng)態(tài)加載
- 靜態(tài)加載:通過(guò)
new ClassName()加載類,編譯時(shí)必須提供類的定義。 - 動(dòng)態(tài)加載:通過(guò)
Class.forName("ClassName")加載類,編譯時(shí)可以缺席,運(yùn)行時(shí)按需提供。
總結(jié)
- Class 對(duì)象:存儲(chǔ)類的所有信息,是反射機(jī)制的核心。
- 生成方式:
類名.class、Class.forName()、實(shí)例對(duì)象.getClass()。 - 常用方法:
getName()、getSuperclass()、getInterfaces()、asSubclass()等。 asSubclass:用于將Class對(duì)象轉(zhuǎn)換為指定類的子類類型。- 靜態(tài)加載與動(dòng)態(tài)加載:靜態(tài)加載在編譯時(shí)提供類定義,動(dòng)態(tài)加載在運(yùn)行時(shí)按需提供。
通過(guò)掌握 Class 對(duì)象和反射機(jī)制,可以在運(yùn)行時(shí)動(dòng)態(tài)操作類的成員,實(shí)現(xiàn)靈活的編程。
十六、小游戲(進(jìn)擊的小鳥)
public class StartGame { //游戲開始類
public static void main(String[] args) throws InterruptedException {
JFrame jFrame = new JFrame("進(jìn)擊の小鳥"); //創(chuàng)建窗口對(duì)象
jFrame.setSize(400,600);//窗口大小
jFrame.setLocationRelativeTo(null); //窗口相對(duì)位置
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設(shè)定點(diǎn)擊關(guān)閉結(jié)束程序
? BirdGame birdGame = new BirdGame(); //初始化游戲?qū)ο箢?
? jFrame.add(birdGame); //把創(chuàng)建好的對(duì)象加進(jìn)來(lái)
? jFrame.setVisible(true); //讓窗口可視化
? birdGame.action(); //地面運(yùn)動(dòng)方法
}
}
public class Bird {
public BufferedImage images[];
public BufferedImage image; //存放小鳥圖片
public int x;
public int y;
public int width;
public int height;
public int index=0;
public double speed=0; //小鳥初始速度
public double upspeed=30; //初始上拋速度
public double s=0; //經(jīng)過(guò)t,發(fā)生的位移
public double t=0.2; //發(fā)生位移時(shí)間
public double g=9.8; //重力加速度
public Bird() throws IOException {
x=120;
y=120;
images=new BufferedImage[8];
image= ImageIO.read(getClass().getResource("0.png"));
width=image.getWidth();
height=image.getHeight();
for (int i=0;i<images.length;i++) {
images[i] = ImageIO.read(getClass().getResource(i+".png"));
}
}
public void fly(){ //小鳥飛飛
index++;
image=images[index/2%8];
}
public void upSpeed(){ //鼠標(biāo)點(diǎn)擊游戲屏幕,給小鳥一個(gè)初始上拋速度
speed=upspeed;
}
public void distanceChange(){ //實(shí)現(xiàn)小鳥速度,位移,縱坐標(biāo)變化
double v=speed; //初始速度
s=v*t-g*t*t/2; //經(jīng)過(guò)t小鳥的位移
speed=v-g*t; //小鳥經(jīng)過(guò)時(shí)間t的末速度
y=y-(int)s; //經(jīng)過(guò)時(shí)間t后,小鳥的y
}
}
public class Column { //管道類
public BufferedImage cImage;
public int x;
public int y;
public int width;
public int height;
public int distance=270; //兩根管道之間的距離
public static int count=0;
Random random = new Random();
public Column() throws IOException {
cImage= ImageIO.read(getClass().getResource("column.png"));
x=450+distance*count;
width=cImage.getWidth(); //獲得管道的寬
height=cImage.getHeight(); //高
y=-( height/2-random.nextInt(300)-50);
count++;
}
public void step(){
x-=5; //讓地面往左運(yùn)動(dòng)
if (x<=-width/2){
x=x+distance*2;
y=-(height/2-random.nextInt(300)-50) ;
//x=400;
}
}
}
public class Ground { //地面類
public BufferedImage image; //存放地面圖片
public int x;
public int y;
public Ground() {
? try {
? x=0;
? y=500;
? image= ImageIO.read(getClass().getResource("ground.png"));
? } catch (IOException e) {
? e.printStackTrace();
? }
}
public void step(){
? x-=1; //讓地面往左運(yùn)動(dòng)
? if (x==-100){
? x=0;
? }
}
}
public class Music implements Runnable { //音樂(lè)類
Player player=null;
@Override
public void run() {
InputStream resourceAsStream = this.getClass().getResourceAsStream("2.mp3");
try {
player=new Player(resourceAsStream);
player.play();
} catch (JavaLayerException e) {
e.printStackTrace();
}
}
public void stopBGM(){
if (player!=null)
player.close();
}
}
public class Score { //連接對(duì)象
private String sid;
private int score;
private String time;
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
public class ScoreManager { //jdbc連接
static{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public List<Score> selectAllScore(){ //查詢方法
List<Score> list = new ArrayList<>();
? try {
? String sql="select * from score order by time";
? Connection conn = DriverManager.getConnection("jdbc:mysql://cdb-kthncrwi.bj.tencentcdb.com:10159/flybird?useUnicode=true", "student", "521qianfeng");
? PreparedStatement pst = conn.prepareStatement(sql);
? ResultSet resultSet = pst.executeQuery();
? while (resultSet.next()){
? Score score = new Score();
? score.setSid(resultSet.getString("sid"));
? score.setScore(resultSet.getInt("score"));
? score.setTime(resultSet.getString("time"));
? list.add(score);
? }
? } catch (SQLException e) {
? e.printStackTrace();
? }
? return list;
}
public int insertScore(int score) { //插入方法
? int num = 0;
? String sql = "insert into score(sid,score,time) value(?,?,?)";
? try {
? Connection conn = DriverManager.getConnection("jdbc:mysql://cdb-kthncrwi.bj.tencentcdb.com:10159/flybird?useUnicode=true", "student", "521qianfeng");
? PreparedStatement pst = conn.prepareStatement(sql);
? String sid= UUID.randomUUID().toString(); //隨機(jī)生成id
? pst.setString(1,sid);
? pst.setInt(2,score);
? Date date = new Date();
? SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //創(chuàng)建時(shí)間類型對(duì)象
? String time=simpleDateFormat.format(date);
? pst.setString(3,time);
? num=pst.executeUpdate();
? } catch (SQLException e) {
? e.printStackTrace();
? }
? return num;
}
}
public class BirdGame extends JPanel { //自定義面板類繼承面板類
ScoreManager sc=new ScoreManager();
public JPanel jp=new JPanel();
public BufferedImage bg; //圖片緩沖區(qū)(在顯示圖片前對(duì)圖片進(jìn)行操作 eg:.getWidth()寬,.getHeight()高)
public BufferedImage startbg;
public BufferedImage overbg;
public Ground ground;
public Bird bird;
public Column columns[];
public Music music;
String file="H:\\Java程序\\小程序\\src\\小鳥\\png\\bg.png";
public int state; //表示游戲狀態(tài)
public static final int START=0; //開始
public static final int RUNNING=1; //運(yùn)行
public static final int GAMEOVER=2; //結(jié)束
public static int score=0; //初始積分
public BirdGame(){
try {
state=START; //游戲初始為游戲開始狀態(tài)
ground=new Ground(); //創(chuàng)建地面類對(duì)象,調(diào)用地面類構(gòu)造方法
bird = new Bird();
columns=new Column[2];
music = new Music();
for (int i=0;i<columns.length;i++){
columns[i]=new Column();
}
bg= ImageIO.read(getClass().getResource("bg.png")); //讀取這張圖片并把圖片值賦給變量
//bg=ImageIO.read(new File(file));
//bg=ImageIO.read(new File("src/小鳥/png/bg.png"));
startbg=ImageIO.read(getClass().getResource("start.png"));
overbg=ImageIO.read(getClass().getResource("gameover.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void paint(Graphics g) { //繪制一次的畫畫方法
super.paint(g); //調(diào)用畫筆
g.drawImage(bg,0,0,null); //繪制背景(最后一個(gè)參數(shù)為觀察者
switch (state){
case START:
//繪制游戲開始圖片
settishi(g);
g.drawImage(startbg,0,0,null);
? break;
? case RUNNING:
? for (int i=0;i<columns.length;i++) {
? g.drawImage(columns[i].cImage, columns[i].x, columns[i].y, null);
? }
? break;
? case GAMEOVER:
? //繪制游戲結(jié)束圖片
? settishi2(g);
? g.drawImage(overbg,0,0,null);
? break;
? }
? g.drawImage(ground.image,ground.x,ground.y,null); //繪制地面
? g.drawImage(bird.image,bird.x, bird.y,null); //繪制小鳥
? setScore(g);
}
public boolean isHitGround(){ //撞擊地面
? if (bird.y+bird.height>500){
? return true;
? }else {
? return false;
? }
}
public boolean isHitSky(){ //撞擊天空
? if (bird.y<0){
? return true;
? }else {
? return false;
? }
}
public boolean isguandao(Column c) {
? if (bird.x + bird.width >= c.x && c.x + c.width >= bird.x) { //撞擊管道左右
? if (bird.y <= c.height / 2 + c.y - 72 || bird.y + bird.height >= c.height / 2 + c.y + 72) {
? return true;
? } else {
? return false;
? }
? } else {
? return false;
? }
}
public void setScore(Graphics g){ //繪制分?jǐn)?shù)方法
Font font = new Font(Font.SERIF, Font.ITALIC, 40); //羅馬字體,斜體,40號(hào)
g.setFont(font); //獲取字體
g.setColor(Color.white);//獲取顏色
g.drawString(score+"分",40,60); //畫字符串
}
public void settishi(Graphics g){ //繪制分?jǐn)?shù)方法
Font font1 = new Font(Font.SERIF, Font.BOLD, 25); //羅馬字體,斜體,40號(hào)
g.setFont(font1); //獲取字體
g.setColor(Color.black);//獲取顏色
g.drawString("點(diǎn)擊屏幕開始運(yùn)行",110,400); //畫字符串
g.drawString(" 制作人---趙嘉盟",120,430);
}
public void settishi2(Graphics g){ //繪制分?jǐn)?shù)方法
Font font2 = new Font(Font.SANS_SERIF, Font.BOLD, 30); //羅馬字體,斜體,40號(hào)
g.setFont(font2); //獲取字體
g.setColor(Color.red);//獲取顏色
g.drawString("點(diǎn)擊屏幕重新開始",100,500); //畫字符串
}
public void action() throws InterruptedException { //游戲?qū)ο筮\(yùn)動(dòng)方法
this.addMouseListener(new BirdMouseListener()); //添加鼠標(biāo)監(jiān)聽器
? while (true){
? switch (state){ //狀態(tài)不同,對(duì)象運(yùn)動(dòng)效果不同
? case START:
? ground.step(); //調(diào)用地面運(yùn)動(dòng)方法
? bird.fly();
break;
case RUNNING:
? bird.distanceChange();
? ground.step(); //調(diào)用地面運(yùn)動(dòng)方法
? bird.fly();
? if (isHitGround()||isHitSky()){
? state=GAMEOVER;
? break;
? }
? for (int i=0;i<columns.length;i++){
? Column cl=columns[i];
? cl.step();
? if (isguandao(cl)){
? state=GAMEOVER;
? break;
? }
? if (bird.x==cl.x){
? score++;
? }
? }
? break;
? case GAMEOVER:
? music.stopBGM();
? break;
? }
? repaint(); //刷新方法(重新繪制)
? Thread.sleep(50); //線程睡眠
? }
}
class BirdMouseListener extends MouseAdapter{ //小鳥飛行鼠標(biāo)控制監(jiān)聽內(nèi)部類
? @Override
? public void mousePressed(MouseEvent e) {
? super.mousePressed(e);
? switch (state){
? case START:
? state=RUNNING; //鼠標(biāo)點(diǎn)擊開始運(yùn)行
? Thread thread = new Thread(music);
? thread.start();
? break;
? case RUNNING:
? bird.upSpeed(); //鼠標(biāo)點(diǎn)擊屏幕給小鳥一個(gè)初始上拋速度
? break;
? case GAMEOVER:
? sc.insertScore(score); //向數(shù)據(jù)庫(kù)插入分?jǐn)?shù)
? List<Score> scores = sc.selectAllScore();//查詢數(shù)據(jù)庫(kù)所有分?jǐn)?shù)
? String message="";
? for (Score score1 : scores) {
? message=message+"時(shí)間:"+score1.getTime()+"\n分?jǐn)?shù):"+score1.getScore()+"\n";
? }
? JOptionPane.showConfirmDialog(jp,message,"實(shí)時(shí)分?jǐn)?shù)",JOptionPane.WARNING_MESSAGE);
? state=START; //鼠標(biāo)點(diǎn)擊游戲恢復(fù)開始狀態(tài)
? bird.x=120;
? bird.y=220;
? bird.speed=0;
? Column.count=0;
? try {
? columns[0] = new Column();
? } catch (IOException ex) {
? ex.printStackTrace();
? }
? try {
? columns[1] = new Column();
? } catch (IOException ex) {
? ex.printStackTrace();
? }
? score = 0;//給積分初始化
? for (int i=0;i<columns.length;i++){
? try {
? columns[i]=new Column();
? } catch (IOException ex) {
? ex.printStackTrace();
? }
? }
? break;
? }
? }
}
}
十七、Stream
Stream簡(jiǎn)介
Java 8 中的 Stream 是對(duì)(Collection)集合對(duì)象功能的增強(qiáng),它專注于對(duì)集合對(duì)象進(jìn)行各種非常便利、高效的聚合操作
或大批量數(shù)據(jù)操作。Stream API 借助于同樣新出現(xiàn)的 Lambda 表達(dá)式,極大的提高編程效率和程序可讀性。
Stream原理
這種編程風(fēng)格將要處理的元素集合看作一種流,流在管道中傳輸,并且可以在管道的節(jié)點(diǎn)上進(jìn)行處理,比如篩選,排序,聚合等。
元素流在管道中經(jīng)過(guò)中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的
結(jié)果。
Stream優(yōu)點(diǎn)
(1)速度更快
(2)代碼更少(增加了新的語(yǔ)法Lambda表達(dá)式)
(3)強(qiáng)大的Stream API
(4)便于并行
(5)最大化減少了空指針異常Optional
Stream的操作三個(gè)步驟:
(1)創(chuàng)建Stream,一個(gè)數(shù)據(jù)源(如:集合、數(shù)組),獲取一個(gè)流;
(2)中間操作,一個(gè)中間操作鏈,對(duì)數(shù)據(jù)源的數(shù)據(jù)進(jìn)行處理;
(3)終止操作,一個(gè)終止操作,執(zhí)行中間操作鏈,并產(chǎn)生結(jié)果。
集合有兩種方式生成流:
stream() − 為集合創(chuàng)建串行流。
parallelStream() − 為集合創(chuàng)建并行流
-Stream的的中間操作(intermediate)和最終操作(terminal)都包含的方法:
中間操作(intermediate)
1.filter : 通過(guò)設(shè)置條件來(lái)過(guò)濾元素。
List<String> list = Arrays.asList("aaa","ddd","bbb","ccc","a2a","d2d","b2b","c2c","a3a","d3d","b3b","c3c");
list.stream().filter((s)->s.contains("a")).forEach(s -> System.out.println(s));
以上代碼使用filter方法過(guò)濾出只包含”a”的元素,然后通過(guò)forEach將滿足條件的元素遍歷出來(lái)。
map : 就是將對(duì)應(yīng)的元素使用給定方法進(jìn)行轉(zhuǎn)換。
List<String> list = Arrays.asList("aaa","ddd","bbb","ccc","a2a","d2d","b2b","c2c","a3a","d3d","b3b","c3c");
list.stream().filter((s)->s.contains("a")).map((s)-> s + "---map").forEach(s -> System.out.println(s));
在filter的基礎(chǔ)上,給每個(gè)元素后面添加字符串”—map”
flatMap:如果流的元素為數(shù)組或者Collection,flatMap就是將每個(gè)Object[]元素或Collection元素都轉(zhuǎn)換為Object元素。
List<String[]> setList = new ArrayList<>();
setList.add(new String[]{"aa","bb"});
setList.add(new String[]{"cc","dd"});
setList.add(new String[]{"ee","ff"});//使用map方法
setList.stream().map(s->Arrays.stream(s)).forEach(s-> System.out.println("map==" + s));//使用flatMap方法
setList.stream().flatMap(s->Arrays.stream(s)).forEach(s-> System.out.println("flatMap==" + s));
map就是將數(shù)組流直接返回,flatMap是將數(shù)組流中的每個(gè)元素都返回。
.distinct:將集合中的元素去重。
List<String> disList = Arrays.asList("aaa","ddd","bbb","ddd","aaa");
disList.stream().distinct().forEach(s-> System.out.println(s));
sorted:將集合中的元素排序。
List<Integer> integerList = Arrays.asList(2,4,1,3); integerList.stream().sorted().forEach(s-> System.out.println(s));
可以按照自定義排序:
List<Integer> integerList = Arrays.asList(2,4,1,3); integerList.stream().sorted((s1,s2)->s2.compareTo(s1)).forEach(s-> System.out.println(s));
peek:生成一個(gè)包含原Stream的所有元素的新Stream,同時(shí)會(huì)提供一個(gè)消費(fèi)函數(shù)即引用的方法A,當(dāng)Stream每個(gè)元素被消費(fèi)的時(shí)候都會(huì)先
執(zhí)行新Stream給定的方法A。peek是中間操作,如果peek后沒(méi)有最終操作,則peek不會(huì)執(zhí)行。
List<Integer> integerList = Arrays.asList(1,2,3,4);
integerList.stream().peek(s-> System.out.println("peek = "+s)).forEach(s-> System.out.println("forEach = "+s));
limit:返回Stream的前n個(gè)元素。
List<Integer> integerList = Arrays.asList(1,2,3,4); integerList.stream().limit(2).forEach(s-> System.out.println(s));
skip:刪除Stream的前n個(gè)元素。
List<Integer> integerList = Arrays.asList(1,2,3,4); integerList.stream().skip(2).forEach(s-> System.out.println(s));
終端操作(terminal)
1.forEach:遍歷Stream中的每個(gè)元素,前面每個(gè)例子都有使用,此處不再演示。
List<Integer> integerList = Arrays.asList(1,2,3,4); integerList.stream().skip(2).forEach(s-> System.out.println(s));
forEachOrdered:遍歷Stream中的每個(gè)元素。
區(qū)別: 在串行流(stream)中沒(méi)有區(qū)別,在并行流(parallelStream)中如果數(shù)據(jù)源是有序集合,forEachOrdered輸出順序與數(shù)據(jù)源中順序
一致,forEach則是亂序。
List<Integer> integerList = Arrays.asList(1,2,3,4); integerList.parallelStream().forEachOrdered(s-> System.out.println(s));
toArray:將流轉(zhuǎn)換為Object[]或者指定類型的數(shù)組。
List<Integer> integerList = Arrays.asList(1,2,3,4); Object[] array = integerList.stream().toArray(); String[] strArr = integerList.stream().toArray(String[]::new);
Stream中的toArray普通情況下和集合中的toArray沒(méi)什么區(qū)別,但是Stream中的toArray轉(zhuǎn)換為指定類型的數(shù)組。
reduce:將集合中的每個(gè)元素聚合成一條數(shù)據(jù)。有三種情況:
reduce(BinaryOperator accumulator):此處需要一個(gè)參數(shù),返回Optional對(duì)象:
Optional reduce = integerList.stream().reduce((a, b) -> a + b);
reduce(T identity, BinaryOperator accumulator):此處需要兩個(gè)參數(shù),第一個(gè)參數(shù)為起始值,第二個(gè)參數(shù)為引用的方法
從起始值開始,每個(gè)元素執(zhí)行一次引用的方法(方法引用的中的兩個(gè)參數(shù):第一個(gè)參數(shù)為上個(gè)元素執(zhí)行方法引用的結(jié)果,第二個(gè)參數(shù)為當(dāng)前元素)。
List<Integer> integerList = Arrays.asList(1,2,3,4); int integer = integerList.stream().reduce(5,(a, b) -> a + b); System.out.println(integer);
此例中使用起始值為5,對(duì)集合中每個(gè)元素求和,可以理解為:5+1+2+3+4=15。
**reduce:**此處需要三個(gè)參數(shù)。此方法用在并發(fā)流(parallelStream)中,啟動(dòng)多個(gè)子線程使用accumulator進(jìn)行并行計(jì)算,最終使用combiner對(duì)子線程結(jié)果進(jìn)行合并,返回identity類型的數(shù)據(jù)。
collect:將流轉(zhuǎn)換成集合或聚合元素。有兩種情況。接受一個(gè)參數(shù)和接受三個(gè)參數(shù)(三個(gè)參數(shù)在并發(fā)流parallelStream中使用),此處介紹一個(gè)參數(shù)的情況,單個(gè)參數(shù)接受的參數(shù)類型為Collector,Collectors 類實(shí)現(xiàn)了很多歸約操作
List<Integer> integerList = Arrays.asList(2,4,1,3);List<Integer> integers = integerList.stream().filter(s -> s > 1).collect(Collectors.toList());System.out.println(integers.toString());
此處統(tǒng)計(jì)集合中大于1的元素并最終返回list。
min:獲取集合中最小值。
List<Integer> integerList = Arrays.asList(2,4,1,3);Integer min = integerList.stream().min(Integer::compareTo).get();System.out.println(min);
max:獲取集合中最大值。
List<Integer> integerList = Arrays.asList(2,4,1,3);Integer max = integerList.stream().max(Integer::compareTo).get();System.out.println(max);
count:獲取集合中元素個(gè)數(shù)
List<Integer> integerList = Arrays.asList(2,4,1,3); long count = integerList.stream().count();System.out.println(count);
到此這篇關(guān)于Java入門到精通的學(xué)習(xí)筆記:零基礎(chǔ)如何快速學(xué)會(huì)Java開發(fā)的文章就介紹到這了,更多相關(guān)Java實(shí)用學(xué)習(xí)筆記內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談MyBatis-Plus學(xué)習(xí)之Oracle的主鍵Sequence設(shè)置的方法
這篇文章主要介紹了淺談MyBatis-Plus學(xué)習(xí)之Oracle的主鍵Sequence設(shè)置的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
JavaCV實(shí)現(xiàn)讀取視頻信息及自動(dòng)截取封面圖詳解
javacv可以幫助我們?cè)趈ava中很方便的使用OpenCV以及FFmpeg相關(guān)的功能接口。本文將利用Javacv實(shí)現(xiàn)在視頻網(wǎng)站中常見(jiàn)的讀取視頻信息和自動(dòng)獲取封面圖的功能,感興趣的可以了解一下2022-06-06
SpringBoot切面實(shí)現(xiàn)token權(quán)限校驗(yàn)詳解
這篇文章主要介紹了SpringBoot切面實(shí)現(xiàn)token權(quán)限校驗(yàn)詳解,要實(shí)現(xiàn)權(quán)限校驗(yàn),首先數(shù)據(jù)表和實(shí)體類上需要有權(quán)限字段,我的表中permission和gender是通過(guò)外鍵約束permission表和gender表實(shí)現(xiàn)枚舉的,因?yàn)榭赏卣剐愿?需要的朋友可以參考下2024-01-01
解決mybatisplus MetaObjectHandler 失效的問(wèn)題
本文主要介紹了解決mybatisplus MetaObjectHandler 失效的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Java實(shí)現(xiàn)List分組的常見(jiàn)方法詳解
這篇文章主要為大家詳細(xì)介紹了使用Java實(shí)現(xiàn)List分組的幾個(gè)常見(jiàn)方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-12-12
Spring事件監(jiān)聽器ApplicationListener的使用與原理分析
文章介紹了Spring中的事件監(jiān)聽器和事件發(fā)布機(jī)制,包括內(nèi)置事件的使用和自定義事件的創(chuàng)建與發(fā)布,以及如何使用@EventListener注解進(jìn)行事件監(jiān)聽,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-11-11
基于Spring Boot 的小區(qū)人臉識(shí)別與出入記錄管理系統(tǒng)功能
文章介紹基于SpringBoot框架與百度AI人臉識(shí)別API的小區(qū)出入管理系統(tǒng),實(shí)現(xiàn)自動(dòng)識(shí)別、記錄及查詢功能,涵蓋技術(shù)選型、數(shù)據(jù)模型設(shè)計(jì)、接口開發(fā)與系統(tǒng)優(yōu)化方案,為智慧社區(qū)提供高效安全管理工具,感興趣的朋友跟隨小編一起看看吧2025-08-08
詳解springboot中各個(gè)版本的redis配置問(wèn)題
這篇文章主要介紹了詳解springboot中各個(gè)版本的redis配置問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Spring Boot分層架構(gòu)詳解之從Controller到Service再到Mapper的完整流程(用戶管理
本文將以一個(gè)實(shí)際案例(用戶管理系統(tǒng))為例,詳細(xì)解析 Spring Boot 中 Controller、POJO、Mapper、Service、ServiceImpl 等層的關(guān)系,并展示從前端發(fā)送請(qǐng)求到后端處理的完整流程,感興趣的朋友跟隨小編一起看看吧2025-09-09

