博主介紹: 大家好,我是想成為Super的Yuperman,互聯網宇宙廠經驗,17年醫療健康行業的碼拉松奔跑者,曾擔任技術專家、架構師、研發總監負責和主導多個應用架構。
近期專注: DeepSeek應用,RPA應用研究,主流廠商產品使用,開源RPA 應用等
技術范圍: java體系,軟件架構,DDD,多年Golang、.Net、Oracle等經驗
業務范圍: 對傳統業務應用技術轉型,從數字醫院到區域醫療,從院內業務系統到互聯網醫院及健康服務,從公立醫院到私立醫院都有一些經歷及理解
*** 為大家分享一些思考與積累,歡迎持續關注公眾號:【火星求索】 ***
背景知識
Java 相關概念
- JavaSE (Java Standard Edition): 基礎版,用于開發桌面應用程序。
- JavaEE (Java Enterprise Edition): 企業版,用于開發企業級應用程序。
- JavaME (Java Micro Edition): 微型版,用于開發嵌入式系統和移動設備應用程序。
編譯與運行
-
編譯階段:
- 源文件:
.java
文件。 - 字節碼文件:
.class
文件。 - 編譯工具:
javac.exe
,用于將.java
文件編譯為.class
文件。- 命令:
javac 文件名.java
- 編譯包:
javac -d 編譯后存放路徑 java源文件路徑
- 命令:
- 源文件:
-
運行階段:
- 運行工具:
java.exe
,用于運行.class
文件。- 命令:
java 類名
(不帶.class
后綴)
- 命令:
- JVM (Java Virtual Machine): Java 虛擬機,負責執行字節碼文件。
- 運行工具:
開發環境
- JDK (Java Development Kit): Java 開發工具包,包含編譯器、調試器等開發工具。
- JRE (Java Runtime Environment): Java 運行環境,包含 JVM 和運行 Java 程序所需的庫。
- JVM (Java Virtual Machine): Java 虛擬機,負責執行字節碼文件。
工具與格式
- native2ascii: 用于將 Unicode 字符轉換為
\u
表示的 ASCII 格式。 - UML (Unified Modeling Language): 面向對象設計圖,用于表示類、接口、繼承、實現等關系。
- 空心箭頭: 指向父類(繼承)。
- 空心虛線箭頭: 指向接口(實現)。
- 實心實線箭頭: 表示關聯關系。
注釋
- 單行注釋:
//
- 多行注釋:
/* */
- 文檔注釋:
/** */
,用于生成幫助文檔。
類與方法結構
類體 {方法體 {java語句;}
}AI寫代碼java運行
總結
- JavaSE 是基礎版,JavaEE 是企業版,JavaME 是微型版。
- 編譯 使用
javac
,運行 使用java
。 - JDK 是開發工具包,JRE 是運行環境,JVM 是虛擬機。
- UML 用于面向對象設計,注釋 用于代碼說明。
- 類與方法 的基本結構如上所示。
Java SE API 和文檔
一、集成開發環境(IDEA)
以下是用戶提供的快捷鍵和組織方式的總結:
組織方式
- Project(工程): 最高層級,包含多個模塊。
- Module(模塊): 工程下的子模塊,包含多個包。
- Package(包): 模塊下的子包,用于組織類和資源。
字體設置
- 路徑:
File -> Settings -> Font
用于調整編輯器的字體樣式和大小。
快捷鍵分類總結
導航與操作
-
展開/移動列表:
- 左右箭頭: 展開或折疊列表。
- 上下箭頭: 在列表中移動。
-
切換與定位:
- Alt+左右箭頭: 切換 Java 程序。
- Alt+上下箭頭: 在方法間快速移動。
- Alt+標號: 打開標號窗口。
- Ctrl+G: 定位到文件的某一行。
- Ctrl+點擊: 切換源碼。
- Ctrl+H: 查看實現類。
-
查找與搜索:
- Ctrl+Shift+N: 查找文件。
- Ctrl+N: 查找類文件。
- Ctrl+F12: 在當前類中查找一個方法。
編輯與格式化
-
代碼編輯:
- Ctrl+Y: 刪除一行。
- Shift+F6: 重命名。
- Alt+拖動: 一次編輯多行。
- Ctrl+Alt+T: 將選中的代碼放在
TRY{}
、IF{}
、ELSE{}
中。
-
代碼提示與自動補全:
- Ctrl+空格: 代碼提示。
- Ctrl+P: 方法參數提示。
- Ctrl+J: 自動代碼。
- Ctrl+Alt+Space: 類名或接口名提示。
-
格式化與優化:
- Ctrl+Alt+L: 格式化代碼。
- Ctrl+Alt+I: 自動縮進。
- Ctrl+Alt+O: 優化導入的類和包。
運行與糾錯
-
運行程序:
- Ctrl+Shift+F10: 運行當前程序。
-
糾錯與提示:
- Alt+回車: 糾錯提示。
窗口操作
- 全屏模式:
- Ctrl+Shift+F12: 切換全屏模式。
總結
- 組織方式: 工程 -> 模塊 -> 包,層級清晰,便于管理。
- 快捷鍵:
- 導航與查找:快速定位文件、類、方法。
- 編輯與格式化:提高代碼編寫效率。
- 運行與糾錯:快速運行程序并修復錯誤。
- 窗口操作:優化開發環境布局。
二、JVM內存劃分
局部變量在方法體中聲明,運行階段內存在棧中分配
方法區內存:字節碼文件在加載 的時候將其放在方法區之中(最先有數據,調用方法時在棧內分配空間)
堆內存(heap):new對象(成員變量中的實例變量(一個對象一份)在java對象內部存儲),只能通過引用調用操作
棧(stack)內存:棧幀永遠指向棧頂元素,棧頂元素處于活躍狀態,先進后出,后進先出(存儲局部變量)
內存區域與數據存儲
-
堆內存(Heap):
- 存儲實例變量(對象屬性)。
- 每個 JVM 實例只有一個堆內存,所有線程共享。
- 垃圾回收器(GC)主要針對堆內存進行回收。
-
方法區(Method Area):
- 存儲靜態變量(類變量)和類元數據(如類信息、常量池等)。
- 每個 JVM 實例只有一個方法區,所有線程共享。
- 方法區是最先有數據的內存區域,因為類加載時靜態變量和類信息會初始化。
-
棧內存(Stack):
- 存儲局部變量和方法調用棧幀。
- 每個線程有一個獨立的棧內存,線程私有。
- 棧內存是使用最頻繁的內存區域,因為方法調用和局部變量的生命周期較短。
變量存儲位置
-
局部變量:
- 存儲在棧內存中。
- 生命周期與方法調用一致,方法結束時局部變量會被銷毀。
-
實例變量:
- 存儲在堆內存中。
- 生命周期與對象一致,對象被垃圾回收時實例變量會被銷毀。
-
靜態變量:
- 存儲在方法區中。
- 生命周期與類一致,類卸載時靜態變量會被銷毀。
垃圾回收器(GC)
-
主要目標:
- 垃圾回收器主要針對堆內存進行回收,清理不再使用的對象。
- 棧內存和方法區的垃圾回收機制與堆內存不同。
-
特點:
- 堆內存是垃圾回收的主要區域,因為對象生命周期較長且占用內存較大。
- 棧內存和方法區的垃圾回收效率較高,因為它們的生命周期較短且數據量相對較小。
三、關鍵字:
類與關鍵字
-
public
:- 表示公開的類,類名必須與文件名一致,且一個文件中只能有一個
public
類。
- 表示公開的類,類名必須與文件名一致,且一個文件中只能有一個
-
class
:- 用于定義一個類。
-
static
:- 表示靜態的,修飾的成員變量或方法屬于類級別,不依賴于對象。
- 靜態變量在類加載時初始化,存儲在方法區內存中。
- 靜態方法不能訪問實例變量或實例方法,需要通過對象訪問。
-
break
:- 用于跳出循環或
switch
語句。
- 用于跳出循環或
-
continue
:- 用于跳過當前循環的剩余部分,直接進入下一次循環。
- 語法:
continue 循環名稱;
或循環名稱:
。
-
this
:- 表示當前對象的引用。
- 用于區分局部變量和實例變量,或在構造方法中調用其他構造方法(
this(實參)
)。 - 不能用于靜態方法中。
-
native
:- 用于調用 JVM 本地程序。
輸入與輸出
-
System.out.println()
:- 控制臺輸出,
println
表示輸出并換行。
- 控制臺輸出,
-
鍵盤輸入:
- 創建鍵盤掃描器對象:
java.util.Scanner s = new java.util.Scanner(System.in);
- 字符串輸入:
String user = s.next();
- 整數輸入:
int num = s.nextInt();
- 創建鍵盤掃描器對象:
final
關鍵字
-
修飾類:
- 類不能被繼承。
-
修飾方法:
- 方法不能被重寫。
-
修飾變量:
- 變量不能被修改。
- 修飾的成員變量必須手動賦值。
- 修飾的引用一旦指向一個對象,就不能指向其他對象,但所指向的內存可以修改。
-
常量:
- 定義常量:
public static final 類型 常量名 = 值;
- 命名規則:全部大寫,用下劃線分隔。
- 定義常量:
super
關鍵字
-
作用:
- 代表當前對象的父類型特征。
- 用于訪問父類的屬性、方法或調用父類的構造方法。
-
語法:
- 訪問父類屬性或方法:
super.
- 調用父類構造方法:
super()
- 訪問父類屬性或方法:
-
規則:
- 不能用于靜態方法中。
- 如果父類和子類有同名屬性,訪問父類屬性時不能省略
super
。 - 構造方法的第一行如果沒有
this()
或super()
,默認會調用super()
。
static
關鍵字
-
靜態變量:
- 屬于類級別,不依賴于對象,類加載時初始化。
-
靜態方法:
- 類級別的方法,不能訪問實例變量或實例方法。
-
靜態代碼塊:
- 在類加載時執行,只執行一次。
- 語法:
static {}
-
實例代碼塊:
- 在構造方法執行之前執行,用于對象初始化。
包與導入
-
package
:- 用于管理類,命名規則:公司域名倒序.項目名.模塊名.功能名。
- 語法:
package 包名;
-
import
:- 用于導入包中的類。
- 語法:
import 包名.類名;
或import 包名.*;
java.lang.*
是核心語言包,無需導入。
-
快捷鍵:
Ctrl+Shift+O
:自動導入。
訪問控制權限修飾符
-
private
:- 私有訪問權限,只能在本類中訪問。
-
default
:- 默認訪問權限,可以被本包中的其他類訪問。
-
protected
:- 受保護的訪問權限,可以被本包及不同包的子類訪問。
-
public
:- 公共訪問權限,可以在任何地方訪問。
-
類的修飾符:
- 類只能使用
public
或默認修飾符(缺省),內部類除外。
- 類只能使用
總結
- 類與關鍵字:
public
、class
、static
、this
、super
等關鍵字的作用與用法。 - 輸入與輸出:控制臺輸出與鍵盤輸入的基本操作。
final
:用于修飾類、方法、變量,表示不可修改。static
:修飾類級別的成員,與對象無關。- 包與導入:
package
和import
的使用及命名規則。 - 訪問控制權限:
private
、default
、protected
、public
的訪問范圍。
四、Java基礎
以下是用戶提供的內容的總結:
標識符
-
定義:
- 用戶有權命名的單詞,包括類名、方法名、常量名、變量名、接口名等。
-
命名規則:
- 類名、接口名: 首字母大寫,后面每個單詞首字母大寫(大駝峰命名法)。
- 方法名、變量名: 首字母小寫,后面每個單詞首字母大寫(小駝峰命名法)。
- 常量名: 全部大寫,單詞間用下劃線分隔。
字面值
- 定義: 數據本身,如數字、字符串等,通常以紫色顯示。
變量
-
局部變量:
- 定義在方法體內,沒有默認值,必須手動初始化。
- 生命周期與方法調用一致。
-
成員變量:
- 定義在類體內,有默認值(數值類型為 0,布爾類型為
false
,引用類型為null
)。 - 分為實例變量和靜態變量。
- 定義在類體內,有默認值(數值類型為 0,布爾類型為
-
實例變量:
- 不帶
static
關鍵字,屬于對象級別。 - 必須通過對象引用訪問(
引用.變量名
)。 - 存儲在堆內存中。
- 不帶
-
靜態變量:
- 帶
static
關鍵字,屬于類級別。 - 在類加載時初始化,存儲在方法區內存中。
- 通過類名訪問(
類名.變量名
)。
- 帶
引用
- 定義: 是一個變量,可以是實例變量或局部變量。
- 實例變量:
類名 引用 = new 類名();
- 局部變量:
引用 變量名 = new 引用();
- 實例變量:
數據類型
-
基本數據類型:
- 整數型:
byte
(1 字節)、short
(2 字節)、int
(4 字節)、long
(8 字節,后綴L
)。 - 浮點型:
float
(4 字節)、double
(8 字節)。 - 布爾型:
boolean
(1 字節)。 - 字符型:
char
(2 字節)。
- 整數型:
-
引用數據類型:
- 字符串:
String
,不可變,存儲在方法區字符串池中。
- 字符串:
-
比較:
- 基本數據類型使用
==
判斷相等。 - 引用數據類型(包括
String
)使用equals
判斷相等。
- 基本數據類型使用
字符編碼
- 發展順序: ASCII < ISO-8859-1 < GB2312 < GBK < GB18030 < Big5 < Unicode(統一全球編碼)。
位運算符
- 邏輯異或(^): 兩邊不一樣為真。
- 短路與(&&): 左邊為假時直接返回假。
- 按位與(&): 將操作數轉換為二進制后按位與。
- 短路或(||): 左邊為真時直接返回真。
- 左移(<<): 二進制數據左移,相當于乘以 2 的 N 次方。
- 右移:
- 帶符號右移(>>): 正數用 0 填充,負數用 1 填充。
- 無符號右移(>>>): 無論正負都用 0 填充。
- 按位取反(~): 將二進制每一位取反,結果為
-(n+1)
。
方法(函數)
-
定義:
[修飾符列表] 返回值類型 方法名(形參列表) {方法體;return; // return 后不能跟語句 }AI寫代碼java運行
-
調用:
類名.方法名(實參列表);
-
實例方法: 不帶
static
,需要對象參與。 -
靜態方法: 帶
static
,與對象無關。
方法重載(Overload)
- 定義: 在同一類中,方法名相同但參數列表不同。
- 特點: 與返回值類型和修飾符列表無關。
方法遞歸
- 定義: 方法調用自身,每次遞歸都會分配新的內存空間(壓棧)。
-
示例:
public static int sum(int n) {if (n == 1) {return 1;}return n + sum(n - 1); }AI寫代碼java運行
方法覆蓋(Override)
- 定義: 發生在繼承關系中,子類重寫父類的方法。
- 規則:
- 方法名、返回值類型、形參列表必須與父類一致。
- 訪問權限不能比父類更低,拋出異常不能更多。
- 限制:
- 私有方法、構造方法不能覆蓋。
- 靜態方法不存在覆蓋。
總結
- 標識符: 命名規則與用途。
- 變量: 局部變量、實例變量、靜態變量的定義與存儲位置。
- 數據類型: 基本數據類型與引用數據類型的區別。
- 位運算符: 各種位運算符的作用與用法。
- 方法: 定義、調用、重載、遞歸與覆蓋的規則與特點。
五、Java 控制流與 Lambda 表達式
1. 控制流語句
-
If-Else 語句:
if (條件) {// 語句 } else if (表達式) {// 語句 } else {// 語句 }AI寫代碼java運行
-
Switch 語句:
switch (關鍵詞) {case 關鍵詞:// java語句break;default:// 默認語句 }AI寫代碼java運行
-
For 循環:
for (初始表達式; 布爾表達式; 更新循環體) {// 循環體 }AI寫代碼java運行
-
增強 For 循環(For Each):
for (元素類型 變量名 : 數組或集合) {System.out.println(變量名); }AI寫代碼java運行
-
While 循環:
while (表達式) {// 循環體 }AI寫代碼java運行
-
Do-While 循環:
do {// 循環體 } while (布爾表達式);AI寫代碼java運行
2. Java 標簽
- 標簽用于控制嵌套循環的跳轉和中斷。
- 語法:
label:
- 用法:
continue label;
:跳過當前循環,繼續執行標簽處的循環。break label;
:結束標簽處的循環,執行循環后的代碼。
3. Lambda 表達式
-
實現 Runnable:
// Java 8 之前 new Thread(new Runnable() {@Overridepublic 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();AI寫代碼java運行
-
事件處理:
// Java 8 之前 JButton show = new JButton("Show"); show.addActionListener(new ActionListener() {@Overridepublic 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"); });AI寫代碼java運行
-
列表迭代:
// 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);AI寫代碼java運行
-
Map 和 Reduce:
// 不使用 lambda 表達式 List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500); for (Integer cost : costBeforeTax) {double price = cost + .12 * cost;System.out.println(price); }// 使用 lambda 表達式 costBeforeTax.stream().map((cost) -> cost + .12 * cost).forEach(System.out::println);// 使用 reduce 計算總和 double bill = costBeforeTax.stream().map((cost) -> cost + .12 * cost).reduce((sum, cost) -> sum + cost).get(); System.out.println("Total : " + bill);AI寫代碼java運行
-
對列表的每個元素應用函數:
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);AI寫代碼java運行
-
計算集合元素的最大值、最小值、總和以及平均值:
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());AI寫代碼java運行
總結
- 控制流語句:用于控制程序的執行流程,包括條件判斷、循環等。
- Java 標簽:用于控制嵌套循環的跳轉和中斷。
- Lambda 表達式:簡化了匿名類的使用,使代碼更簡潔,特別是在實現函數式接口(如
Runnable
、ActionListener
)時非常有用。 - Stream API:提供了強大的集合操作功能,如
map
、reduce
、forEach
等,使得對集合的處理更加高效和簡潔。
六、面向對象
面向過程與面向對象的對比
-
面向過程:
- 因果關系:關注問題的具體步驟和流程。
- 具體過程:強調如何一步步解決問題。
- 耦合度高:各個模塊之間依賴性強,修改一個模塊可能會影響其他模塊。
- 軟件拓展性差:由于耦合度高,系統的擴展和維護較為困難。
-
面向對象:
- 分類對象:將問題分解為多個對象,每個對象負責特定的功能。
- 關系層度低:對象之間的依賴關系較弱,耦合度低。
- 關注對象功能:關注對象能完成哪些功能,而不是具體的實現步驟。
- 三大特征:
- 封裝性:將復雜的事務封裝起來,只保留簡單的操作入口。封裝后形成獨立的對象,提高了代碼的復用性、適應性和安全性。
- 繼承性:實現代碼復用,最重要的是支持多態和方法覆蓋。
- 多態性:父類型的引用可以指向子類型對象,降低程序耦合度,提高擴展力。
面向對象的分析與設計
- 面向對象的分析(OOA):分析問題域,識別對象及其關系。
- 面向對象的設計(OOD):設計對象的結構和行為,定義類及其關系。
- 面向對象的編程(OOP):使用編程語言實現設計,創建對象并實現其功能。
類與對象
- 類:高度抽象的對象的集合,是一個模板。
- 靜態代碼塊:類加載時執行。
- 實例代碼塊:實例化時執行。
- 靜態變量:類級別的變量。
- 實例變量:對象級別的變量,存儲在堆內存中。
- 構造方法:創建對象時調用,用于初始化實例變量。
- 靜態方法:類級別的方法。
- 實例方法:對象級別的方法。
- 成員變量:對象的屬性,描述對象的狀態。
- 成員方法:對象的行為,描述對象的動作。
- 對象:類的具體實例。
- 創建對象:
類名 對象名稱 = new 類名();
- 使用對象:
對象名稱.屬性名
或對象名稱.方法名()
- 修改對象:
引用.變量名 = 值
- 引用與對象:引用保存了對象的地址,指向堆內存中的對象。多個引用可以指向同一個對象,但一個引用只能指向一個對象。
- 創建對象:
User u=new User();
Address a=new Address();
u.addr=a;
Print(u.addr.city);
A.city=”天津”;
Print(u.addr.city); AI寫代碼java運行
封裝
- 私有化屬性:使用
private
關鍵字將屬性私有化。 - 提供操作入口:通過
getter
和setter
方法提供對屬性的訪問和修改。- 讀取屬性:
public 數據類型 get屬性名() { return 屬性; }
- 修改屬性:
public void set屬性名(數據類型 屬性) { this.屬性 = 屬性; }
- 讀取屬性:
- 業務邏輯控制:在
setter
方法中添加業務邏輯進行安全控制。
構造方法
- 作用:創建對象并初始化實例變量。
- 語法:
修飾符 構造方法名(形參) { 構造方法體; this.實例變量 = 形參; }
- 特點:沒有返回值類型,方法名與類名一致,不能使用
return
返回值,但可以使用return
結束方法。 - 調用:
new 構造方法名(實參)
- 缺省構造器:如果沒有定義構造方法,編譯器會自動生成一個無參的缺省構造器。
繼承
- 語法:
[修飾符列表] class 子類名 extends 父類名 { 類體 = 屬性 + 方法 }
- 單繼承:Java中類只能繼承一個父類。
- 繼承關系:
- 父類:也稱為基類、超類、
superclass
。 - 子類:也稱為派生類、
subclass
。
- 父類:也稱為基類、超類、
- 不可繼承:私有的屬性和方法、構造方法。
- 間接繼承:通過繼承鏈,子類可以間接繼承父類的父類。
- 默認繼承:如果沒有顯式繼承任何類,默認繼承
java.lang.Object
類。 - super關鍵字:用于調用父類的屬性、方法和構造方法。
多態
- 向上轉型(Upcasting):子類轉換為父類型,自動類型轉換。
- 語法:
父類 引用 = new 子類();
- 特點:編譯通過,運行沒有問題。
- 語法:
- 向下轉型(Downcasting):父類轉換為子類,強制類型轉換。
- 語法:
子類 引用 = (子類) 父類引用;
- 特點:存在隱患,可能導致
ClassCastException
異常。
- 語法:
- 動態綁定:父類型引用指向子類型對象,調用方法時實際執行的是子類的方法。
- instanceof運算符:用于在強制轉換前檢查對象的類型,避免
ClassCastException
異常。- 語法:
引用 instanceof 數據類型名
- 返回值:布爾類型,
true
表示引用指向的對象是后面的數據類型,false
表示不是。
- 語法:
以下是關于 抽象類 和 接口 的總結:
抽象類
-
定義:
- 使用
abstract
關鍵字修飾的類,是類的進一步抽象。 - 屬于引用數據類型。
- 使用
-
語法:
[修飾符列表] abstract class 類名 {}AI寫代碼java運行
-
特點:
- 不能使用
private
或final
修飾。 - 抽象類可以包含抽象方法和非抽象方法。
- 抽象類的子類可以是抽象類或非抽象類。
- 不能實例化(不能創建對象),但可以有構造方法,供子類使用。
- 不能使用
-
抽象方法:
- 使用
abstract
關鍵字修飾,無方法體。 - 語法:
[修飾符列表] abstract 返回值類型 方法名();
- 包含抽象方法的類一定是抽象類。
- 使用
-
規則:
- 抽象類不一定有抽象方法,但抽象方法必須出現在抽象類中。
- 非抽象類繼承抽象類時,必須實現所有抽象方法。
接口
-
定義:
- 使用
interface
關鍵字定義,是完全抽象的(特殊的抽象類)。 - 屬于引用數據類型。
- 使用
-
語法:
[修飾符列表] interface 接口名 {}AI寫代碼java運行
-
特點:
- 接口中只能包含常量和抽象方法(默認
public static final
和public abstract
,修飾符可省略)。 - 支持多繼承,一個接口可以繼承多個接口。
- 接口不能繼承抽象類。
- 接口中只能包含常量和抽象方法(默認
-
方法類型:
- 抽象方法:
abstract
修飾(可省略)。 - 默認方法:
default
修飾,提供默認實現。 - 靜態方法:
static
修飾,通過接口名調用。
- 抽象方法:
-
實現:
- 類通過
implements
關鍵字實現接口。 - 非抽象類實現接口時,必須重寫所有抽象方法。
- 一個類可以實現多個接口。
- 類通過
-
多態:
- 接口支持多態:
父類型引用指向子類對象
。 - 示例:
接口名 引用 = new 實現類();
- 接口支持多態:
-
作用:
- 解耦合:調用者面向接口調用,實現者面向接口編寫實現。
- 擴展性強:接口+多態可以降低程序耦合度。
抽象類與接口的區別
特性 | 抽象類 | 接口 |
---|---|---|
抽象程度 | 半抽象(可以包含具體方法) | 完全抽象(只能包含抽象方法) |
構造方法 | 有構造方法,供子類使用 | 無構造方法 |
繼承 | 單繼承(一個類只能繼承一個抽象類) | 支持多繼承(一個類可以實現多個接口) |
內容 | 可以包含抽象方法和非抽象方法 | 只能包含常量和抽象方法 |
用途 | 抽象行為和數據 | 主要抽象行為 |
實例化 | 不能實例化 | 不能實例化 |
開發中的選擇
-
抽象類:
- 當多個類有共同的屬性和行為,且需要部分具體實現時使用。
- 適合定義“是什么”(
is-a
關系)。
-
接口:
- 當需要定義一組行為規范,且不關心具體實現時使用。
- 適合定義“能做什么”(
like-a
關系)。
示例
-
抽象類:
abstract class Animal {abstract void sound();void sleep() {System.out.println("Sleeping...");} }AI寫代碼java運行
-
接口:
interface Flyable {void fly(); }AI寫代碼java運行
-
實現與繼承:
class Bird extends Animal implements Flyable {@Overridevoid sound() {System.out.println("Chirp...");}@Overridepublic void fly() {System.out.println("Flying...");} }AI寫代碼java運行
總結
-
面向對象編程通過封裝、繼承和多態三大特征,提高了代碼的復用性、擴展性和維護性。
-
類與對象是面向對象編程的基礎,類是對對象的抽象,對象是類的實例。
-
封裝通過私有化屬性和提供操作入口,增強了代碼的安全性和可控性。
-
繼承實現了代碼的復用,并支持多態和方法覆蓋。
-
多態通過向上轉型和向下轉型,降低了程序的耦合度,提高了擴展力。
-
面向抽象編程,而不是面向具體,可以進一步降低耦合度,提高系統的靈活性和可擴展性。
-
抽象類 用于定義類的共有特征,支持部分具體實現。
-
接口 用于定義行為規范,支持多繼承和解耦合。
-
在實際開發中,根據需求選擇抽象類或接口,合理使用可以提高代碼的擴展性和可維護性。
七、類庫
源碼、字節碼與幫助文檔
-
源碼:
- 理解程序:源碼是程序員編寫的原始代碼,用于理解程序的邏輯和功能。
-
字節碼:
- 程序開發使用:字節碼是源碼編譯后的中間代碼,由JVM執行。它是跨平臺的,可以在任何支持JVM的系統上運行。
-
幫助文檔:
- 對開發提供幫助:幫助文檔是開發者的參考指南,通常通過
javadoc
生成。 - 注意使用版本同一:確保使用的幫助文檔與代碼版本一致,避免因版本差異導致的錯誤。
- 對開發提供幫助:幫助文檔是開發者的參考指南,通常通過
Object類(根類)
Object
是Java中所有類的根類,提供了一些核心方法:
-
protected Object clone()
:- 負責對象克隆,返回對象的副本。
-
boolean equals(Object obj)
:- 判斷兩個對象是否相等。默認比較引用地址,通常需要重寫以比較對象內容。
-
int hashCode()
:- 返回對象的哈希代碼值,用于哈希表等數據結構。
-
String toString()
:- 返回對象的字符串表示形式。默認返回類名@哈希值,通常需要重寫以提供更有意義的信息。
-
protected void finalize() throws Throwable
:- 垃圾回收器負責調用,用于對象銷毀前的清理工作。
-
System.gc()
:- 建議啟動垃圾回收器,但不保證立即執行。
System類
System
類提供了一些系統級別的操作:
-
System.gc()
:- 建議啟動垃圾回收器。
-
System.out
:- 靜態變量,用于控制臺輸出。
-
System.out.print()
:- 輸出打印不換行。
-
System.out.println()
:- 換行輸出。
-
System.currentTimeMillis()
:- 獲取自1970年1月1日00:00:00到當前系統時間的總毫秒數。
-
System.exit(0)
:- 退出JVM。
Arrays類
Arrays
是數組工具類,提供了一些常用方法:
-
Arrays.sort(arr)
:- 對數組進行排序。
-
Arrays.binarySearch(arr, key)
:- 使用二分法查找元素,不存在時返回-1。
String類
String
類用于操作字符串,提供了豐富的構造方法和方法:
-
構造方法:
String(byte[] byte)
:將字節數組轉換為字符串。String(char[] char)
:將字符數組轉換為字符串。String(String string)
:復制字符串。
-
常用方法:
char charAt(int index)
:返回指定索引的字符。int compareTo(String string)
:字典比較大小。boolean contains(String string)
:判斷是否包含指定字符串。boolean endsWith(String string)
:判斷是否以指定字符串結尾。boolean startsWith(String prefix)
:判斷是否以指定前綴開頭。boolean equals(Object anObject)
:比較字符串內容。boolean equalsIgnoreCase(String anotherString)
:忽略大小寫比較。byte[] getBytes()
:將字符串轉換為字節數組。int indexOf(String str)
:返回子字符串第一次出現的索引。int lastIndexOf(String str)
:返回子字符串最后一次出現的索引。boolean isEmpty()
:判斷字符串是否為空。String replace(CharSequence target, CharSequence replacement)
:替換字符串。String substring(int beginIndex)
:截取字符串。char[] toCharArray()
:將字符串轉換為字符數組。String toLowerCase()
:將字符串轉換為小寫。String toUpperCase()
:將字符串轉換為大寫。String[] split(String regex)
:按正則表達式拆分字符串。String trim()
:去除前后空白。static String valueOf()
:將其他類型轉換為字符串。
StringBuffer與StringBuilder
-
StringBuffer:
- 線程安全,適用于多線程環境。
- 常用方法:
append()
、reverse()
。
-
StringBuilder:
- 非線程安全,性能優于
StringBuffer
。
- 非線程安全,性能優于
包裝類
包裝類用于將基本數據類型轉換為對象:
- 常用包裝類:
Integer
、Character
等。
- 常用方法:
int intValue()
:拆箱,將包裝類轉換為基本類型。static int parseInt(String s)
:將字符串轉換為整數。
日期相關類
-
java.util.Date
:- 表示日期和時間。
-
SimpleDateFormat
:- 用于格式化日期。
- 常用方法:
format()
、parse()
。
數字相關類
-
DecimalFormat
:- 用于格式化數字。
-
BigDecimal
:- 用于高精度計算,適用于財務數據。
-
Random
:- 用于生成隨機數。
枚舉(Enum)
枚舉是一種特殊的類,用于定義一組常量:
enum Season {SPRING, SUMMER, AUTUMN, WINTER
}AI寫代碼java運行
內部類
-
成員內部類:
- 定義在類中,可以訪問外部類的所有成員。
-
局部內部類:
- 定義在方法中,只能在該方法內訪問。
-
靜態內部類:
- 使用
static
修飾,只能訪問外部類的靜態成員。
- 使用
-
匿名內部類:
- 沒有名稱的內部類,通常用于實現接口或抽象類。
總結
- 源碼是理解程序的基礎,字節碼是程序運行的關鍵,幫助文檔是開發的指南。
- Object是Java的根類,提供了對象的基本操作。
- System類提供了系統級別的操作,如垃圾回收、時間獲取等。
- String類用于操作字符串,提供了豐富的構造方法和方法。
- StringBuffer和StringBuilder用于字符串的拼接和修改,前者線程安全,后者性能更優。
- 包裝類用于將基本數據類型轉換為對象。
- 日期相關類用于處理日期和時間。
- 內部類提供了更靈活的代碼組織方式。
八、數組
一維數組
-
定義:
- 數組是引用數據類型,存儲在堆內存中。
- 可以存儲各種數據類型,但不能直接存儲對象,存儲的是對象的引用(內存地址)。
-
特點:
- 數組元素類型統一,最后一個下標為
length - 1
。 - 帶有
length
屬性,用于獲取數組長度。
- 數組元素類型統一,最后一個下標為
-
優點:
- 查詢、查找、檢索某個下標元素效率極高(內存連續,類型相同)。
-
缺點:
- 隨機增刪元素效率較低。
- 不能存儲大數據量。
-
定義與初始化:
-
靜態初始化:
數據類型[] 數組名 = {元素1, 元素2, ...};AI寫代碼java運行
-
動態初始化:
數據類型[] 數組名 = new 數據類型[長度];AI寫代碼java運行
-
-
賦值:
數組名[下標] = 值;AI寫代碼java運行
-
遍歷:
-
使用
for
循環或增強for
循環:for (int i = 0; i < 數組名.length; i++) {System.out.println(數組名[i]); }AI寫代碼java運行
-
-
方法參數:
-
數組可以作為方法的參數:
void 方法名(數據類型[] 數組名) {}AI寫代碼java運行
-
-
main 方法的數組參數:
-
main
方法的參數是一個字符串數組,用于接收命令行參數:public static void main(String[] args) {}AI寫代碼java運行
-
-
存儲對象:
-
數組可以存儲對象的引用:
類名[] 數組名 = new 類名[長度]; 數組名[0] = new 類名();AI寫代碼java運行
-
-
數組擴容:
-
新建一個大數組,然后將原數組拷貝過去:
int[] newArray = new int[原數組.length * 2]; System.arraycopy(原數組, 0, newArray, 0, 原數組.length);AI寫代碼java運行
-
-
數組拷貝:
-
使用
System.arraycopy
方法:System.arraycopy(原數組, 原起點, 目標數組, 目標下標, 長度);AI寫代碼java運行
-
二維數組
-
定義:
- 二維數組是數組的數組,可以看作是一個表格。
-
語法:
數據類型[][] 數組名 = new 數據類型[行數][列數];AI寫代碼java運行
-
初始化:
-
靜態初始化:
數據類型[][] 數組名 = {{元素1, 元素2}, {元素3, 元素4}};AI寫代碼java運行
-
動態初始化:
數據類型[][] 數組名 = new 數據類型[行數][列數];AI寫代碼java運行
-
-
遍歷:
-
使用嵌套
for
循環:for (int i = 0; i < 數組名.length; i++) {for (int j = 0; j < 數組名[i].length; j++) {System.out.println(數組名[i][j]);} }AI寫代碼java運行
-
總結
-
一維數組:
- 適用于存儲一組相同類型的數據。
- 查詢效率高,增刪效率低。
- 可以通過
length
屬性獲取長度。 - 支持靜態初始化和動態初始化。
-
二維數組:
- 適用于存儲表格型數據。
- 可以看作是一維數組的數組。
- 支持靜態初始化和動態初始化。
-
數組的優缺點:
- 優點:查詢效率高,內存連續。
- 缺點:增刪效率低,不能存儲大數據量。
-
數組的應用場景:
- 存儲一組固定長度的數據。
- 存儲對象引用。
- 存儲表格型數據(二維數組)。
示例
-
一維數組:
int[] arr = {1, 2, 3, 4, 5}; for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]); }AI寫代碼java運行
-
二維數組:
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]);} }AI寫代碼java運行
-
數組存儲對象:
Animal[] animals = new Animal[2]; animals[0] = new Cat(); animals[1] = new Dog();AI寫代碼java運行
-
數組擴容:
int[] src = {1, 2, 3}; int[] dest = new int[src.length * 2]; System.arraycopy(src, 0, dest, 0, src.length);AI寫代碼java運行
通過合理使用數組,可以高效地存儲和操作數據,但需要注意其增刪效率較低的缺點。
九、算法
以下是常見 排序算法 和 查找算法 的思想總結,并附帶 Java 實例:
排序算法
-
冒泡排序(Bubble Sort):
-
思想:重復遍歷數組,每次比較相鄰元素,如果順序錯誤則交換,直到沒有需要交換的元素。
-
時間復雜度:O(n2)。
-
Java 實現:
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;}}} }AI寫代碼java運行
-
-
選擇排序(Selection Sort):
-
思想:每次從未排序部分選擇最小元素,放到已排序部分的末尾。
-
時間復雜度:O(n2)。
-
Java 實現:
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;} }AI寫代碼java運行
-
-
插入排序(Insertion Sort):
-
思想:將未排序部分的元素逐個插入到已排序部分的正確位置。
-
時間復雜度:O(n2)。
-
Java 實現:
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;} }AI寫代碼java運行
-
-
快速排序(Quick Sort):
-
思想:選擇一個基準元素,將數組分為兩部分,左邊小于基準,右邊大于基準,遞歸排序。
-
時間復雜度:O(n log n)。
-
Java 實現:
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; }AI寫代碼java運行
-
-
歸并排序(Merge Sort):
-
思想:將數組分成兩半,分別排序,然后合并。
-
時間復雜度:O(n log n)。
-
Java 實現:
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];} }AI寫代碼java運行
-
查找算法
-
線性查找(Linear Search):
-
思想:從頭到尾遍歷數組,逐個比較,找到目標元素。
-
時間復雜度:O(n)。
-
Java 實現:
public static int linearSearch(int[] arr, int target) {for (int i = 0; i < arr.length; i++) {if (arr[i] == target) {return i;}}return -1; }AI寫代碼java運行
-
-
二分查找(Binary Search):
-
思想:在有序數組中,每次取中間元素與目標比較,縮小查找范圍。
-
時間復雜度:O(log n)。
-
Java 實現:
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; }AI寫代碼java運行
-
總結
-
排序算法:
- 冒泡排序:簡單但效率低,適合小規模數據。
- 選擇排序:每次選擇最小元素,適合小規模數據。
- 插入排序:適合部分有序的數據。
- 快速排序:高效,適合大規模數據。
- 歸并排序:穩定且高效,適合大規模數據。
-
查找算法:
- 線性查找:適合無序數據。
- 二分查找:適合有序數據,效率高。
-
選擇依據:
- 數據規模、是否有序、穩定性要求等。
示例
public class Main {public static void main(String[] args) {int[] arr = {5, 3, 8, 4, 2};bubbleSort(arr);System.out.println("冒泡排序結果: " + Arrays.toString(arr));int[] arr2 = {5, 3, 8, 4, 2};quickSort(arr2, 0, arr2.length - 1);System.out.println("快速排序結果: " + Arrays.toString(arr2));int target = 4;int index = binarySearch(arr2, target);System.out.println("二分查找結果: " + (index != -1 ? "找到,下標為 " + index : "未找到"));}
}AI寫代碼java運行
輸出:
冒泡排序結果: [2, 3, 4, 5, 8]
快速排序結果: [2, 3, 4, 5, 8]
二分查找結果: 找到,下標為 2AI寫代碼
通過合理選擇排序和查找算法,可以高效地處理數據。
十、異常
1. 異常的基本概念
- 異常在 Java 中以類的方式存在,每個異常類都可以創建異常對象。
- 方法覆蓋規則:子類重寫父類方法時,不能拋出比父類方法更高的異常(運行時異常
RuntimeException
除外)。 - 異常的分類:
java.lang.Throwable
:異常的父類,有兩個子類:Error
:錯誤,通常是系統級錯誤(如OutOfMemoryError
),不可處理,只能退出程序。Exception
:異常,所有異常都是在運行階段發生的。Exception
的直接子類:編譯時異常(受檢異常CheckedException
),需要在編寫程序時預處理。RuntimeException
:運行時異常,通常由程序邏輯錯誤引起,不需要顯式處理。
2. 常見運行時異常
NullPointerException
:空指針異常,嘗試訪問null
對象的成員。ArrayIndexOutOfBoundsException
:數組下標越界異常。ClassCastException
:類型轉換異常,嘗試將對象強制轉換為不兼容的類型。NumberFormatException
:數字轉換異常,嘗試將非數字字符串轉換為數字。
3. 異常處理方式
-
throws
關鍵字:-
在方法聲明位置使用,將異常拋給調用者處理。
-
示例:
public void readFile() throws IOException {// 可能拋出 IOException 的代碼 }AI寫代碼java運行
-
-
try-catch-finally
語句:-
捕獲并處理異常。
-
示例:
try {// 可能拋出異常的代碼 } catch (NullPointerException e) {System.out.println("空指針異常: " + e.getMessage()); } catch (ArrayIndexOutOfBoundsException e) {System.out.println("數組下標越界: " + e.getMessage()); } finally {// 無論是否發生異常,都會執行的代碼System.out.println("finally 塊執行"); }AI寫代碼java運行
-
4. 常用異常方法
getMessage()
:獲取異常的簡單描述信息(通常是構造方法的參數)。printStackTrace()
:打印異常的堆棧追蹤信息(異步線程中常用)。
5. 自定義異常
-
步驟:
- 編寫一個類繼承
Exception
(受檢異常)或RuntimeException
(運行時異常)。 - 提供兩個構造方法:一個無參,一個有參。
- 使用
throw
手動拋出異常。
- 編寫一個類繼承
-
示例:
// 自定義異常類 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("自定義異常發生");} catch (MyException e) {System.out.println(e.getMessage());}} }AI寫代碼java運行
6. 異常處理的最佳實踐
- 明確異常類型:捕獲具體異常,而不是直接捕獲
Exception
。 - 合理使用
finally
:用于釋放資源(如關閉文件、數據庫連接等)。 - 避免空指針異常:在使用對象前進行
null
檢查。 - 日志記錄:使用日志框架(如
Log4j
或SLF4J
)記錄異常信息,便于排查問題。
總結
- 異常分類:
Error
和Exception
,其中Exception
分為編譯時異常和運行時異常。 - 處理方式:
throws
拋給調用者,try-catch-finally
捕獲并處理。 - 自定義異常:繼承
Exception
或RuntimeException
,提供構造方法,使用throw
拋出。 - 最佳實踐:明確異常類型,合理使用
finally
,避免空指針異常,記錄日志。
十一、I/O
I/O(輸入/輸出)概述
I/O(Input/Output)是指應用程序與外部設備(如磁盤、網絡、鍵盤、顯示器等)之間的數據交互。Java通過java.io
包提供了豐富的I/O類庫,支持文件操作、字節流、字符流等功能。
File類
File
類是java.io
包中唯一代表磁盤文件本身的對象,用于操作文件和目錄。
構造方法
-
File(String path)
:- 根據路徑創建
File
對象。
- 根據路徑創建
-
File(String parent, String child)
:- 根據父路徑和子路徑(包括文件名)創建
File
對象。
- 根據父路徑和子路徑(包括文件名)創建
-
File(File parent, String child)
:- 根據
File
對象表示的父路徑和子路徑創建File
對象。
- 根據
注意:路徑分隔符可以使用\\
(Windows)或/
(Unix/Linux)。
常用方法
-
boolean exists()
:- 判斷文件或目錄是否存在。
-
boolean delete()
:- 刪除文件或目錄。
-
boolean createNewFile()
:- 如果文件不存在,則創建一個新文件。
-
String getName()
:- 返回文件或目錄的名稱。
-
String getPath()
:- 返回文件或目錄的路徑。
-
String getAbsolutePath()
:- 返回文件或目錄的絕對路徑。
-
boolean canRead()
:- 判斷文件是否可讀。
-
boolean canWrite()
:- 判斷文件是否可寫。
-
boolean isFile()
:- 判斷是否為文件。
-
boolean isDirectory()
:- 判斷是否為目錄。
-
long length()
:- 返回文件內容的長度(字節數)。
-
String[] list()
:- 返回目錄內所有文件和子目錄的名稱。
-
File[] listFiles()
:- 返回目錄內所有文件和子目錄的
File
對象。
- 返回目錄內所有文件和子目錄的
-
createTempFile(String prefix, String suffix)
:- 創建臨時文件。
-
deleteOnExit()
:- JVM退出時自動刪除文件。
字節流
字節流用于處理二進制數據(如圖片、音頻、視頻等),以字節為單位進行讀寫操作。
字節輸入流(InputStream)
InputStream
是字節輸入流的抽象類,用于從源(如文件、網絡等)讀取數據。
常用方法:
-
int read()
:- 逐個字節讀取,返回讀取的字節值(0-255),如果到達流末尾則返回-1。
-
int read(byte[] b)
:- 將數據讀取到字節數組
b
中,返回實際讀取的字節數。
- 將數據讀取到字節數組
-
int read(byte[] b, int off, int len)
:- 從偏移量
off
開始,讀取len
個字節到數組b
中,返回實際讀取的字節數。
- 從偏移量
-
void close()
:- 關閉流,釋放資源。
字節輸出流(OutputStream)
OutputStream
是字節輸出流的抽象類,用于將數據寫入目標(如文件、網絡等)。
常用方法:
-
void write(int b)
:- 逐個字節寫入。
-
void write(byte[] b)
:- 將字節數組
b
中的數據寫入。
- 將字節數組
-
void write(byte[] b, int off, int len)
:- 從偏移量
off
開始,寫入len
個字節。
- 從偏移量
-
void flush()
:- 強制將緩沖區中的數據寫入目標。
-
void close()
:- 關閉流,釋放資源。
具體實現類
-
FileInputStream
:- 用于從文件中讀取字節數據。
-
FileOutputStream
:- 用于將字節數據寫入文件。
拓展總結
-
文件操作:
- 使用
File
類可以創建、刪除、重命名文件,判斷文件是否存在,查詢文件屬性等。
- 使用
-
字節流:
- 字節流適用于處理二進制數據,
InputStream
和OutputStream
是字節流的抽象基類。 FileInputStream
和FileOutputStream
是常用的字節流實現類,用于文件的讀寫操作。
- 字節流適用于處理二進制數據,
-
流的使用注意事項:
- 使用流時,務必在操作完成后調用
close()
方法關閉流,釋放系統資源。 - 對于輸出流,可以調用
flush()
方法強制將緩沖區中的數據寫入目標。
- 使用流時,務必在操作完成后調用
-
臨時文件:
- 使用
createTempFile()
方法可以創建臨時文件,deleteOnExit()
方法可以確保JVM退出時自動刪除臨時文件。
- 使用
-
路徑處理:
- 路徑分隔符可以使用
\\
(Windows)或/
(Unix/Linux),Java會自動處理。
- 路徑分隔符可以使用
-
性能優化:
- 對于大文件的讀寫,建議使用緩沖區(如
BufferedInputStream
和BufferedOutputStream
)來提高性能。
- 對于大文件的讀寫,建議使用緩沖區(如
示例代碼
文件操作
File file = new File("test.txt");
if (!file.exists()) {file.createNewFile(); // 創建文件
}
System.out.println("文件名稱: " + file.getName());
System.out.println("文件路徑: " + file.getAbsolutePath());
file.delete(); // 刪除文件AI寫代碼java運行
字節流讀寫
// 寫入文件
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));}
}AI寫代碼java運行
通過掌握這些核心概念和類庫,可以高效地處理文件操作和字節流讀寫。
字符流總結
字符流是Java I/O中用于處理文本數據的流,它以字符為單位進行讀寫操作。與字節流不同,字符流專門用于處理字符數據(如文本文件),并且支持字符編碼(如UTF-8、GBK等),能夠正確處理多字節字符。
字符流概述
字符流的核心類是Reader
和Writer
,它們分別是字符輸入流和字符輸出流的抽象基類。字符流的主要特點包括:
- 以字符為單位:
- 字符流以字符為單位讀寫數據,適合處理文本文件。
- 支持字符編碼:
- 字符流可以正確處理字符編碼,避免亂碼問題。
- 高效讀寫:
- 字符流通常與緩沖區結合使用(如
BufferedReader
和BufferedWriter
),提高讀寫效率。
- 字符流通常與緩沖區結合使用(如
字符輸入流(Reader)
Reader
是字符輸入流的抽象類,用于從源(如文件、字符串等)讀取字符數據。
常用方法
-
int read()
:- 讀取單個字符,返回字符的Unicode值(0-65535),如果到達流末尾則返回-1。
-
int read(char[] cbuf)
:- 將字符數據讀取到字符數組
cbuf
中,返回實際讀取的字符數。
- 將字符數據讀取到字符數組
-
int read(char[] cbuf, int off, int len)
:- 從偏移量
off
開始,讀取len
個字符到數組cbuf
中,返回實際讀取的字符數。
- 從偏移量
-
void close()
:- 關閉流,釋放資源。
具體實現類
-
FileReader
:- 用于從文件中讀取字符數據。
-
BufferedReader
:- 帶有緩沖區的字符輸入流,提供
readLine()
方法逐行讀取文本。
- 帶有緩沖區的字符輸入流,提供
-
InputStreamReader
:- 將字節流轉換為字符流,支持指定字符編碼。
字符輸出流(Writer)
Writer
是字符輸出流的抽象類,用于將字符數據寫入目標(如文件、控制臺等)。
常用方法
-
void write(int c)
:- 寫入單個字符。
-
void write(char[] cbuf)
:- 寫入字符數組
cbuf
中的數據。
- 寫入字符數組
-
void write(char[] cbuf, int off, int len)
:- 從偏移量
off
開始,寫入len
個字符。
- 從偏移量
-
void write(String str)
:- 寫入字符串
str
。
- 寫入字符串
-
void write(String str, int off, int len)
:- 從偏移量
off
開始,寫入len
個字符。
- 從偏移量
-
void flush()
:- 強制將緩沖區中的數據寫入目標。
-
void close()
:- 關閉流,釋放資源。
具體實現類
-
FileWriter
:- 用于將字符數據寫入文件。
-
BufferedWriter
:- 帶有緩沖區的字符輸出流,提供
newLine()
方法寫入換行符。
- 帶有緩沖區的字符輸出流,提供
-
OutputStreamWriter
:- 將字節流轉換為字符流,支持指定字符編碼。
字符流與字節流的區別
-
單位不同:
- 字節流以字節為單位,適合處理二進制數據。
- 字符流以字符為單位,適合處理文本數據。
-
編碼支持:
- 字節流不涉及字符編碼,直接處理字節數據。
- 字符流支持字符編碼,能夠正確處理多字節字符。
-
性能優化:
- 字符流通常與緩沖區結合使用,提高讀寫效率。
示例代碼
字符流讀寫文件
// 寫入文件
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);}
}AI寫代碼java運行
使用指定編碼讀寫文件
// 寫入文件(指定編碼為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);}
}AI寫代碼java運行
總結
-
字符流適用場景:
- 處理文本文件、字符串等字符數據。
-
核心類:
Reader
和Writer
是字符流的抽象基類。FileReader
、BufferedReader
、FileWriter
、BufferedWriter
是常用的實現類。
-
字符編碼:
- 使用
InputStreamReader
和OutputStreamWriter
可以指定字符編碼,避免亂碼問題。
- 使用
-
性能優化:
- 使用
BufferedReader
和BufferedWriter
可以提高讀寫效率。
- 使用
-
流關閉:
- 使用
try-with-resources
語法確保流被正確關閉,釋放資源。
- 使用
通過掌握字符流的核心概念和類庫,可以高效地處理文本數據的讀寫操作。
十二、集合
集合是Java中用于存儲和管理一組對象的容器。它提供了一種更靈活、更高效的方式來操作數據集合。以下是集合的核心概念和總結:
集合的特點
-
容器性質:
- 集合是一個容器,可以容納其他類型的數據。
- 集合不能直接存儲基本數據類型(如
int
、char
等),也不能直接存儲對象,存儲的是Java對象的內存地址(引用)。
-
數據結構:
- 不同的集合對應不同的數據結構(如數組、鏈表、哈希表、二叉樹等)。
- 使用不同的集合等同于使用了不同的數據結構。
-
包位置:
- 所有的集合類都位于
java.util
包中。
- 所有的集合類都位于
集合的層次結構
-
超級父接口:
Iterable<T>
:- 所有集合都是可迭代的,即可以通過迭代器遍歷集合中的元素。
- 方法:
Iterator<T> iterator()
:返回集合的迭代器。
-
單個元素集合的父接口:
Collection<E>
:- 表示存儲單個元素的集合的超級接口。
- 子接口包括:
List
、Set
、Queue
等。
-
鍵值對集合的父接口:
Map<K,V>
:- 表示存儲鍵值對的集合,獨立于
Collection
體系。
- 表示存儲鍵值對的集合,獨立于
集合的實現類總結
1. List
接口的實現類:
ArrayList
:- 底層是數組,查詢快,增刪慢。
- 非線程安全。
LinkedList
:- 底層是雙向鏈表,增刪快,查詢慢。
- 非線程安全。
Vector
:- 底層是數組,線程安全,但效率較低,使用較少。
2. Set
接口的實現類:
HashSet
:- 底層是
HashMap
,元素存儲在HashMap
的key
部分。 - 無序且不允許重復。
- 底層是
TreeSet
:- 底層是
TreeMap
,元素存儲在TreeMap
的key
部分。 - 元素自動按大小順序排序。
- 底層是
3. Map
接口的實現類:
HashMap
:- 底層是哈希表,非線程安全。
- 允許
null
鍵和null
值。
Hashtable
:- 底層是哈希表,線程安全,但效率較低,使用較少。
- 不允許
null
鍵和null
值。
Properties
:- 底層是哈希表,線程安全。
key
和value
只能存儲字符串(String
)。
TreeMap
:- 底層是二叉樹。
key
自動按照大小順序排序。
集合的選擇
-
需要存儲單個元素:
- 如果需要有序且允許重復,使用
List
:- 查詢多,增刪少:
ArrayList
。 - 增刪多,查詢少:
LinkedList
。
- 查詢多,增刪少:
- 如果不需要重復元素,使用
Set
:- 無序:
HashSet
。 - 有序:
TreeSet
。
- 無序:
- 如果需要有序且允許重復,使用
-
需要存儲鍵值對:
- 非線程安全:
HashMap
。 - 線程安全:
Hashtable
或Properties
。 - 需要排序:
TreeMap
。
- 非線程安全:
-
線程安全:
- 如果需要線程安全,可以使用
Vector
、Hashtable
或Properties
,但效率較低。 - 推薦使用
Collections.synchronizedList()
或ConcurrentHashMap
等并發集合。
- 如果需要線程安全,可以使用
總結
-
集合的核心:
- 集合是存儲和管理一組對象的容器,存儲的是對象的內存地址。
- 不同的集合對應不同的數據結構,選擇合適的集合可以提高程序效率。
-
常用集合:
List
:有序且允許重復,常用ArrayList
和LinkedList
。Set
:無序且不允許重復,常用HashSet
和TreeSet
。Map
:存儲鍵值對,常用HashMap
、TreeMap
和Properties
。
-
線程安全:
- 線程安全的集合有
Vector
、Hashtable
和Properties
,但效率較低。 - 推薦使用并發集合(如
ConcurrentHashMap
)來實現線程安全。
- 線程安全的集合有
通過掌握集合的核心概念和常用實現類,可以更高效地處理數據集合,并根據需求選擇合適的集合類型。
List 集合存儲元素的特點:
有序可重復
有序:存進去的順序和取出的順序相同,每一個元素都有下標
可重復:存進去1,可以再存儲一個1
Set 集合存儲元素的特點(Map的Key):
無序不可重復
無序:存進去的順序和取出的順序不一定相同,另外 Set 集合中元素沒有下標(哈希表的存儲)
不可重復:存進去1,不能再存儲1了(哈希表的覆蓋)
SortedSet( SortedMap )集合存儲元素特點:
首先是無序不可重復的,但是 SortedSet 集合中的元素是可排序的
無序:存進去的順序和取出的順序不一定相同,另外 Set 集合中元素沒有下標
不可重復:存進去1,不能再存儲1了
可排序:可以按照大小順序排列。
Map 集合的 key ,就是一個 Set 集合。
往 Set 集合中放數據,實際上放到了 Map 集合的 key 部分。
Interface Collection
沒有使用泛型前可以存儲Object的所有子類型
- Boolean add(E e) 添加元素
- Object[] toArray() 轉化成數組(使用不多)
- Int size() 返回此集合中元素的數目。
- Boolean contains(Object o) 如果此集合包含指定的元素(存放在集合中的類型,需要重寫equals方法)
- Void clear() 從此集合中刪除所有元素
- Boolean equals(Object o) 將指定的對象與此集合進行比較以實現相等性(內存地址)
- Boolean remove(Object o) 從此集合中刪除指定元素的單個實例
- Boolean isEmpty() 如果此集合不包含任何元素(判空)則返回。true
Iterator<E> iterator() ***:**不管存進去什么,拿出來都是Object,取出來還是原類型
返回此集合中元素的迭代器**,Collection通用,Map集合不能用**
只要集合結構發生改變迭代器一定要重新獲取
- default void forEachRemaining(Consumer<? super E> action) 對每個剩余元素執行給定的操作,直到所有元素都已處理完畢或該操作引發異常。
- Boolean hasNext() 如果迭代具有更多元素,則返回。true
- Object next() 返回迭代中的下一個元素。(返回object)
- default void remove() 從基礎集合中刪除此迭代器返回的最后一個元素(可選操作)。
Interface List 有序可重復,Collection子接口
- void add(int index, E element) 在此列表中的指定位置插入指定的元素
- E get(int index) 返回此列表中指定位置處的元素
- E set(int index, E element) 將此列表中指定位置的元素替換為指定的元素
- int indexOf(Object o) 返回此列表中指定元素的第一次出現的索引,如果此列表不包含該元素,則返回 -1
- int lastIndexOf(Object o) 返回此列表中指定元素的最后一次出現的索引,如果此列表不包含該元素,則返回 -1。
- E remove(int index) 刪除此列表中指定位置的元素
Class ArrayList 非線程安全數組,初始化容量10,底層object數組
構造方法:
- ArrayList() 構造初始容量為 10 的空列表(底層先創建了一個長度為0的數組,添加元素是初始化為10,自動擴容1.5倍)
- ArrayList(int initialCapacity) 構造具有指定初始容量的空列表(建議提前估計,減少擴容)
- ArrayList(Collection<? extends E> c) 構造一個列表,其中包含指定集合的元素,并按集合的迭代器返回這些元素的順序排列。
方法:同List方法
Class LinkedList 雙向鏈表,隨機增刪效率高,檢索效率低
Class Vector 線程安全數組,默認10,擴容翻倍**(不經常使用)**
轉換:使用集合工具類:java.util.Collections.synchronizedList(集合)
Interface Set 無序不可重復 存儲Map的Key
Class HashSet 哈希表(底層HashMap)
需要重寫hashCode和equals方法,其他方法參見HashMap
Interface SortedSet 無序不可重復可排序
Class TreeSet 二叉樹(底層TreeMap Key部分)無序不可重復可排序
Key值自定義類需要實現java.long.Comparable接口或者創建比較器對象
class user implements Comparable<user>{ //自定義類需要實現接口int age;public user(int age) {this.age = age;}@Overridepublic String toString() {return "user{" + "age=" + age + '}';}@Override //重寫比較規則public int compareTo(user o) {return this.age-o.age; //返回==0,value覆蓋,返回大于0 到右子樹,返回小于0到左子樹}
}AI寫代碼java運行
Interface Map<K,V> Map主接口(和Collection沒有繼承關系)
以Key和Value存儲數據都是引用數據類型,都存儲內存地址,Key是主導
- V put(K key, V value) 添加鍵值對(Key元素需要重新hashCode和equals方法)(Key可以為空,只有一個)
- void clear() 清空Map集合
- V get(Object key) 通過key獲取value(key元素需要重新hashCode和equals方法)
- boolean containsKey(Object key) 判斷Map是否包含某個key(底層equals)
- boolean containsValue(Object value) 判斷Map是否包含某個value(底層equals)
- boolean isEmpty() 判斷Map集合元素個數是否為零
- Set<K> keySet() 獲取Map集合所有的Key(是個set集合)
- V remove(Object key) 通過key刪除鍵值對
- Collection<V> values() 獲取Map集合中鍵值對所有value(返回Collection)
- int size() 獲取Map集合所有的鍵值對個數
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) //效率較高,適合大數據,直接獲取System.out.println(node); //組合遍歷AI寫代碼java運行
Class HashMap<K,V> 哈希表 非線程安全(初始化容量16[必須是2的倍數],默認加載因子0.75)
Key元素類型需要重新hashCode和equals方法
JDK8新特性:當單向鏈表長度超過8后數據結構會變成紅黑樹數據結構,當紅黑樹小于6,會變回鏈表
構造 函數 描述
- HashMap() 使用默認初始容量 (16) ,默認負載系數 (0.75)
- HashMap(int initialCapacity) 指定的初始容量,默認負載系數 初始容量必須是2的倍數:達到散列均勻,提高存取效率
- HashMap(int initialCapacity, float loadFactor) 指定初始容量和負載系數
- HashMap(Map<? extends K,? extends V> m)
Class Hashtable<K,V> 哈希表 線程安全(synchronized) Key不可以為空*(不常用)**
初始化容量11,默認加載因子0.75f,擴容:原容量*2+1
Class Properties 屬性類 繼承Hashtable類 僅支持String
- Object setProperty(String key, String value) 存
- String getProperty(String key) 取
- String getProperty(String key, String defaultValue) 當key值為NULL時,返回def的值;當key值不為NULL時,返回key的值
Interface SortedMap<K,V>
Class TreeMap<K,V> 二叉樹 可排序集合(中序遍歷)
Key值自定義類需要實現java.long.Comparable接口或者創建比較器對象(類或者匿名內部類)
Class Collections 集合工具類
- synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(線程安全)映射。
- synchronizedList(List list) 返回由指定列表支持的同步(線程安全)列表。**
**synchronizedCollection(Collection c) 返回由指定集合支持的同步(線程安全)集合 - sort(List list, Comparator<? super T> c) 根據指定比較器引發的順序對指定列表進行排序。
十三、泛型
1. 泛型概述
-
引入時間:JDK 5.0 之后的新特性。
-
作用:
- 統一集合中元素的類型,避免類型轉換錯誤。
- 只在程序編譯階段起作用,編譯后會進行類型擦除(Type Erasure)。
-
語法:
-
在創建對象時,前后兩段添加泛型類型。
-
示例:
List<String> list = new ArrayList<String>();AI寫代碼java運行
-
2. 泛型的優點
- 類型安全:編譯時檢查類型,避免運行時類型轉換錯誤。
- 代碼復用:可以編寫通用的類和方法,適用于多種類型。
- 代碼簡潔:減少強制類型轉換的代碼。
3. 泛型的缺點
- 導致集合存儲缺少多樣性:泛型限制了集合中元素的類型,無法存儲多種類型的對象。
- 類型擦除:泛型信息在編譯后會被擦除,運行時無法獲取泛型的具體類型。
4. 自動推斷機制(鉆石表達式)
-
引入時間:JDK 7 新特性。
-
作用:自動推斷泛型類型,簡化代碼。
-
語法:只寫前面的泛型類型,后面的泛型類型可以省略。
-
示例:
List<String> list = new ArrayList<>();AI寫代碼java運行
5. 自定義泛型
-
泛型類:
-
在定義類時添加
<T>
,T
是類型參數。 -
示例:
public class Box<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;} }AI寫代碼java運行
-
使用:
Box<String> box = new Box<>(); box.setValue("Hello"); String value = box.getValue();AI寫代碼java運行
-
-
泛型方法:
-
在定義方法時添加
<T>
,T
是類型參數。 -
示例:
public <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);} }AI寫代碼java運行
-
使用:
Integer[] intArray = {1, 2, 3}; printArray(intArray);AI寫代碼java運行
-
6. 泛型的通配符
-
<?>
:表示任意類型。 -
<? extends T>
:表示T
或其子類型(上界通配符)。 -
<? super T>
:表示T
或其父類型(下界通配符)。 -
示例:
public void printList(List<?> list) {for (Object element : list) {System.out.println(element);} }AI寫代碼java運行
7. 泛型的限制
- 不能使用基本類型:泛型類型必須是引用類型(如
Integer
而不是int
)。 - 不能創建泛型數組:例如
new T[10]
是非法的。 - 不能實例化泛型類型:例如
new T()
是非法的。
8. 泛型的應用場景
- 集合框架:如
List<T>
、Map<K, V>
等。 - 工具類:如
Comparator<T>
、Comparable<T>
等。 - 自定義數據結構:如棧、隊列、鏈表等。
總結與拓展
- 泛型的作用:統一集合中元素的類型,提高代碼的安全性和復用性。
- 自動推斷機制:JDK 7 引入的鉆石表達式簡化了泛型代碼。
- 自定義泛型:通過泛型類和泛型方法實現通用代碼。
- 通配符:
<?>
、<? extends T>
、<? super T>
提供了更靈活的類型約束。 - 限制:泛型不能使用基本類型、不能創建泛型數組、不能實例化泛型類型。
十四、多線程
進程是:一個應用程序(1個進程是一個軟件)
獨立性:系統分配資源和調度資源的獨立單位
動態性:進程實質是程序的一次執行過程,進程是動態產生,動態消亡的
并發性:任何進程都可以同其他進程一起并發執行
線程是:一個進程中的執行場景/執行單元,是進程中單個順序控制流,是一條執行路徑。
并行:同一時刻,多個指令在多個CPU上同時執行
并發:同一時刻,多個指令在單個CPU交替執行
線程狀態轉換
1、新建狀態(New):新創建了一個線程對象。
2、就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位于可運行線程池中,變得可運行,等待獲取CPU的使用權。
3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
(一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖)
(二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池中。
(三)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態
當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖)
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
線程構造方法:
構造方法名 | 備注 |
---|---|
Thread() | |
Thread(String name) | name為線程名字 |
創建線程第二種方式 | |
Thread(Runnable target) | |
Thread(Runnable target, String name) | name為線程名字 |
Java 中實現線程的三種方式總結
1. 繼承 Thread
類
-
實現方式:
- 編寫一個類,直接繼承
java.lang.Thread
。 - 重寫
run()
方法,定義線程執行的任務。
- 編寫一個類,直接繼承
-
創建線程對象:
MyThread thread = new MyThread();AI寫代碼java運行
-
啟動線程:
thread.start();AI寫代碼java運行
-
特點:
- 簡單易用,但 Java 是單繼承,繼承
Thread
類后無法繼承其他類。
- 簡單易用,但 Java 是單繼承,繼承
2. 實現 Runnable
接口
-
實現方式:
- 編寫一個類,實現
java.lang.Runnable
接口。 - 實現
run()
方法,定義線程執行的任務。 - 通常使用匿名內部類創建。
- 編寫一個類,實現
-
創建線程對象:
Runnable task = new MyRunnable(); Thread thread = new Thread(task);AI寫代碼java運行
-
啟動線程:
thread.start();AI寫代碼java運行
-
特點:
- 更靈活,可以避免單繼承的限制。
- 適合多個線程共享同一個任務。
3. 使用 Callable
和 Future
接口
-
實現方式:
- 編寫一個類,實現
java.util.concurrent.Callable
接口。 - 實現
call()
方法,定義線程執行的任務,并返回結果。
- 編寫一個類,實現
-
創建線程對象:
-
創建
Callable
實現類的實例:Callable<Integer> task = new MyCallable();AI寫代碼java運行
-
使用
FutureTask
包裝Callable
對象:FutureTask<Integer> futureTask = new FutureTask<>(task);AI寫代碼java運行
-
使用
FutureTask
對象作為Thread
的target
創建線程:Thread thread = new Thread(futureTask);AI寫代碼java運行
-
-
啟動線程:
thread.start();AI寫代碼java運行
-
獲取結果:
Integer result = futureTask.get(); // 阻塞直到獲取結果AI寫代碼java運行
-
特點:
call()
方法可以有返回值和拋出異常。- 適合需要獲取線程執行結果的場景。
Future
接口的常用方法
cancel(boolean mayInterruptIfRunning)
:嘗試取消任務。get()
:獲取任務結果,阻塞直到任務完成。get(long timeout, TimeUnit unit)
:在指定時間內獲取任務結果,超時拋出TimeoutException
。isCancelled()
:判斷任務是否被取消。isDone()
:判斷任務是否完成。
三種方式的對比
方式 | 優點 | 缺點 |
---|---|---|
繼承 Thread 類 | 簡單易用 | 單繼承限制,無法繼承其他類 |
實現 Runnable 接口 | 靈活,避免單繼承限制,適合多線程共享任務 | 無法直接獲取線程執行結果 |
使用 Callable 和 Future | 可以獲取線程執行結果,支持異常處理,功能更強大 | 使用稍復雜,需要 FutureTask 包裝 |
總結
- 繼承
Thread
類:適合簡單的線程任務,但受限于單繼承。 - 實現
Runnable
接口:更靈活,適合多線程共享任務。 - 使用
Callable
和Future
:適合需要獲取線程執行結果或處理異常的場景。
根據具體需求選擇合適的方式實現多線程編程。
獲取當前線程對象、獲取線程對象名字、修改線程對象名字
方法名 | 作用 |
---|---|
static Thread currentThread() | 獲取當前線程對象 |
String getName() | 獲取線程對象名字 |
void setName(String name) | 修改線程對象名字 |
關于線程的sleep方法
方法名 | 作用 |
---|---|
static void sleep(long millis) | 讓當前線程休眠millis秒 |
關于線程中斷sleep()的方法
方法名 | 作用 |
---|---|
void interrupt() | 終止線程的睡眠 |
Java進程的優先級
常量名 | 備注 |
---|---|
static int MAX_PRIORITY | 最高優先級(10) |
static int MIN_PRIORITY | 最低優先級(1) |
static int NORM_PRIORITY | 默認優先級(5) |
方法:
方法名 | 作用 |
---|---|
int getPriority() | 獲得線程優先級 |
void setPriority(int newPriority) | 設置線程優先級 |
static void yield() | 讓位,當前線程暫停,回到就緒狀態,讓給其它線程。 |
void join() | 將一個線程合并到當前線程中,當前線程受阻塞,加入的線程執行直到結束 |
void join(long millis) | 接上條,等待該線程終止的時間最長為 millis 毫秒 |
void join(long millis, int nanos) | 接第一條,等待該線程終止的時間最長為 millis 毫秒 + nanos 納秒 |
多線程并發環境下,數據的安全問題(重點)
1.為什么這個是重點?
以后在開發中,我們的項目都是運行在服務器當中,而服務器已經將線程的定義,線程對象的創建,線程的啟動等,都已經實現完了。這些代碼我們都不需要編寫。
最重要的是: 你要知道,你編寫的程序需要放到一個多線程的環境下運行,你更需要關注的是這些數據在多線程并發的環境下是否是安全的。(重點:★★★★★)
2.什么時候數據在多線程并發的環境下會存在安全問題呢?★★★★★
滿足三個條件:
條件1:多線程并發。
條件2:有共享數據。
條件3:共享數據有修改的行為。
滿足以上3個條件之后,就會存在線程安全問題。
3.怎么解決線程安全問題呢?
當多線程并發的環境下,有共享數據,并且這個數據還會被修改,此時就存在線程安全問題,怎么解決這個問題?
線程排隊執行。(不能并發)。用排隊執行解決線程安全問題。
這種機制被稱為:線程同步機制。專業術語叫做:線程同步,實際上就是線程不能并發了,線程必須排隊執行。
線程同步就是線程排隊了,線程排隊了就會 犧牲一部分效率 ,數據安全第一位,只有數據安全了,我們才可以談效率。數據不安全,沒有效率的事兒。
死鎖(DeadLock)
死鎖(Deadlock)是多線程編程中的一種常見問題,指的是兩個或多個線程在執行過程中,因為爭奪資源而造成的一種互相等待的現象,導致這些線程都無法繼續執行下去。
死鎖代碼要會寫。一般面試官要求你會寫。只有會寫的,才會在以后的開發中注意這個事兒。因為死鎖很難調試。
死鎖的四個必要條件
死鎖的發生必須同時滿足以下四個條件:
-
互斥條件(Mutual Exclusion):
- 資源一次只能被一個線程占用。
-
占有并等待(Hold and Wait):
- 線程已經占有了至少一個資源,但又申請新的資源,而新的資源被其他線程占用。
-
不可搶占(No Preemption):
- 線程已占有的資源不能被其他線程強行搶占,必須由線程自己釋放。
-
循環等待(Circular Wait):
- 存在一個線程的等待循環鏈,每個線程都在等待下一個線程所占用的資源。
Java 中的死鎖示例
以下是一個經典的死鎖代碼示例,展示了兩個線程互相等待對方釋放鎖的情況:
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();}
}AI寫代碼java運行
代碼分析
-
線程1:
- 先獲取
lock1
,然后嘗試獲取lock2
。 - 在獲取
lock2
之前,線程1會休眠100毫秒。
- 先獲取
-
線程2:
- 先獲取
lock2
,然后嘗試獲取lock1
。 - 在獲取
lock1
之前,線程2會休眠100毫秒。
- 先獲取
-
死鎖發生:
- 線程1持有
lock1
并等待lock2
。 - 線程2持有
lock2
并等待lock1
。 - 兩個線程互相等待,導致死鎖。
- 線程1持有
如何避免死鎖
-
避免嵌套鎖:
- 盡量不要在持有一個鎖的同時去申請另一個鎖。
-
按順序獲取鎖:
- 如果多個線程需要獲取多個鎖,確保它們以相同的順序獲取鎖。
-
使用超時機制:
- 在獲取鎖時設置超時時間,如果超時則釋放已持有的鎖并重試。
-
使用工具檢測:
- 使用工具(如
jstack
)檢測死鎖。
- 使用工具(如
死鎖的調試與檢測
-
使用
jstack
:- 運行程序后,使用
jstack
命令查看線程狀態,可以檢測到死鎖。
- 運行程序后,使用
-
日志輸出:
- 在代碼中添加日志,記錄鎖的獲取和釋放情況。
-
使用工具:
- 使用IDE(如IntelliJ IDEA)或第三方工具(如VisualVM)檢測死鎖。
守護線程
在Java中,線程分為兩大類:用戶線程和守護線程。守護線程(Daemon Thread)是一種特殊的線程,它的生命周期依賴于用戶線程。當所有的用戶線程結束時,守護線程會自動退出。
守護線程的特點
-
依賴用戶線程:
- 守護線程是為用戶線程提供服務的線程。
- 當所有的用戶線程結束時,守護線程會自動退出。
-
典型代表:
- 垃圾回收線程(
GC
)是Java中最典型的守護線程。
- 垃圾回收線程(
-
主線程是用戶線程:
main
方法所在的線程是用戶線程。
-
死循環:
- 守護線程通常是一個死循環,持續執行某些后臺任務。
守護線程的應用場景
-
定時任務:
- 例如,每天00:00自動備份系統數據。
- 可以使用定時器(如
Timer
或ScheduledExecutorService
),并將定時任務設置為守護線程。
-
后臺監控:
- 例如,監控系統資源使用情況、日志清理等。
-
垃圾回收:
- Java的垃圾回收線程就是一個守護線程。
守護線程的設置
在Java中,可以通過setDaemon(boolean on)
方法將一個線程設置為守護線程:
方法簽名 | 說明 |
---|---|
void setDaemon(boolean on) | on 為true 表示將線程設置為守護線程 |
注意:
- 必須在調用
start()
方法之前設置守護線程,否則會拋出IllegalThreadStateException
。 - 守護線程中創建的子線程默認也是守護線程。
代碼示例
以下是一個守護線程的示例,展示了如何設置守護線程以及它的行為:
public class DaemonThreadExample {public static void main(String[] args) {Thread daemonThread = new Thread(() -> {while (true) {System.out.println("守護線程正在運行...");try {Thread.sleep(1000); // 模擬任務執行} catch (InterruptedException e) {e.printStackTrace();}}});// 設置為守護線程daemonThread.setDaemon(true);// 啟動守護線程daemonThread.start();// 主線程(用戶線程)執行任務System.out.println("主線程開始執行...");try {Thread.sleep(5000); // 模擬主線程執行任務} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主線程執行完畢,程序退出。");}
}AI寫代碼java運行
代碼分析
-
守護線程:
- 守護線程是一個死循環,每隔1秒輸出一條消息。
- 設置為守護線程后,當主線程結束時,守護線程會自動退出。
-
主線程:
- 主線程執行5秒后結束。
- 主線程結束后,守護線程也會自動退出。
守護線程的注意事項
-
資源釋放:
- 守護線程中不要執行關鍵任務(如文件寫入、數據庫操作等),因為它的退出是不可控的。
-
線程優先級:
- 守護線程的優先級通常較低,適合執行后臺任務。
-
生命周期:
- 守護線程的生命周期依賴于用戶線程,不能獨立存在。
定時器的作用:
間隔特定的時間,執行特定的程序。在實際的開發中,每隔多久執行一段特定的程序,這種需求是很常見的,那么在java中其實可以采用多種方式實現:
可以使用sleep方法,睡眠,設置睡眠時間,沒到這個時間點醒來,執行任務。這種方式是最原始的定時器。(比較low)
在java的類庫中已經寫好了一個定時器:java.util.Timer,可以直接拿來用。
不過,這種方式在目前的開發中也很少用,因為現在有很多高級框架都是支持定時任務的。
在實際的開發中,目前使用較多的是Spring框架中提供的SpringTask框架,這個框架只要進行簡單的配置,就可以完成定時器的任務。
構造方法名 | 備注 |
---|---|
Timer() | 創建一個定時器 |
Timer(boolean isDaemon) | isDaemon為true為守護線程定時器 |
Timer(String name) | 創建一個定時器,其線程名字為name |
Timer(String name, boolean isDaemon) | 結合2、3 |
方法名 | 作用 |
void schedule(TimerTask task, Date firstTime, long period) | 安排指定的任務在指定的時間開始進行重復的固定延遲執行 |
void cancel() | 終止定時器 |
關于Object類的wait()、notify()、notifyAll()方法
方法名 | 作用 |
---|---|
void wait() | 讓活動在當前對象的線程無限等待(釋放之前占有的鎖) |
void notify() | 喚醒當前對象正在等待的線程(只提示喚醒,不會釋放鎖) |
void notifyAll() | 喚醒當前對象全部正在等待的線程(只提示喚醒,不會釋放鎖) |
wait和notify方法不是線程對象的方法,是java中任何一個java對象都有的方法,因為這兩個方法是 Object類中自帶 的。
wait方法和notify方法不是通過線程對象調用
調用:
Object o = new Object();
o.wait();
總結 ★★★★★(呼應生產者消費者模式)
1、wait和notify方法不是線程對象的方法,是普通java對象都有的方法。
2、wait方法和notify方法建立在 線程同步 的基礎之上。因為多線程要同時操作一個倉庫。有線程安全問題。
3、wait方法作用:o.wait() 讓正在o對象上活動的線程t進入等待狀態,并且釋放掉t線程之前占有的o對象的鎖
4、notify方法作用:o.notify() 讓正在o對象上等待的線程喚醒,只是通知,不會釋放o對象上之前占有的鎖。
生產者消費者模式(wait()和notify())
什么是“生產者和消費者模式”?
生產線程負責生產,消費線程負責消費。
生產線程和消費線程要達到均衡。
這是一種特殊的業務需求,在這種特殊的情況下需要使用wait方法和notify方法。
模擬一個業務需求
倉庫我們采用List集合。
List集合中假設只能存儲1個元素。
1個元素就表示倉庫滿了。
如果List集合中元素個數是0,就表示倉庫空了。
保證List集合中永遠都是最多存儲1個元素。
必須做到這種效果:生產1個消費1個。
十五、反射
1. Class 對象概述
- Class 對象:在 Java 中,每個類在加載到內存時都會生成一個
Class
對象,該對象存儲了類的所有信息(如方法、構造函數、字段等)。 - 反射:通過
Class
對象,可以在運行時動態獲取類的信息并操作類的成員(如調用方法、訪問字段等)。
2. Class 對象的生成方式
-
類名.class
:-
JVM 將類加載到內存中,但不進行初始化。
-
返回該類的
Class
對象。 -
示例:
Class<?> clazz = String.class;AI寫代碼java運行
-
-
Class.forName("包名.類名")
:-
加載類并默認進行靜態初始化。
-
返回該類的
Class
對象。 -
示例:
Class<?> clazz = Class.forName("java.lang.String");AI寫代碼java運行
-
-
Class.forName("包名.類名", false, 類加載器)
:-
第二個參數為
false
時,不進行初始化;為true
時,進行初始化。 -
示例:
Class<?> clazz = Class.forName("java.lang.String", false, ClassLoader.getSystemClassLoader());AI寫代碼java運行
-
-
實例對象.getClass()
:-
對類進行靜態初始化和非靜態初始化。
-
返回運行時實際對象所屬類的
Class
對象。 -
示例:
String str = "Hello"; Class<?> clazz = str.getClass();AI寫代碼java運行
-
3. Class 對象的特性
- 父子類 Class 對象不一致:
- 如果
A
是B
的子類,則A.class
和B.class
返回的Class
對象不同。 - 如果
a
是A
的實例,則A.class
和a.getClass()
返回的Class
對象一致。
- 如果
4. Class 類的常用方法
getName()
:返回類的全限定名(包名 + 類名)。getSuperclass()
:返回類的直接父類的Class
對象。getInterfaces()
:返回類實現的所有接口的Class
數組。isArray()
:判斷該類是否是數組類型。isEnum()
:判斷該類是否是枚舉類型。isInterface()
:判斷該類是否是接口。isPrimitive()
:判斷該類是否是基本類型(如int
、boolean
等)。isAssignableFrom(Class cls)
:判斷該類是否是cls
的父類或父接口。getComponentType()
:如果該類是數組類型,返回數組的組件類型。asSubclass(Class clazz)
:將當前Class
對象轉換為clazz
的子類類型。
5. asSubclass
方法的使用
-
作用:將當前
Class
對象轉換為指定類的子類類型。 -
示例:
List<String> strList = new ArrayList<>(); Class<? extends List> strListCast = strList.getClass().asSubclass(List.class);AI寫代碼java運行
-
動態加載時的應用:
Class.forName("xxx.xxx.xxx").asSubclass(List.class).newInstance();AI寫代碼java運行
- 如果
xxx.xxx.xxx
是List
的子類,則正常執行;否則拋出ClassCastException
。
- 如果
6. 靜態加載與動態加載
- 靜態加載:通過
new ClassName()
加載類,編譯時必須提供類的定義。 - 動態加載:通過
Class.forName("ClassName")
加載類,編譯時可以缺席,運行時按需提供。
總結
- Class 對象:存儲類的所有信息,是反射機制的核心。
- 生成方式:
類名.class
、Class.forName()
、實例對象.getClass()
。 - 常用方法:
getName()
、getSuperclass()
、getInterfaces()
、asSubclass()
等。 asSubclass
:用于將Class
對象轉換為指定類的子類類型。- 靜態加載與動態加載:靜態加載在編譯時提供類定義,動態加載在運行時按需提供。
通過掌握 Class
對象和反射機制,可以在運行時動態操作類的成員,實現靈活的編程。
十六、小游戲(進擊的小鳥)
public class StartGame { //游戲開始類public static void main(String[] args) throws InterruptedException {JFrame jFrame = new JFrame("進擊の小鳥"); //創建窗口對象jFrame.setSize(400,600);//窗口大小jFrame.setLocationRelativeTo(null); //窗口相對位置jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定點擊關閉結束程序? BirdGame birdGame = new BirdGame(); //初始化游戲對象類
? jFrame.add(birdGame); //把創建好的對象加進來
? jFrame.setVisible(true); //讓窗口可視化
? birdGame.action(); //地面運動方法}
}AI寫代碼java運行
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; //經過t,發生的位移public double t=0.2; //發生位移時間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(){ //鼠標點擊游戲屏幕,給小鳥一個初始上拋速度speed=upspeed;}public void distanceChange(){ //實現小鳥速度,位移,縱坐標變化double v=speed; //初始速度s=v*t-g*t*t/2; //經過t小鳥的位移speed=v-g*t; //小鳥經過時間t的末速度y=y-(int)s; //經過時間t后,小鳥的y}
}AI寫代碼java運行
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; //讓地面往左運動if (x<=-width/2){x=x+distance*2;y=-(height/2-random.nextInt(300)-50) ;//x=400;}}
}AI寫代碼java運行
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; //讓地面往左運動
? if (x==-100){
? x=0;
? }}
}AI寫代碼java運行
public class Music implements Runnable { //音樂類Player player=null;@Overridepublic 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();}
}AI寫代碼java運行
public class Score { //連接對象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;}
}AI寫代碼java運行
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(); //隨機生成id
? pst.setString(1,sid);
? pst.setInt(2,score);
? Date date = new Date();
? SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //創建時間類型對象
? String time=simpleDateFormat.format(date);
? pst.setString(3,time);
? num=pst.executeUpdate();
? } catch (SQLException e) {
? e.printStackTrace();
? }
? return num;}
}AI寫代碼java運行
public class BirdGame extends JPanel { //自定義面板類繼承面板類ScoreManager sc=new ScoreManager();public JPanel jp=new JPanel();public BufferedImage bg; //圖片緩沖區(在顯示圖片前對圖片進行操作 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; //表示游戲狀態public static final int START=0; //開始public static final int RUNNING=1; //運行public static final int GAMEOVER=2; //結束public static int score=0; //初始積分public BirdGame(){try {state=START; //游戲初始為游戲開始狀態ground=new Ground(); //創建地面類對象,調用地面類構造方法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();}}@Overridepublic void paint(Graphics g) { //繪制一次的畫畫方法super.paint(g); //調用畫筆g.drawImage(bg,0,0,null); //繪制背景(最后一個參數為觀察者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:
? //繪制游戲結束圖片
? 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){ //繪制分數方法Font font = new Font(Font.SERIF, Font.ITALIC, 40); //羅馬字體,斜體,40號g.setFont(font); //獲取字體g.setColor(Color.white);//獲取顏色g.drawString(score+"分",40,60); //畫字符串}public void settishi(Graphics g){ //繪制分數方法Font font1 = new Font(Font.SERIF, Font.BOLD, 25); //羅馬字體,斜體,40號g.setFont(font1); //獲取字體g.setColor(Color.black);//獲取顏色g.drawString("點擊屏幕開始運行",110,400); //畫字符串g.drawString(" 制作人---趙嘉盟",120,430);}public void settishi2(Graphics g){ //繪制分數方法Font font2 = new Font(Font.SANS_SERIF, Font.BOLD, 30); //羅馬字體,斜體,40號g.setFont(font2); //獲取字體g.setColor(Color.red);//獲取顏色g.drawString("點擊屏幕重新開始",100,500); //畫字符串}public void action() throws InterruptedException { //游戲對象運動方法this.addMouseListener(new BirdMouseListener()); //添加鼠標監聽器? while (true){
? switch (state){ //狀態不同,對象運動效果不同
? case START:
? ground.step(); //調用地面運動方法
? bird.fly();break;case RUNNING:? bird.distanceChange();
? ground.step(); //調用地面運動方法
? 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{ //小鳥飛行鼠標控制監聽內部類
? @Override
? public void mousePressed(MouseEvent e) {
? super.mousePressed(e);? switch (state){
? case START:
? state=RUNNING; //鼠標點擊開始運行
? Thread thread = new Thread(music);
? thread.start();
? break;
? case RUNNING:
? bird.upSpeed(); //鼠標點擊屏幕給小鳥一個初始上拋速度
? break;
? case GAMEOVER:
? sc.insertScore(score); //向數據庫插入分數
? List<Score> scores = sc.selectAllScore();//查詢數據庫所有分數
? String message="";
? for (Score score1 : scores) {
? message=message+"時間:"+score1.getTime()+"\n分數:"+score1.getScore()+"\n";
? }
? JOptionPane.showConfirmDialog(jp,message,"實時分數",JOptionPane.WARNING_MESSAGE);
? state=START; //鼠標點擊游戲恢復開始狀態? 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;? }
? }}
}AI寫代碼java運行
十七、Stream
Stream簡介
Java 8 中的 Stream 是對(Collection)集合對象功能的增強,它專注于對集合對象進行各種非常便利、高效的聚合操作
或大批量數據操作。Stream API 借助于同樣新出現的 Lambda 表達式,極大的提高編程效率和程序可讀性。
Stream原理
這種編程風格將要處理的元素集合看作一種流,流在管道中傳輸,并且可以在管道的節點上進行處理,比如篩選,排序,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的
結果。
Stream優點
(1)速度更快
(2)代碼更少(增加了新的語法Lambda表達式)
(3)強大的Stream API
(4)便于并行
(5)最大化減少了空指針異常Optional
Stream的操作三個步驟:
(1)創建Stream,一個數據源(如:集合、數組),獲取一個流;
(2)中間操作,一個中間操作鏈,對數據源的數據進行處理;
(3)終止操作,一個終止操作,執行中間操作鏈,并產生結果。
集合有兩種方式生成流:
stream() ? 為集合創建串行流。
parallelStream() ? 為集合創建并行流
-Stream的的中間操作(intermediate)和最終操作(terminal)都包含的方法:
中間操作(intermediate)
1.filter : 通過設置條件來過濾元素。
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));AI寫代碼java運行
以上代碼使用filter方法過濾出只包含”a”的元素,然后通過forEach將滿足條件的元素遍歷出來。
map : 就是將對應的元素使用給定方法進行轉換。
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));AI寫代碼java運行
在filter的基礎上,給每個元素后面添加字符串”—map”
flatMap:如果流的元素為數組或者Collection,flatMap就是將每個Object[]元素或Collection元素都轉換為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));AI寫代碼java運行
map就是將數組流直接返回,flatMap是將數組流中的每個元素都返回。
.distinct:將集合中的元素去重。
List<String> disList = Arrays.asList("aaa","ddd","bbb","ddd","aaa");disList.stream().distinct().forEach(s-> System.out.println(s));AI寫代碼java運行
sorted:將集合中的元素排序。
List<Integer> integerList = Arrays.asList(2,4,1,3);integerList.stream().sorted().forEach(s-> System.out.println(s));AI寫代碼java運行
可以按照自定義排序:
List<Integer> integerList = Arrays.asList(2,4,1,3);integerList.stream().sorted((s1,s2)->s2.compareTo(s1)).forEach(s-> System.out.println(s));AI寫代碼java運行
peek:生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數即引用的方法A,當Stream每個元素被消費的時候都會先
執行新Stream給定的方法A。peek是中間操作,如果peek后沒有最終操作,則peek不會執行。
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));AI寫代碼java運行
limit:返回Stream的前n個元素。
List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().limit(2).forEach(s-> System.out.println(s));AI寫代碼java運行
skip:刪除Stream的前n個元素。
List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().skip(2).forEach(s-> System.out.println(s));AI寫代碼java運行
終端操作(terminal)
1.forEach:遍歷Stream中的每個元素,前面每個例子都有使用,此處不再演示。
List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().skip(2).forEach(s-> System.out.println(s));AI寫代碼java運行
forEachOrdered:遍歷Stream中的每個元素。
區別: 在串行流(stream)中沒有區別,在并行流(parallelStream)中如果數據源是有序集合,forEachOrdered輸出順序與數據源中順序
一致,forEach則是亂序。
List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.parallelStream().forEachOrdered(s-> System.out.println(s));AI寫代碼java運行
toArray:將流轉換為Object[]或者指定類型的數組。
List<Integer> integerList = Arrays.asList(1,2,3,4);Object[] array = integerList.stream().toArray();String[] strArr = integerList.stream().toArray(String[]::new);AI寫代碼java運行
Stream中的toArray普通情況下和集合中的toArray沒什么區別,但是Stream中的toArray轉換為指定類型的數組。
reduce:將集合中的每個元素聚合成一條數據。有三種情況:
reduce(BinaryOperator accumulator):此處需要一個參數,返回Optional對象:
Optional reduce = integerList.stream().reduce((a, b) -> a + b);
reduce(T identity, BinaryOperator accumulator):此處需要兩個參數,第一個參數為起始值,第二個參數為引用的方法
從起始值開始,每個元素執行一次引用的方法(方法引用的中的兩個參數:第一個參數為上個元素執行方法引用的結果,第二個參數為當前元素)。
List<Integer> integerList = Arrays.asList(1,2,3,4);int integer = integerList.stream().reduce(5,(a, b) -> a + b);System.out.println(integer);AI寫代碼java運行
此例中使用起始值為5,對集合中每個元素求和,可以理解為:5+1+2+3+4=15。
**reduce:**此處需要三個參數。此方法用在并發流(parallelStream)中,啟動多個子線程使用accumulator進行并行計算,最終使用combiner對子線程結果進行合并,返回identity類型的數據。
collect:將流轉換成集合或聚合元素。有兩種情況。接受一個參數和接受三個參數(三個參數在并發流parallelStream中使用),此處介紹一個參數的情況,單個參數接受的參數類型為Collector,Collectors 類實現了很多歸約操作
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());AI寫代碼java運行
此處統計集合中大于1的元素并最終返回list。
min:獲取集合中最小值。
List<Integer> integerList = Arrays.asList(2,4,1,3);Integer min = integerList.stream().min(Integer::compareTo).get();System.out.println(min);AI寫代碼java運行
max:獲取集合中最大值。
List<Integer> integerList = Arrays.asList(2,4,1,3);Integer max = integerList.stream().max(Integer::compareTo).get();System.out.println(max);AI寫代碼java運行
count:獲取集合中元素個數
List<Integer> integerList = Arrays.asList(2,4,1,3);long count = integerList.stream().count();System.out.println(count);AI寫代碼java運行
原文地址:https://blog.csdn.net/m0_57376564/article/details/148143797