study-notes(1 JavaSE)-2
這篇文章是將自己所學技術按模組劃分總結而成的筆記,包含了 JavaSE、JavaWeb(SpringMVC、Spring、MyBatis、SpringBoot、SpringCloud 等)、Linux、Hadoop、MapReduce、Hive、Scala、Spark 等,希望通過這些筆記的總結,不僅能讓自己對這些技術的掌握更加深刻,同時也希望能幫助一些其他熱愛技術的人,這些筆記後續會繼續更新,以後自己學習的其他最新技術,也都會以這樣筆記的形式來保留,這些筆記已經共享到 Github,大家可以在那裡下載到 Markdown 檔案,如果大家在看的時候有什麼問題或疑問,可以通過郵箱與我取得聯絡,或者在下面的評論區留言,同時也可以在 Github 上與我進行互動,希望能與大家一起相互學習,相互進步,共同成長。
1.5.15 HashMap 中重寫 hashCode 和 equals 方法的步驟
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null || this.getClass() != obj.getClass()) return false;
Student student = (Student) obj;
return this.age == student.getAge() && Objects.equals(this.name, student.getName());
}
1.6 泛型
1.6.1 什麼是泛型
泛型是指可以在類或方法中預支地使用未知的型別。一般在建立物件時,將未知的型別確定具體的型別。當沒有指定泛型時,預設型別為Object型別。
1.6.2 使用泛型的好處有哪些
將執行時期的ClassCastException,轉移到了編譯時期變成了編譯失敗。
避免了型別強轉的麻煩。
1.7 多型
1.7.1 多型的前提
繼承或者實現
方法的重寫
父類引用指向子類物件(格式體現)
父類型別 變數名 = new 子類物件;
變數名.方法名();
Fu f = new Zi();
f.method();
當使用多型方式呼叫方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫後方法。
1.7.2 多型的好處
實際開發的過程中,父類型別作為方法形式引數,傳遞子類物件給方法,進行方法的呼叫,更能體現出多型的擴充套件性與便利。
1.7.3 向上轉型與向下轉型
**向上轉型:**父類引用指向子類物件
使用格式:
父類型別 變數名 = new 子類型別();
如:Animal a = new Cat();
**向下轉型:**父類引用指向子類引用
使用格式:
子類型別 變數名 = (子類型別) 父類變數名;
如:Cat c =(Cat) a;
1.8 異常
1.8.1 異常的分類
**編譯時期異常:**checked 異常,在編譯時期,就會檢查,如果沒有處理異常,則編譯失敗(如日期格式化異常).
**執行時期異常:**runtime 異常,在執行時期,檢查異常,執行異常不會被編譯器檢測(不報錯)(如數學異常).
1.8.2 異常的處理方式
**丟擲異常 throw :**throw 用在方法內,用來丟擲一個異常物件,將這個異常物件傳遞到呼叫者處,並結束當前方法的執行.
使用格式:
throw new 異常類名(引數);
**捕獲異常 try … catch … finally :**Java 中對異常有針對性的語句進行捕獲,可以對出現的異常進行指定方式的處理.
使用格式:
try {
編寫可能會出現異常的程式碼
} catch (異常型別 e) {
處理異常的程式碼
// 記錄日誌
// 列印異常資訊
// 繼續丟擲異常
} finally {
關閉資源(硬體檔案資源,資料庫資源,網路資源 ...)
}
**try : ** 該程式碼塊中編寫可能產生異常的程式碼.
catch : 用來進行某種異常的捕獲,實現對捕獲到的異常進行處理.
finally : finally 中的程式碼塊一定會被執行,就是為了 ‘關閉資源’ 而設計,主要用於關閉硬體檔案資源,資料庫資源,網路資源 … ,finally 塊的設計,就是為了解決 catch 塊中 return 語句的問題,在 return 之前會先執行 finally 程式碼塊.
try … catch … finally 參考程式碼如下:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class ExceptionTest1 {
public static void main(String[] args) {
String result = readFile("a.txt");
System.out.println("result = " + result);
}
// 需求 : IO (BufferedReader & BufferedWriter)
// 說明 : 定義一個方法, 讀取指定檔案中的資料, 並返回是否讀取成功字串.
public static String readFile(String fileName) {
// 1. 讀取檔案
// 說明 : 建立 FileReader 時, 會發生一個 `FileNotFoundException` 檔案找不到異常.
// 選擇捕獲異常 : try - catch
FileReader reader = null;
try {
reader = new FileReader(fileName);
} catch (FileNotFoundException e) {
System.out.println("發生了檔案找不到異常, 請檢查檔案路徑或檔名稱.");
// 如果程式進入 catch 語句, 說明程式發生了異常. 檔案讀取失敗了.
return "檔案讀取失敗";// return 之前會先執行 finally 程式碼塊.
} finally {
// finally 程式碼塊就是為 `關閉資源` 而設計. (硬體檔案資源, 資料庫資源, 網路資源 ...)
// 說明 : finally 塊的設計就是為了解決 catch 塊中 return 語句的問題.
System.out.println("finally 程式碼塊被執行 ...");
// 說明 : 如果一個物件的預設值為null, 呼叫時, 必須要做 null 判斷.
if (reader != null) {
try {
reader.close();
} catch (IOException exception) {
// 忽略 ...
}
}
// finally 塊中, 在開發時, 絕對不會出現 return 語句, 因為 finally 塊就是用來關閉資源的, 而不是返回結果的.
// 如果要返回結果, 請在 catch 語句中返回.
// return "哈哈哈哈";
}
return "檔案讀取成功";
}
}
輸出結果 :
發生了檔案找不到異常, 請檢查檔案路徑或檔名稱.
finally 程式碼塊被執行 ...
result = 檔案讀取失敗
1.8.3 try … catch … finally 執行順序的思考
當有多個 catch 時,前面一個 catch 捕獲異常後,後面的 catch 不會再執行.
參考程式碼如下:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class ExceptionTest1 {
public static void main(String[] args) {
String result = readFile("a.txt");
System.out.println("result = " + result);
}
// 需求 : IO (BufferedReader & BufferedWriter)
// 說明 : 定義一個方法, 讀取指定檔案中的資料, 並返回是否讀取成功字串.
public static String readFile(String fileName) {
// 1. 讀取檔案
// 說明 : 建立 FileReader 時, 會發生一個 `FileNotFoundException` 檔案找不到異常.
// 選擇捕獲異常 : try - catch
FileReader reader = null;
try {
reader = new FileReader(fileName);
} catch (FileNotFoundException e) {
System.out.println("發生了檔案找不到異常, 請檢查檔案路徑或檔名稱.");
// 如果程式進入 catch 語句, 說明程式發生了異常. 檔案讀取失敗了.
return "檔案讀取失敗";// return 之前會先執行 finally 程式碼塊.
} finally {
// finally 程式碼塊就是為 `關閉資源` 而設計. (硬體檔案資源, 資料庫資源, 網路資源 ...)
// 說明 : finally 塊的設計就是為了解決 catch 塊中 return 語句的問題.
System.out.println("finally 程式碼塊被執行 ...");
// 說明 : 如果一個物件的預設值為null, 呼叫時, 必須要做 null 判斷.
if (reader != null) {
try {
reader.close();
} catch (IOException exception) {
// 忽略 ...
}
}
// finally 塊中, 在開發時, 絕對不會出現 return 語句, 因為 finally 塊就是用來關閉資源的, 而不是返回結果的.
// 如果要返回結果, 請在 catch 語句中返回.
// return "哈哈哈哈";
}
return "檔案讀取成功";
}
}
輸出結果 :
發生了檔案找不到異常, 請檢查檔案路徑或檔名稱.
finally 程式碼塊被執行 ...
result = 檔案讀取失敗
1.8.4 異常處理過程
如果有異常發生,並沒有進行處理,此時該異常繼續向上拋,該方法由誰呼叫,該異常就拋給誰. 由於 Java 虛擬機器呼叫了 main 方法,所以該異常就拋給了 ‘Java虛擬機器’,當虛擬機器接收到異常時,就輸出異常的相關資訊,然後提前終止程式,退出Java虛擬機器. 當對異常進行處理時,就根據處理方案對異常進行處理.
1.9 多執行緒
1.9.1 並行與併發
**並行:**指兩個或多個事件在同一時刻發生(同時發生).
**併發:**指兩個或多個事件在同一個時間段內發生.
1.9.2 多執行緒執行過程
- 多執行緒執行時,在棧記憶體中,每一個執行執行緒都有一片自己所屬的棧記憶體空間,進行方法的壓棧和彈棧.
- 每條執行緒都有自己獨立的棧區執行空間,堆區只有一個.
- 多執行緒中,每條執行緒的棧空間獨立,堆空間 ‘共享’.
- 堆空間中儲存的是 ‘物件’,而物件中儲存的是該物件的 ‘資料’.
- 物件的屬性存放在堆區中,區域性變數存放在棧區中,多執行緒操作物件的屬性會存在安全問題,多執行緒操作區域性變數不會存在安全問題.
1.9.3 實現 Runnable 介面比繼承 Thread 類所具有的優勢
- 適合多個相同的程式程式碼的執行緒去共享同一個資源.
- 可以避免 Java 中的單繼承的侷限性.
- 增加程式的健壯性,實現解耦操作,程式碼可以被多個執行緒共享,程式碼和執行緒獨立.
- 執行緒池只能放入實現 Runnable 或 Callable 類執行緒,不能直接放入繼承 Thread 的類.
1.10 位元組流與字元流
1.10.1 位元組流與字元流的區別
**位元組流:**所有型別的資料在硬碟中都是以位元組的形式實現儲存的,位元組流可以完成所有型別的 ‘讀寫’ 操作.
**字元流:**字元流僅能操作字元資料,不能操作其他型別資料,字元流 = 位元組流 + 文字編碼表. 字元流寫入資料三部曲 寫入、換行、重新整理
程式以記憶體作為參照物,區分讀和寫.
- 資料從硬碟中,流入到記憶體,被稱為讀.
- 資料從記憶體中,流入到硬碟,被稱為寫.
位元組流使用方法
/**
* 複製檔案(BufferedInputStream & BufferedOutputStream)
* @throws IOException
*/
public static void copyPicture2() throws IOException {
// 建立 File 物件
File origin = new File("file/柳巖.jpg");
File destination = new File("file/柳巖3.jpg");
// 建立檔案輸入、輸出流物件
FileInputStream in = new FileInputStream(origin);
FileOutputStream out = new FileOutputStream(destination);
// 建立高效位元組緩衝輸入、輸出流物件
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(out);
try (in; out) {
// 迴圈讀取原檔案資料並將其複製到目標檔案
int len = -1;
while ((len = bis.read()) != -1) {
bos.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("檔案複製完成");
}
字元流使用方法
/**
* FileReader 的構造方法
* @throws FileNotFoundException
*/
public static void testFileReader() throws IOException {
// 讀取字元資料
// 建立一個字元輸入流
File file2 = new File("file/a.txt");
FileReader reader3 = new FileReader(file2);
// 迴圈讀取資料
char[] c = new char[2];
int read = -1;
while ((read = reader3.read(c)) != -1) {
String str = new String(c, 0, read);
System.out.println(str);
}
// 關閉資源
reader3.close();
}
/**
* FileWriter 的構造方法
* @throws IOException
*/
public static void testFileWriter() throws IOException {
// 字元緩衝流寫入資料,寫入資料三部曲:寫入、換行、重新整理
// 建立一個字元緩衝輸出流物件
File file2 = new File("file/e.txt");
FileWriter fileWriter = new FileWriter(file2, true);
BufferedWriter writer2 = new BufferedWriter(fileWriter);
// 寫入資料
writer2.write("我愛上海明珠塔.");
writer2.newLine();
writer2.flush();
// 關閉資源
writer2.close();
}
1.11 函數語言程式設計
1.11.1 方法中不定陣列傳參的方法
方法定義:
public static int getNum(int... nums) {
System.out.println(Arrays.toString(nums));
}
方法呼叫:
private static void main(String[] args) {
getNum(10, 20);
}
1.11.2 Lambda 表示式
**省略原則:**能推導, 就可以省略.
- () 引數列表小括號中, 引數型別可以以省略, 如果引數僅有一個, 小括號也可以省略.
- {} 如果方法體中有多條語句, 大括號必須書寫. 如果方法體中僅有一條語句, 大括號就可以省略. 不管該方法有沒有返回值, return 關鍵字和最後的分號都可以省略.
使用前提條件:
- 必須擁有
函式式介面
. (Java語言已經提供了很多函式式介面) - 呼叫的方法必須擁有函式式介面作為方法的引數. (Java語言已經提供了很多方法, 這些方法的引數都是函式式介面)
1.11.3 常用函式式介面
- Supplier : 無中生有,抽象方法為 T get() .
- Consumer : 有去無回,抽象方法為 void accept(T t),預設方法為 andThen .
- Predicate : 元芳,你怎麼看,抽象方法為 boolean test(T t),預設方法為 and、or、negate(or) .
- Function<T, R> : 有去有會,抽象方法為 R apply(T t),預設方法為 andThen .
1.12 方法引用
1.12.1 方法引用的型別
- 通過物件名引用成員方法.
- 通過類名稱引用靜態方法.
- 通過 super 引用成員方法.
- 通過 this 引用成員方法.
- 類的構造器引用.
- 陣列的構造器引用.
程式碼示例:
// 通過物件名引用成員方法.
public class Assistant {
public void dealFile(String file) {
System.out.println("幫忙處理 <" + file + "> 檔案.");
}
}
@FunctionalInterface
public interface WorkHelper {
void help(String file);
}
public class Test2 {
public static void main(String[] args) {
// 呼叫方法 : Lambda 表示式
work(s -> System.out.println("幫忙處理 <" + s + "> 檔案."));
// 呼叫方法 : 物件引用物件方法
Assistant assistant = new Assistant();
work(assistant::dealFile);
}
public static void work(WorkHelper helper) {
helper.help("機密");
}
}
輸出結果 :
幫忙處理 <機密檔案> 檔案.
幫忙處理 <機密檔案> 檔案.
// 通過類名稱引用靜態方法.
public class StringUtils {
// 方法 : 判斷是否為空
public static boolean isBlank(String str) {
// 條件1 : str 引數不能為 null
// 條件2 : str 引數取出前後空格不能拿為空字串
return str == null || "".equals(str.trim());
}
}
@FunctionalInterface
public interface StringChecker {
// 抽象方法
boolean checkString(String str);
}
public class Test4 {
public static void main(String[] args) {
// 呼叫方法 : Lambda 表示式
stringCheck(" ", s -> s == null || "".equals(s.trim()));
// 呼叫方法 : 靜態方法引用
stringCheck(" ", StringUtils::isBlank);
}
public static void stringCheck(String str, StringChecker stringChecker) {
boolean result = stringChecker.checkString(str);
System.out.println("傳入的字串是否為空 : " + result);
}
}
輸出結果 :
傳入的字串是否為空 : true
傳入的字串是否為空 : true
// 通過 super 引用成員方法.
@FunctionalInterface
public interface Greetable {
void greet();
}
public class Human {
public void sayHello() {
System.out.println("Hello!");
}
}
public class Man extends Human {
public void sayHi() {
// 呼叫方法 : Lambda 表示式
method(() -> System.out.println("Hello!"));
// 呼叫方法 : 執行父類中的 sayHello 方法.
method(() -> super.sayHello());
// 呼叫方法 : super引用, 使用父類中的 sayHello 方法.
method(super::sayHello);
}
public void method(Greeting greeting) {
greeting.greet();
System.out.println("I am a Man.");
}
}
// 通過 this 引用成員方法.
@FunctionalInterface
public interface Richable {
void buy();
}
public class Husband {
// 行為 : 變得快樂
public void beHappy() {
// 結婚吧
merry(() -> System.out.println("買套房子."));
merry(() -> this.buyCar());
merry(this::changeWife);
}
// 行為 : 結婚 (需要變得有錢, 必須要買東西)
private void merry(Richable richable) {
richable.buy();
}
// 行為 : 買套方法
private void buyHouse() {
System.out.println("買套房子.");
}
// 行為 : 買輛車子
private void buyCar() {
System.out.println("買輛車子.");
}
// 行為 : 換個老婆
private void changeWife() {
System.out.println("換個老婆. 開個玩笑.");
}
}
public class Test {
public static void main(String[] args) {
Husband husband = new Husband();
husband.beHappy();
}
}
輸出結果 :
買套房子.
買輛車子.
換個老婆. 開個玩笑.
// 類的構造器引用.
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@FunctionalInterface
public interface PersonBuilder {
// 抽象方法
Person builderPerson(String name);
}
public class Test1 {
public static void main(String[] args) {
printPerson("張三丰", name -> new Person(name));
printPerson("張三丰", Person::new);
}
// 定義方法 :
public static void