1. 程式人生 > >study-notes(1 JavaSE)-2

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. 多執行緒執行時,在棧記憶體中,每一個執行執行緒都有一片自己所屬的棧記憶體空間,進行方法的壓棧和彈棧.
  2. 每條執行緒都有自己獨立的棧區執行空間,堆區只有一個.
  3. 多執行緒中,每條執行緒的棧空間獨立,堆空間 ‘共享’.
  4. 堆空間中儲存的是 ‘物件’,而物件中儲存的是該物件的 ‘資料’.
  5. 物件的屬性存放在堆區中,區域性變數存放在棧區中,多執行緒操作物件的屬性會存在安全問題,多執行緒操作區域性變數不會存在安全問題.

1.9.3 實現 Runnable 介面比繼承 Thread 類所具有的優勢

  1. 適合多個相同的程式程式碼的執行緒去共享同一個資源.
  2. 可以避免 Java 中的單繼承的侷限性.
  3. 增加程式的健壯性,實現解耦操作,程式碼可以被多個執行緒共享,程式碼和執行緒獨立.
  4. 執行緒池只能放入實現 Runnable 或 Callable 類執行緒,不能直接放入繼承 Thread 的類.

1.10 位元組流與字元流

1.10.1 位元組流與字元流的區別

**位元組流:**所有型別的資料在硬碟中都是以位元組的形式實現儲存的,位元組流可以完成所有型別的 ‘讀寫’ 操作.

**字元流:**字元流僅能操作字元資料,不能操作其他型別資料,字元流 = 位元組流 + 文字編碼表. 字元流寫入資料三部曲 寫入、換行、重新整理

程式以記憶體作為參照物,區分讀和寫.

  1. 資料從硬碟中,流入到記憶體,被稱為讀.
  2. 資料從記憶體中,流入到硬碟,被稱為寫.

位元組流使用方法

/**
     * 複製檔案(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 表示式

**省略原則:**能推導, 就可以省略.

  1. () 引數列表小括號中, 引數型別可以以省略, 如果引數僅有一個, 小括號也可以省略.
  2. {} 如果方法體中有多條語句, 大括號必須書寫. 如果方法體中僅有一條語句, 大括號就可以省略. 不管該方法有沒有返回值, return 關鍵字和最後的分號都可以省略.

使用前提條件:

  1. 必須擁有 函式式介面. (Java語言已經提供了很多函式式介面)
  2. 呼叫的方法必須擁有函式式介面作為方法的引數. (Java語言已經提供了很多方法, 這些方法的引數都是函式式介面)

1.11.3 常用函式式介面

  1. Supplier : 無中生有,抽象方法為 T get() .
  2. Consumer : 有去無回,抽象方法為 void accept(T t),預設方法為 andThen .
  3. Predicate : 元芳,你怎麼看,抽象方法為 boolean test(T t),預設方法為 and、or、negate(or) .
  4. Function<T, R> : 有去有會,抽象方法為 R apply(T t),預設方法為 andThen .

1.12 方法引用

1.12.1 方法引用的型別

  1. 通過物件名引用成員方法.
  2. 通過類名稱引用靜態方法.
  3. 通過 super 引用成員方法.
  4. 通過 this 引用成員方法.
  5. 類的構造器引用.
  6. 陣列的構造器引用.

程式碼示例:

//  通過物件名引用成員方法.

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