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個元素。
圖解冒泡演算法解析過程
程式碼實現
經上述圖解分析:程式碼實現需要
- 雙重迴圈
- 外層迴圈,控制躺數(躺數從0開始,因為陣列索引從0開始,比較好計算)。
躺數 = 陣列長度 - 1
- 內層迴圈,控制每趟比較的次數。
每趟比較的次數 = 陣列的長度 - 當前躺數 - 1
- 外層迴圈,控制躺數(躺數從0開始,因為陣列索引從0開始,比較好計算)。
- 比較相鄰的兩個數字,若位置錯誤,則兩個數字交換
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 }
中分別查詢出元素5
、20
、3
折半查詢,不能簡單的除以2,需要確認最小索引和最大索引。因為可能會多次折半(一次 折半可能查詢不成功),索引最小索引和最大索引可能會變化。所以需要確定以下變數:
最小索引:min = 0
最大索引:max = 陣列的長度 - 1
中間索引:(max + min)/2
圖解分析
圖解分析查詢元素5的過程:
圖解分析查詢元素20的過程:
圖解分析查詢元素3(一個不存在的元素)的過程:
結論
對於要查詢的元素:
- 折半次數不確定(可能是多次),使用while迴圈更簡便。
- 折半過程中,若查詢的值小於mid位置的值,則更改索引max為:max = mid - 1
- 折半過程中,若查詢的值大於mid位置的值,則更改索引min為:min = mid + 1
- 查詢成功,要查詢的元素 和 mid值相等時,終止查詢。
- 元素中不存在該元素,條件是索引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()方法原始碼中追加的空格。