1. 程式人生 > 實用技巧 >JavaSE第13篇:常用API、氣泡排序、二分查詢、正則

JavaSE第13篇:常用API、氣泡排序、二分查詢、正則

目錄

核心概述:本篇我們將會學習常用的API,其中有Object類、日期相關操作類、陣列相關操作類Arrays、正則表示式;同時也會學習關於陣列的一些常用演算法,其他中有氣泡排序、二分查詢法。

第一章:Object類

1.1-概述(瞭解)

java.lang.Object類是Java語言中的根類,每個類都使用 Object

作為超類。所有物件(包括陣列)都實現這個類的方法。

如果一個類沒有特別指定父類, 那麼預設則繼承自Object類。例如:

public class MyClass /*extends Object*/ {
  	// ...
}

1.2-本地方法(瞭解)

在Object類的原始碼中定義了native修飾的方法,native修飾的方法稱為本地方法。

本地方法的特點

  • 被native修飾的方法,非Java語言編寫,是由C++語言編寫。
  • 本地方法在執行時期進入本地方法棧記憶體,本地方法棧是一塊獨立記憶體的區域。
  • 本地方法的意義是和作業系統進行互動。

如Object類中部分原始碼:

private static native void registerNatives();
static {
    registerNatives();
}

當程式執行的時候,Object類會最先被載入到記憶體中。類進入記憶體後首先載入自己的靜態成員,static程式碼塊中呼叫了本地方法registerNatives(),和作業系統進行互動。

檢視Object類原始碼:在IDEA編輯器中,新建一個類,在類中一個main方法中new Object(),滑鼠停留在Object類上,按住ctrl + 點選滑鼠,進入Object類原始碼中。

1.3-toString方法(理解)

認識toString方法

方法宣告:public String toString():返回該物件的字串表示。

Object類toString()方法原始碼:

public String toString() {
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

原始碼分析:

  • getClass().getName()返回類的全限定名字。
  • hashCode()方法返回int值,可以暫時理解為物件的記憶體地址。
  • Integer.toHexString()將int型別的值轉成十六進位制。
  • 因此呼叫物件的toString()方法將看到記憶體的地址值。

建立Person類,並呼叫方法toString()

public static void main(String[] args){
    Student stu = new Student();
    String str = stu.toString();
    System.out.println(str);    // www.penglei666.com.demo01.Student@5f2050f6
    System.out.println(stu);    // www.penglei666.com.demo01.Student@5f2050f6
}

通過程式執行,得到結論,在輸出語句中列印物件,就是在呼叫物件的toString()方法

重寫toString方法

由於toString方法返回的結果是記憶體地址,而在開發中,記憶體地址並沒有實際的應用價值,經常需要按照物件的屬性得到相應的字串表現形式,因此也需要重寫它。

public class Student {  
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Student:" + name + "," + age;
    }
    // 省略構造器與Getter Setter
}

1.4-equals方法(理解)

認識equals方法

方法宣告:public boolean equals(Object obj):指示其他某個物件是否與此物件“相等”。

Object類equals()方法原始碼:

public boolean equals(Object obj) {
    return (this == obj);
}

原始碼分析:

  • this是當前物件,哪個物件呼叫的equals方法就表示哪個物件。
  • obj表述傳遞的引數,引數型別Object,可以傳遞任意型別物件。
  • this==obj 比較兩個物件的記憶體地址是否相同

equals方法預設比較兩個物件的記憶體地址是否相同,相同則返回true。

重寫equals方法

實際應用中,比較記憶體地址是否相同並沒有意義,我們可以定義物件自己的比較方式,比較物件中成員變數的值是否相同。需要對方法進行重寫。

需求:重寫equals()方法,比較兩個物件中姓名和年齡是否相同,如果姓名和年齡都相同返回true,否則返回false。

public class Person {
    private String name;
    private int age;
    
    public boolean equals(Object obj){
        //判斷兩個物件地址弱相同,即為同一個物件
        if(this == obj)
            return true;
        //obj物件為空,無需比較,返回false
        if(obj == null)
            return  false;
        //obj如果是Person型別物件,則強制轉換
        if(obj instanceof Person){
            Person person = (Person)obj;
            //比較兩個物件的name屬性和age屬性,如果相等,返回true
            return this.name.equals(person.name) && this.age == person.age;
        }
        return false;
    }
}

第二章:日期操作類

2.1-Date類(記憶)

構造方法

  • public Date():從執行程式的此時此刻到時間原點經歷的毫秒值,轉換成Date物件,分配Date物件並初始化此物件,以表示分配它的時間(精確到毫秒)。
  • public Date(long date):將指定引數的毫秒值date,轉換成Date物件,分配Date物件並初始化此物件,以表示自從標準基準時間(稱為“曆元(epoch)”,即1970年1月1日00:00:00 GMT)以來的指定毫秒數。
public class Test01 {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date);    // Fri Jul 31 11:59:20 CST 2020
        Date date2 = new Date(0);
        System.out.println(date2);   // Thu Jan 01 08:00:00 CST 1970
    }
}

常用方法

  • public long getTime() 把日期物件轉換成對應的時間毫秒值。
  • public void setTime(long time) 把方法引數給定的毫秒值設定給日期物件。
public class Test02 {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());        // 1596168137169
        date.setTime(15961681371611L);
        System.out.println(date);                  // Tue Oct 22 00:22:51 CST 2475
    }
}

毫秒值(時間戳)和日期物件互相轉換

日期物件轉換為毫秒值:日期物件.getTime();

毫秒值轉換為日期物件:new Date(long time);

獲取當前時間的毫秒值:System.currentTimeMillis()

public class Test02 {
    public static void main(String[] args) {
        // 從1970年到現在的總毫秒值(也叫時間戳)
        long millis = System.currentTimeMillis();
        System.out.println(millis);       // 1596168719242
        // 毫秒值轉日期物件
        Date date = new Date(millis);
        System.out.println(date);         // Fri Jul 31 12:11:59 CST 2020
        // 日期物件轉毫秒值
        long millis2 = date.getTime();
        System.out.println(millis2);      // 1596168719242
    }
}

2.2-DateFormat類(記憶)

概述

java.text.DateFormat 是日期/時間格式化子類的抽象類,我們通過這個類可以幫我們完成日期和文字之間的轉換,也就是可以在Date物件與String物件之間進行來回轉換。

  • 格式化:按照指定的格式,把Date物件轉換為String物件。
  • 解析:按照指定的格式,把String物件轉換為Date物件

構造方法

由於DateFormat為抽象類,不能直接使用,所以需要常用的子類java.text.SimpleDateFormat。這個類需要一個模式(格式)來指定格式化或解析的標準。構造方法為:

  • public SimpleDateFormat(String pattern):用給定的模式和預設語言環境的日期格式符號構造SimpleDateFormat。

引數pattern是一個字串,代表日期時間的自定義格式。

常用的格式規則為:

標識字母(區分大小寫) 含義
y
M
d
H
m
s

備註:更詳細的格式規則,可以參考SimpleDateFormat類的API文件。

轉換方法

  • String format(Date date) 傳遞日期物件,返回格式化後的字串。
  • Date parse(String str) 傳遞字串,返回日期物件。
  public static void main(String[] args) throws ParseException {
    // 建立當前日期物件
    Date date = new Date();
    // 建立格式化物件
    DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    String dateStr = format.format(date);
    System.out.println(dateStr);   // 2019-12-07 05:37:53
    // 把格式化日期轉換為日期物件
    Date date2 = format.parse("2100-12-12 12:12:12");
    System.out.println(date2);   // Sun Dec 12 00:12:12 CST 2100
  }

2.3-Calendar類(記憶)

概述

java.util.Calendar是日曆類,在Date後出現,替換掉了許多Date的方法。該類將所有可能用到的時間資訊封裝為靜態成員變數,方便獲取。日曆類就是方便獲取各個時間屬性的。

獲取日曆物件

Calendar是抽象類,不能建立物件,需要使用子類物件。java.util.GregorianCalendar類是Calendar的子類,但是建立日曆物件需要根據本地的時區,語言環境來建立,比較困難,Calendar類提供了靜態方法 getInstance()直接獲取子類的物件。

public static Calendar getInstance():使用預設時區和語言環境獲得一個日曆。

  public static void main(String[] args) {
    // 獲取日曆物件
    Calendar calendar = Calendar.getInstance();
 }

常用方法

常用方法

  • public int get(int field):返回給定日曆欄位的值。
  • public void set(int field, int value):將給定的日曆欄位設定為給定值。
  • public abstract void add(int field, int amount):根據日曆的規則,為給定的日曆欄位新增或減去指定的時間量。
  • public Date getTime():返回一個表示此Calendar時間值(從曆元到現在的毫秒偏移量)的Date物件。

field:Calendar類中提供很多成員常量,代表給定的日曆欄位:

欄位值 含義
YEAR
MONTH 月(從0開始,可以+1使用)
DAY_OF_MONTH 月中的天(幾號)
HOUR 時(12小時制)
HOUR_OF_DAY 時(24小時制)
MINUTE
SECOND
DAY_OF_WEEK 週中的天(周幾,週日為1,可以-1使用)

程式碼:

  public static void main(String[] args) {
    // 獲取日曆物件
    Calendar calendar = Calendar.getInstance();
    // 獲取當前日期的部分資料
    System.out.println(calendar.get(Calendar.YEAR));
    System.out.println(calendar.get(Calendar.MONTH));    // 月份 0-11
    System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
    System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
    System.out.println(calendar.get(Calendar.MINUTE));
    System.out.println(calendar.get(Calendar.SECOND));
    System.out.println(calendar.get(Calendar.DAY_OF_WEEK));

  }

練習

需求:獲取任意一年的二月有多少天

分析:

  • 可以將日曆設定到任意年的三月一日
  • 向前偏移一天
  • 獲取偏移後的日曆即可

程式碼:

public static void main(String[] args) {
    //鍵盤錄入任意的年份
    Scanner sc = new Scanner(System.in);
    System.out.println("請輸入年:");
    int year = sc.nextInt();

    //設定日曆物件的年、月、日
    Calendar c = Calendar.getInstance();
    c.set(year, 2, 1);

    //3月1日往前推一天,就是2月的最後一天
    c.add(Calendar.DATE, -1);

    //獲取這一天輸出即可
    int date = c.get(Calendar.DATE);
    System.out.println(year + "年的2月份有" + date + "天");

第三章:System類

3.1-概述(瞭解)

java.lang.System類中提供了大量的靜態方法,可以獲取與系統相關的資訊或系統級操作。System類私有修飾構造方法,不能建立物件,直接類名呼叫。

3.2-常用方法(記憶)

3.3-測試程式執行時長(練習)

需求:在控制檯輸出1-10000,計算這段程式碼執行了多少毫秒

public static void main(String[] args) {
    //獲取當前時間毫秒值
    System.out.println(System.currentTimeMillis()); 
    // 計算程式執行時間
    long start = System.currentTimeMillis();
    for (int i = 1; i <= 10000; i++) {
        	System.out.println(i);
    }
    long end = System.currentTimeMillis();
    System.out.println("共耗時毫秒:" + (end - start));
 }  

3.4-arryCopy方法(記憶)

引數

  • Object src:要複製的資料來源陣列
  • int srcPost:資料來源陣列的開始索引
  • Object dest:複製後的目的陣列
  • int destPos:目的陣列開始索引
  • int length:要複製的陣列元素的個數

程式碼

將源陣列中從1索引開始,複製3個元素到目的陣列中

public static void main(String[] args){
    int[] src = {1,2,3,4,5};
    int[] dest = {6,7,8,9,0};
    //將源陣列中從1索引開始,複製3個元素到目的陣列中
    System.arraycopy(src,1,dest,0,3);
    for(int i = 0 ; i < dest.length;i++){
    System.out.println(dest[i]);
}

3.5-gc方法(瞭解)

執行垃圾回收器,JVM將從堆記憶體中清理物件,清理物件的同時會呼叫物件的finalize()方法,JVM的垃圾回收器是通過另一個執行緒開啟的,因此程式中的效果並不明顯。

Person類

public class Person {
    protected void finalize() throws Throwable {
        System.out.println("物件被回收");
}

測試類:

public static void main(String[] args){
	new Person();
	new Person();
	new Person();
	new Person();
	new Person();
	new Person();
	System.gc();
}

第四章:陣列的氣泡排序

陣列的排序,是將陣列中的元素按照大小進行排序,預設都是以升序的形式進行排序,陣列排序的方法很多,我們講解的是陣列的氣泡排序。

排序,都要進行陣列 元素大小的比較,再進行位置的交換。氣泡排序法是採用陣列中相鄰元素進行比較換位。

4.1-什麼是氣泡排序(瞭解)

氣泡排序(Bubble Sort),是一種較簡單的排序演算法。

重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果順序錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素列已經排序完成。

這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端一樣,故名“氣泡排序”。

實 質:把小(大)的元素往前(後)調

4.2-氣泡排序的分析和實現(練習)

需求

對陣列 { 3, 6, 4, 2, 1, 5 } 進行從小到大的排序。

該陣列共6個元素。

圖解冒泡演算法解析過程

程式碼實現

經上述圖解分析:程式碼實現需要

  1. 雙重迴圈
    • 外層迴圈,控制躺數(躺數從0開始,因為陣列索引從0開始,比較好計算)。 躺數 = 陣列長度 - 1
    • 內層迴圈,控制每趟比較的次數。每趟比較的次數 = 陣列的長度 - 當前躺數 - 1
  2. 比較相鄰的兩個數字,若位置錯誤,則兩個數字交換
public class Test06 {
    public static void main(String[] args) {
        // 定義要排序的陣列
        int[] arr = {3, 6, 4, 2, 1, 5};
        System.out.println("陣列排序前:" + showArray(arr));
        /**
         * 氣泡排序
         */
        for (int i = 0; i < arr.length - 1; i++) {
            // 外層迴圈控制躺數
            // i表示當前躺數
            for (int j = 0; j < arr.length - i - 1; j++) {
                // 內層迴圈控制每趟比較的次數
                // 相鄰的兩個數字開始比較:arr[j] 和 arr[j+1]
                if (arr[j] > arr[j + 1]) {
                    // 位置錯誤發生交換
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        System.out.println("陣列排序後:" + showArray(arr));
    }
    /**
     * 檢視陣列
     */
    public static String showArray(int[]arr) {
        StringBuilder sb = new StringBuilder();
        sb.append("{ ");
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                sb.append(arr[i]);
            } else {
                sb.append(arr[i] + ", ");
            }
        }
        sb.append(" }");
        return sb.toString();
    }
}

執行結果:

/*
    陣列排序前:{ 3, 6, 4, 2, 1, 5 }
    陣列排序後:{ 1, 2, 3, 4, 5, 6 }
*/

第五章:陣列的二分查詢

目的:從陣列中查詢一個元素,比較高效的查詢方式,就是二分查詢。

5.1-什麼是二分查詢(瞭解)

二分查詢也稱折半查詢(Binary Search),它是一種效率較高的查詢方法,但是,折半查詢要求查詢的陣列的元素是有序排列的

查詢過程如下:

  • 首先,假設陣列中的元素是按升序排列

  • 將陣列中間位置記錄的元素值與要查詢的元素值比較,如果兩者相等,則查詢成功;

  • 否則利用中間位置的記錄將陣列分成前、後兩個子陣列;

  • 如果中間位置記錄的元素值大於要查詢元素值,則進一步查詢前一子陣列,否則進一步查詢後一子陣列。

  • 重複以上過程,直到找到滿足條件的記錄,使查詢成功,或直到子表不存在為止,此時查詢不成功。

5.2-二分查詢的分析和實現(練習)

需求

從陣列{ 2, 5, 9, 10, 18, 20, 24 }中分別查詢出元素5203

折半查詢,不能簡單的除以2,需要確認最小索引和最大索引。因為可能會多次折半(一次 折半可能查詢不成功),索引最小索引和最大索引可能會變化。所以需要確定以下變數:

最小索引:min = 0

最大索引:max = 陣列的長度 - 1

中間索引:(max + min)/2

圖解分析

圖解分析查詢元素5的過程

圖解分析查詢元素20的過程:

圖解分析查詢元素3(一個不存在的元素)的過程:

結論

對於要查詢的元素:

  1. 折半次數不確定(可能是多次),使用while迴圈更簡便。
  2. 折半過程中,若查詢的值小於mid位置的值,則更改索引max為:max = mid - 1
  3. 折半過程中,若查詢的值大於mid位置的值,則更改索引min為:min = mid + 1
  4. 查詢成功,要查詢的元素 和 mid值相等時,終止查詢。
  5. 元素中不存在該元素,條件是索引min大於索引max時,終止查詢。

程式碼

public class Test06 {
    public static void main(String[] args) {
        // 定義陣列
        int[]arr = { 2, 5, 9, 10, 18, 20, 24 };
        // 查詢元素5
        int index1 = binarySearch(arr,5);
        System.out.println("從陣列:{ 2, 5, 9, 10, 18, 20, 24 }查詢元素5的索引是:" + index1);
        // 查詢元素20
        int index2 = binarySearch(arr,20);
        System.out.println("從陣列:{ 2, 5, 9, 10, 18, 20, 24 }查詢元素20的索引是:" + index2);
        // 查詢元素3
        int index3 = binarySearch(arr,3);
        System.out.println("從陣列:{ 2, 5, 9, 10, 18, 20, 24 }查詢元素3的索引是:" + index3);
    }

    /**
     * 二分查詢,返回要查詢的元素的索引
     * @param arr 陣列
     * @param key 要查詢的元素值
     * @return 查詢的結果,-1表示不存在該元素
     */
    public static int binarySearch(int[]arr, int key) {
        // 最小索引初始化為0
        int min = 0;
        // 最大索引初始化為陣列的長度 -1
        int max = arr.length - 1;
        // 中間索引
        int mid = 0;
        // 定義查詢後的元素的索引,預設為-1,表示不存在該元素
        int index = -1;
        // 迴圈查詢
        while (min <= max){
            mid = (max + min)/2;
            if(key > arr[mid]) {
                min = mid + 1;
            }else if(key < arr[mid]){
                max = mid - 1;
            }else {
               index = mid;
               break;
            }
        }
        return index;
    }

}

查詢結果:

/*
    從陣列:{ 2, 5, 9, 10, 18, 20, 24 }查詢元素5的索引是:1
    從陣列:{ 2, 5, 9, 10, 18, 20, 24 }查詢元素20的索引是:5
    從陣列:{ 2, 5, 9, 10, 18, 20, 24 }查詢元素3的索引是:-1
*/

第六章:Arrays類

我們發現運算元組時,不論是排序、二分查詢等,每次實現都需要自己分析寫程式碼。在開發過程中,影響開發效率。所以Java中提供了運算元組的工具類Arrays。

6.1-概述(瞭解)

java.util.Arrays 此類包含用來運算元組的各種方法,比如排序和搜尋等。Arrays類私有修飾構造方法,其所有方法均為靜態方法,呼叫起來非常簡單。

6.2-常用方法(記憶)

瞭解更多方法可查詢API

    public static void main(String[] args) {
        // 定義陣列
        int[]arr = { 2, 9, 5, 10, 24, 20, 18 };
        // 排序
        Arrays.sort(arr);
        // 排序後的陣列
        System.out.println(Arrays.toString(arr));
        // 二分查詢元素5的索引
        System.out.println(Arrays.binarySearch(arr,5));
        // 二分查詢元素20的索引
        System.out.println(Arrays.binarySearch(arr,20));
        // 二分查詢元素3的索引
        System.out.println(Arrays.binarySearch(arr,3));
    }

第七章:正則表示式

在實際開發中,我們經常會對一些字串做驗證,比如驗證郵箱格式、手機號碼、身份證號碼等。此時,最有效的驗證方式就是正則表示式,只需要用正則描述驗證規則,然後直接匹配字串即可。

7.1-概述(瞭解)

正則表示式是對字串操作的一種規則,事先定義好一些字串,這些字串稱為規則字串,使用規則字串表達一些邏輯功能。

例如:指定一個字串[email protected],判斷出這個字串是否符合電子郵件的規則。使用字串String物件的方法是可以完成的,但是非常複雜,若使用正則表示式則會非常的簡單實現。

7.2-正則規則-字元類(記憶)

在中括號中定義的字元

7.3-正則規則-預定義字元類(記憶)

預定義字元,具有特殊含義的字元。

注意:對於\w、\d、\W、\D 在java中表示時用雙槓如\\w表示,因為\在java的字串中表示轉義。

7.4-正則規則-數量詞(記憶)

7.5-正則規則-分組(記憶)

分組,就是將多個字元看做一個整體,用小括號表示。

比如:"(李){4}" 表示字元“李”必須出現四次

比如:"(李小龍){4}" 表示字元"李小龍"必須連續出現四次

7.6-正則規則-轉義符(記憶)

對於,具有特殊含義的字元若表示為普通字元,可以使用轉義符\,如.就在正則中表示任意字元,但若就是表示為普通的點的話,則需要轉義\. java字串中表示用\\.

7.5-Java中使用正則表示式(練習)

String類matches方法使用正則表示式

方法:boolean matches(String regex)傳遞正則表示式規則,檢測字串是否匹配正則表示式規則,匹配返回true。

需求:檢查手機號,檢查郵件地址。

分析:

  • 手機號:只能1開頭,第二位可以是345678任意一個,第三位開始全數字,總長11位。
  • 郵件地址:@前面可以是數字,字母,下劃線。@後面是字母和.。
public static void main(String[] args){
    //【驗證手機號碼】
    String tel = "13800138000";
    // 規則
    String telRegex = "1[345678][0-9]{9}";
    // 檢測
    boolean flag = tel.matches(telRegex);
    System.out.println(flag); 
    //【驗證郵件地址】
    String email = "[email protected]";
    String emailReg = "\\w+@(\\w+\\.)+\\w+";
    flag = email.matches(emailReg);
    System.out.println(flag);
}

String類split方法使用正則表示式

方法:String[] split(String regex)傳遞正則表示式規則,以正則規則對字串進行切割,返回陣列。

public static void main(String[] args){
    String str1 = "ab  a   bbb  abc   aa      c";
    //對空格進行切割
    String[] strArr =str1.split(" +");
    System.out.println(Arrays.toString(strArr));

    String str2 = "192.168.1.121";
    strArr = str2.split("\\.");
    System.out.println(Arrays.toString(strArr));
}

注意:輸出陣列元素時會看到存在一個多餘空格,Arrays.toString()方法原始碼中追加的空格。