(小白學JAVA之)Java高階特性知識點梳理

Helltaker發表於2020-08-24

集合框架和泛型

用陣列儲存多個同型別的資料,會存在如下一些明顯的缺陷:

  • 陣列長度固定不變,不能很好地適應元素數量動態變化的情況
  • 可通過陣列名.leng()獲取陣列的長度,卻無法直接獲取陣列中實際儲存的元素個數
  • 陣列採用在記憶體中分配連續空間的儲存方式儲存,根據元素資訊查詢時效率比較低,需要多次比較

Java集合框架提供了一套效能優良、使用方便的介面和類,它們都位於java.util包中,其主要內容及彼此之間的關係如下圖所示:
在這裡插入圖片描述
Java的集合類主要由Map介面和Collection介面派生而來,其中Collection介面有兩個常用的子介面,即List介面和Set介面。

List介面

可以儲存一組不唯一、無序的物件;List介面常用的實現類有ArrayList和LinkedList

ArrayList

ArrayList類底層為動態陣列,遍歷元素更快,改變值也就更快;它可以新增任何型別的資料,並且新增的資料都將轉換成Object型別

ArrayList類的常用方法

在這裡插入圖片描述

具體實現步驟

1.匯入ArrayList類
2.建立ArrayList物件,並新增資料
3.判斷集合中是否包含某元素
4.移除索引為0的元素
5.把索引為1的元素替換為其他元素
6.輸出某個元素所在的索引位置
7.清空ArrayList集合中的資料
8.判斷ArrayList集合中是否包含資料

public static void main(String[] args){
	ArrayList list=new ArrayList(); // 1
	list.add("張三");
	list.add("李四");
	list.add("王五"); // 2
	//判斷集合中是否包含"小劉" 3
	System.out.println(list.contains("小劉")); //輸出false
	// 4
	list.remove(0);
	// 5
	list.set(1, "黃蓉");
	// 6
	System.out.println(list.indexOf("小龍女")) //沒有該元素,輸出-1
	// 7
	list.clear();
	// 8
	System.out.println(list.isEmpty()); //第7步已經清空,這裡輸出true
	//遍歷
	for (int i=0; i<list.size(); i++){
		String name = (String)list.get(i);
		System.out.println(name);
	}
	for (Object obj:list){ //增強for
		String name = (String)obj;
		System.out.println(name);
	}
}

LinkedList

LinkedList類底層是雙向連結串列結構,插入和刪除更快。它支援實現所有List解耦可選的列表的操作,並允許元素值是任何資料,包括null

LinkedList類的常用方法

LinkedList除了包含ArrayList類所包含的方法外,還提供一些自身特有的方法
在這裡插入圖片描述

具體實現步驟

1.建立LinkedList物件,並新增資料
2.新增頭條和末條元素
3.獲取頭條和末條元素
4.刪除頭條和末條元素

NewTitle car = new NewTitle(1,"汽車","管理員");
NewTitle medical = new NewTitle(2,"醫學","管理員");
NewTitle fun = new NewTitle(3,"娛樂","管理員");
NewTitle gym = new NewTitle(4,"體育","管理員");

// 建立儲存新聞標題的集合物件並新增資料
LinkedList newsTitleList = new LinkedList();
newsTitleList.add(car);
newsTitleList.add(medical);

// 2
newsTitleList.addFirst(fun);
newsTitleList.addLast(gym);

// 3
NewTitle first = (NewTitle) newsTitleList.getFirst();
NewTitle last = (NewTitle) newsTitleList.getLast();

// 4
newsTitleList.removeFirst();
newsTitleList.removeLast();

Set介面

Set介面可以儲存一組唯一、無序的物件,它常用的實現類有HashSet

HashSet

HashSet集合的特點如下:

  • 集合內的元素是無序排列
  • HashSet類是非執行緒安全
  • 允許集合元素值為null

HashSet類的常用方法

方法作用
boolean add(Object o)如果Set中尚未包含指定元素o,則新增指定元素o
void clear()從Set中移除所有元素
int size返回Set中的元素的數量
boolean isEmpty()如果Set不包含任何元素,則返回true
boolean contains(Object o)如果Set包含指定元素o,則返回true
boolean remove(Object o)如果指定元素o存在於Set中,則將其移除

注意!Set介面不存在 get() 方法!

具體實現步驟

1.建立HashSet物件,並新增資料
2.獲取新聞標題的總數
3.判斷集合中是否包含汽車新聞標題
4.移除物件
5.判斷集合是否為空
6.遍歷集合

NewTitle car = new NewTitle(1,"汽車","管理員");
NewTitle medical = new NewTitle(2,"醫學","管理員");
// 1
Set newsTitleList = new HashSet();
newsTitleList.add(car);
newsTitleList.add(medical);
// 2
newsTitleList.size();
// 3
newsTitleList.contains(car);
// 4
newsTitleList.remove(medical);
// 5
newsTitleList.isEmpty();
// 6
for (Object obj:newsTitleList){
	NewTitle title = (NewTitle) obj;
	System.out.println(title.getTitleName());
}

Iterator介面

Iterator介面表示對集合進行迭代的迭代器。Iterator介面為集合而生,專門實現集合的遍歷。此介面主要有如下兩個方法:

  • hasNext() 判斷是否存在下一個可訪問的元素,如果有元素可以迭代,則返回true
  • next() 返回要訪問的下一個元素

使用Iterator介面遍歷List類集合:(Set同理)

Iterator it = list.iterator();
while (it.hasNext()){
	System.out.println(it.next());
}

Map介面

Map介面儲存一組成對的鍵(key)— 值(value)物件,提供key到value的對映,通過key來檢索。Map介面中的key不要求有序,不允許重複。value同樣不要求有序,但允許重複。

Map介面的常用用法

在這裡插入圖片描述

HashMap實現步驟

最常用的Map實現類是HashMap,其優點是查詢指定元素效率高。

1.匯入HashMap類
2.建立HashMap物件
3.呼叫HashMap物件的put()方法,向集合中新增資料
4.輸出學員個數
5.輸出鍵集
6.判斷是否存在“Jack”這個鍵,如果存在,則根據鍵獲取相應的值

// 1
Student student1 = new Student("李明","男");
Student student2 = new Student("劉麗","女");
// 2
Map students = new HashMap();
// 3
students.put("Jack", student1);
students.put("Rose", student2);
// 4
students.size();
// 5
students.keySet();
// 6
String key = "Jack";
if (students.containsKey(key)) {
	Student student = (Student) students.get(key);
	System.out.println("英文名為"+key+"的學員姓名:"+student.getName());
}

遍歷HashMap集合

1.遍歷:使用entrySet方法獲取鍵值對的集合

Set set = students.entrySet();
Iterator itr = set.iterator();
while (itr.hasNext()){
	System.out.println(itr.next());
}

2.遍歷鍵集:鍵集用Set儲存

for (Object key:students.keySet()) {
	System.out.println(key.toString());
}

3.遍歷值集:值集用Collection儲存

Collection values = map.values();
for (Object value : values) {
	System.out.println(value);
}

Collections類

Collections類是Java提供的一個集合操作工具類,它包含了大量的靜態方法,用於實現對集合元素的排序、查詢和替換等操作。

注意!Collections和Collection是不同的,前者是集合的操作類,後者是集合介面。

Collections類常用方法

以下方法皆為靜態方法:
sort() 排序
binarySearch() 查詢
max()\min() 查詢最大\最小值

Comparable介面

通過重寫compareTo()方法,用來實現比較大小
定義語句:int compareTo(Object obj);
引數:obj即將要比較的物件
返回值:負整數、零或正整數,根據此物件是小於、等於還是大雨指定物件返回不同的值

例項:

public int compareTo(Object obj) {
	Student student = (Student) obj;
	//如果學號相同,那麼兩者就是相等的
	if (this.number==student.getNumber){
		return 0;
	//如果這個學生的學號大於傳入學生的學號
	} else if (this.number>student.getNumber()){
		return 1;
	//如果這個學生的學號小於傳入學生的學號
	} else {
		return -1;
	}
}

元素之間可以比較大小之後,就可以使用Collections類的sort()方法對元素進行排序操作了。Map介面本身是無序的,所以不能進行排序。可以對List介面進行排序,但注意必須是實現了Comparable介面的元素才可以。

ArrayList list = new ArrayList();
list.add(student1);
list.add(student2);
list.add(student3);
//sort()方法排序
Collections.sort(list);
//binarySearch()方法查詢
int index = Collections.binarySearch(list, students3);

替換集合元素

如果需要把一個List集合中的所有元素都替換為相同的元素,則可以使用Collections類的靜態方法fill()來實現

Collections.fill(list, "李明");

泛型

泛型的本質是引數化型別。Java語言引入泛型的好處是安全簡單,且所有強制轉換都是自動和隱式進行的,提高了程式碼的重用率

泛型的定義

語法格式:類1或者介面<型別實參>物件=new 類2<型別實參>();
例如:ArrayList<String> list = new ArrayList<String>();
上述程式碼表示建立了一個ArrayList集合,但規定該集合中儲存的元素型別必須為String型別

泛型在集合中的應用

學習List介面時提到add()方法的引數是Object型別,無論什麼物件放入List介面,或其子介面,或實現類,都會被轉換為Object型別。在通過get()方法取出集合中的元素是必須進強制型別轉換,不僅繁瑣且容易出現異常。

引入泛型是如何解決上述問題的呢? 使用泛型集合在建立集合物件時指定了集合中元素的型別,從集合中取出元素時,無需進行強制型別轉換,並且如果把非指定型別物件放入集合,會出現編譯錯誤。

List和ArrayList的泛型形式是List<E> ArrayList<E>
Map和HashMap的泛型形式是Map<K,V> HashMap<K,V>

實用類

Java應用程式程式設計介面是執行庫的集合,預定義了一些介面和類,程式設計師可以直接使用這些已經被打包的介面和類來開發具體的應用。

常用的包:
java.lang: 編寫Java程式時最廣泛使用的包,自動匯入到所有的程式中,包含了Java程式的基礎類和介面
java.util: 包含了系統輔助類,特別是Collection、List和Map等集合類
java.io: 包含了與輸入\輸出有關的類
java.sql: 包含了與資料庫相關的類

列舉

列舉是指有一組固定的常量組成的型別,使用關鍵字enum定義

[Modifier] enum enumName {
	enumContantName1, enumConstantName2... // 表示列舉常量列表,列舉常量之間以逗號隔開
	[field, method] //表示其他的成員,包括構造方法,置於列舉常量的後面
}
//在列舉中,如果除了定義列舉常量,還定義了其他成員,則列舉常量列表必須以分號(;)結尾

例項:

public enum Week{
	MON, TUE, WED, THU, FRI, SAT, SUN
}
public void doWhat(Week day){
	switch(day){
		case MON:
		case TUE:
		case WED:
		case THU:
		case FRI:
			System.out.println("工作日");
			break;
		case SAT:
		case SUN:
			System.out.println("週末");
			break;
	}
}

包裝類

Java語言是物件導向的,但是基本資料型別不是物件導向的。包裝類的用途主要有兩個:

  • 包裝類作為和基本資料型別對應的類存在,方便物件操作
  • 包裝類包含每種基本資料型別的相關屬性,如最大最小值,以及祥光的操作方法
基本資料型別包裝類
byteByte
booleanBoolean
shortShort
cahrCharacter
intInteger
longLong
floatFloat
doubleDouble

拆箱和裝箱

裝箱:把基本資料型別變為包裝型別
拆箱:把包裝型別轉為基本資料型別

賦值方式

以Integer為例:幾種賦值方法

  • new Integer(整形)
  • new Integer(字串)
  • Interger.valueOf(字串/整形)
  • Integer.paraseInt(字串)

注意:Character類的valueOf()方法只有一個版本的定義,即valueOf(char c),它返回一個表示指定char值的Character物件

Math類

java.lang.Math類提供了常用的數學運算方法和兩個靜態常量E(自然對數的底數) 和PI(圓周率)
這個類是final類,因此沒有子類,Math類常見方法:

  • static double abs(double a) 返回一個絕對值
  • static double max(double a, double b) 返回其中一個較大的值
  • static double random() 返回一個隨機值

Random類

Random類用於生成隨機數

構造方法說明
Random()建立一個新的隨機數生成器
Random(long seed)使用單個long種子建立一個新的隨機數生成器

用同一個種子值來初始化兩個Random 物件,然後用每個物件呼叫相同的方法,得到的隨機數也是相同的

比較常用的是nextInt()方法,它返回下一個偽隨機整型數

int nextInt();
int nextInt(int n); //從0到n之間(不包括n)

日期操作類

Date類 物件用來表示日期和時間,該類提供了一系列操作日期和時間各組成部分的方法

Calendar類 也是用來操作日期和時間的類,它是抽象類,可以通過靜態方法getInstance()獲得Calender類的物件。它的方法如下:

方法說明
int get(int field)返回給定日曆欄位的值
YEAR指示年
MONTH指示月
DAY_OF_MONTH指示一個月中的某天
DAY_OF_WEEK指示一個星期中的某天

DateFormat類 是一個抽象類,提供了多種格式化和解析時間的方法。使用比較多的是它的子類SimpleDateFormat

Date date = new Date();
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("當前時間為"+formater.format(date));

String類

String類的常用方法

1.求字串長度 str.length()

2.字串比較 字串1.equals(字串2)
需要注意的是:“==”比較的是兩個字串物件在記憶體中的地址,而equals()比較的是兩個字串物件的值
忽略大小的字串比較字串1.equalsIgnoreCase(字串2)
轉大小寫 toLowerCase() / toUpperCase()

3.字串拼接 字串1.concat(字串2)

4.字串提取和查詢
在這裡插入圖片描述
5.字串拆分 字串名.split(separator, limit);
separator和limit均為可選項

StringBuffer和StringBuilder類

1.toString()方法
2.append()方法
3.inset()方法 字串.insert(位置,引數)
4.replace()方法字串名.replace(int start,int end,String str)

三者比較

  • String:不可被改變,真正意義上的安全,在頻繁字串拼接的情況下,速度非常慢
  • StringBuffer:執行緒安全,速度慢
  • StringBuilder:執行緒不安全,速度快

I/O

java.io包提供了一些介面和類,對檔案進行基本的操作,包括對檔案和目錄屬性的操作、對檔案讀寫的操作等

File類訪問檔案屬性

在這裡插入圖片描述

File類的常用方法

在這裡插入圖片描述

流是指一連串流動的字元,是以先進先出的方式傳送和接受資料的通道
在這裡插入圖片描述

流的分類

在這裡插入圖片描述

InputStream

方法說明
int read()從輸入流中讀取下一個位元組資料
int read(byte[] b)從輸入流中讀取資料,並將資料儲存在緩衝區陣列b中,返回實際讀取的位元組數
int read(byte[] b, int off, int len)從輸入流中讀取最多len長度的位元組,儲存到位元組陣列b中,儲存的位置從off開始
void close()關閉輸入流

InputStream類的常用子類有FileInputStream,用於從檔案中讀取資料

FileInputStream讀檔案的流程:
1、FileInputStream物件和String物件宣告
2、建立FileInputStream物件(檔案路徑或File物件)
3、讀單位元組或整個讀到byte陣列中
4、轉成字串
5、關閉FileInputStream流
6、返回結果字串

public static String readFile(String path){
	FileInputStream fis = null;
    String str = null;
    try {
        fis = new FileInputStream(path);
        byte[] b = new byte[fis.available()];
        fis.read(b);
        str = new String(b);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return str;
}

OutputStream

方法說明
void write(int c)將制定的位元組資料寫入此輸出流中
void write(byte[] buf)將陣列buf中的所有位元組寫入此輸出流中
void write(byte[] b, int off, int len)將位元組陣列中從偏移量off開始的長度為len的位元組資料輸出到輸出流中
void close()關閉輸出流

OutputStream類的常用子類為FileOutputStream,用於向檔案寫資料

FileOutputStream寫檔案的流程:
1、File物件裝載檔案路徑
2、判斷檔案父級目錄是否存在,不存在則建立
3、宣告FileOutputStream物件
4、建立FileOutputStream物件(file物件,是否追加)
5、把要寫的字串轉成byte陣列,並寫入輸出流
6、關閉FileOutputStream流

public static void writeFile(String str, String path, boolean isAppend){
	File f =new File(path);
    if (!f.getParentFile().exists()){
        f.getParentFile().mkdirs();
    }
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(f, isAppend);
        byte[] b = str.getBytes();
        fos.write(b);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Reader

方法說明
int read()從輸入流中讀取單個字元,返回所讀取的字元資料
int read(byte[] c)從輸入流中最多讀取c.length個字元
int read(char[] c, int off, int len)從輸入流中讀取最多len個字元
void close()關閉流

Reader類的常用子類為BufferedReader,接受Reader物件作為引數,並對其新增字元緩衝器

使用BufferedReader類和FileReader類讀取文字檔案資料:

public static String readBuffer(String path){
    File f = new File(path);
    FileReader fr = null;
    BufferedReader br = null;
    String str = null;
    try {
        fr = new FileReader(f);
        br = new BufferedReader(fr);
        String s;
        StringBuffer sb = new StringBuffer();
        while ((s=br.readLine())!=null){
            sb.append(s);
        }
        str = sb.toString();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return str;
}

Writer

方法說明
void write(String str)將str字串裡包含的字元輸出到指定的輸出流中
void write(String str, int off, int len)將str字串裡從off位置開始,長度為len的多個字元輸出到輸出流中
void close()關閉輸出流
void flush()重新整理輸出流

Writer類的常用子類為BufferedWriter,用於將資料緩衝到字元輸出流

使用BufferedWriter以及FileWirter物件向文字檔案中寫資料

public static void writeBuffer(String str, String path, boolean isAppend){
	FileWriter fw = null;
	BufferedWriter bw = null;
	try {
		fw = new FileWriter(path, isAppend);
		bw = new BufferedWriter(fw);
		bw.write(str);
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

注意! 在操作上位元組流與字元流有一個區別,字元流在操作時使用了緩衝區(內部儲存器),而位元組流在操作時直接操作檔案,不會使用緩衝區;所有的這些方法在出現錯誤時都會丟擲IOException異常。

讀寫二進位制檔案

讀寫二進位制檔案(例如圖片)檔案常用的類有DataInputStream和DataOutputStream

例項:

public static void copyData(String fromPath, String targetPath){
    FileInputStream fis = null;
    DataInputStream dis = null;
    FileOutputStream fos = null;
    DataOutputStream dos = null;
    try {
        fis = new FileInputStream(fromPath);
        dis = new DataInputStream(fis);
        fos = new FileOutputStream(targetPath);
        dos = new DataOutputStream(fos);
        int tmp;
        while ((tmp=dis.read())!=-1){
            dos.write(tmp);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            dos.close();
            fos.close();
            dis.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

物件流

序列化和反序列化:

  • 場景一:記憶體物件需要在其它環境下使用
    兩個程式間進行網路通訊時,無論是傳送何種型別的資料,均需以二進位制序列形式進行傳送
    傳送方必須將資料物件(比如Java物件)轉化為位元組序列
    接收方則需要將接收到的位元組序列再還原成Java物件
  • 場景二:記憶體物件需要在將來某個時間使用
    將記憶體中的資料物件永久儲存在磁碟中(持久化)

常用序列化方案:
在這裡插入圖片描述

序列化儲存物件資訊

步驟可以概括成如下兩大步:
1.建立一個物件輸出流(ObjectOutputStream),它可以包裝一個其他型別的輸出流,如檔案輸出流FileOutputStream
2.通過物件輸出流的writeObject()方法寫物件,也就是輸出可序列化物件

例項:使用序列化將學生物件儲存到檔案中
1.引入相關類
2.建立學生類,實現Serializable介面
3.建立物件輸出流
4.呼叫writeObject()方法將物件寫入檔案
5.關閉物件輸出流

public class Student implements Serializable{
	//Student屬性和方法
}

public class TestStudent{
	public static void main(String[] args){
		ObjectOutputStream oos = null;
		try{
			//建立ObjectOutputStream輸出流
			oos=new ObjectOutputStream(new FileOutputStream("目標檔案路徑"));
			Student stu = new Student("李梅", 22, "女");
			//物件序列化,寫入輸出流
			oos.writeObject(stu);
		} catch(IOException e) {
			e.printStackTrace();
		} finally {
			if (oos!=null) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

//oos還可以寫入集合中的物件
ArrayList<Student> list = new ArrayList<Student>();
list.add(stu);
list.add(stu1);
oos.writeObject(list);

反序列化獲取物件資訊

反序列化的步驟大致概括為以下兩步:
1.建立一個物件輸入流(ObjectInputStream),它可以包裝一個其他型別的輸入流,如檔案輸入流FileInputStream
2.通過物件輸入流的readObject()方法讀取物件,該方法返回一個Object型別的物件,如果程式知道該Java物件的型別,則可以將該物件強制轉換成其真實的型別。

例項:使用反序列化讀取檔案中的學生物件
1.引入相關類
2.建立物件輸入流
3.呼叫readObject()方法讀取物件
4.關閉物件輸入流

ObjectInputStream ois - null;
trry {
	ois = new ObjectInputStream(new FileInputStream("讀取檔案路徑"));
	Student stu = (Student) ois.readObject();
	//輸出生成後的物件資訊
	System.out.println("姓名為"+stu.getName());
	...
} catach (IOException e) {
	e.printStackTrace();
} finally {
	if (ois!=null) {
		try {
			ois.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

//集合的反序列化
ArrayList<Student> List = (ArrayList<Student>) ois.readObject();
Student stu = (Student) ois.readObject();
for (Student stu:list) {
	System.out.println("姓名為"+stu.getName());
}

反射

Java的反射機制是Java特性之一;Java反射機制是指在執行狀態中,動態獲取資訊以及動態呼叫物件方法的功能

反射的作用:

  • 在執行時獲取類的修飾符,包名,類名,實現的介面,繼承的父類
  • 在執行時獲取類的所有屬性名,修飾符,屬性型別
  • 在執行時獲取所有方法,方法的返回值型別,方法名,方法引數數量,方法引數型別
  • 在執行時呼叫載入類的方法

訪問類包含的構造

public class TestConstructor {
    public static void main(String[] args) throws Exception{
        Class<Student> c = Student.class;
        Constructor<Student> sClass = c.getDeclaredConstructor(int.class, String.class, String.class);
        sClass.setAccessible(true);
        Student s = sClass.newInstance(2, "baba", "male");
        System.out.println(s);
    }
}

訪問類包含的方法

public class TestMethod {
    public static void main(String[] args) throws Exception {
        Student s = TestStudent123.getStudent();
        Class<Student> c = Student.class;
        Method setStuId = c.getDeclaredMethod("setStuId", int.class);
        setStuId.setAccessible(true);
        
        setStuId.invoke(s, 3);
        Method getStuId = c.getDeclaredMethod("getStuId");
        getStuId.setAccessible(true);
        Object stuId = getStuId.invoke(s);
        
        System.out.println(s);
        System.out.println(stuId);
    }
}

訪問類包含的屬性

public class TestField {
    public static void main(String[] args) throws Exception{
        Student student = TestStudent123.getStudent();
        Class<Student> c = Student.class;
        Field stuId = c.getField("stuId");
        stuId.set(student, 11);
        Field stuName = c.getDeclaredField("stuName");
        stuName.setAccessible(true);
        stuName.set(student, "張三");
        
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            System.out.println(field.get(student));
        }
        System.out.println(student);
    }
}

多執行緒

程式

程式是程式的一次動態執行過程,它有如下特點:

  • 程式是系統執行程式的基本單位
  • 每一個程式都有自己獨立的一塊記憶體空間、一組系統資源
  • 每一個程式的內部資料和狀態都是完全獨立的

執行緒

執行緒是程式中執行運算的最小單位,一個程式在其執行過程中可以產生多個執行緒,而執行緒必須在某個程式內執行
執行緒和程式既有聯絡又有區別:

  • 一個程式中至少要有一個執行緒
  • 資源分配給程式,同一程式的所有執行緒共享該程式的所有資源
  • 處理機分配給執行緒,即真正在處理機上執行的是執行緒

多執行緒的優勢

多執行緒程式可以帶來更好的使用者體驗,避免因程式執行過慢而導致出現計算機當機或者白屏的情況
多執行緒程式可以最大限度地提高計算機系統的利用效率

編寫執行緒類

使用一個執行緒的過程可以分為如下4個步驟:
在這裡插入圖片描述

使用Thread類

定義MyThread類繼承Thread類
重寫run()方法,編寫執行緒執行體
建立執行緒物件,呼叫start()方法啟動執行緒

public class MyThread extends Thread{
	private int count=0;
	//重寫run方法
	public void run(){
		while(count<100){
			count++;
			System.out.println(count)
		}
	}
}
publict class Test{
	public static void main(String[] args){
		MyThread mt = new MyThread();
		mt.start();
	}
}

使用Runnable介面

定義MyRunnable類實現Runnable介面
實現run()方法,編寫執行緒執行體
建立執行緒物件,呼叫start()方法啟動執行緒

public class MyThread implements Runnable{
	private int count = 0;
	public void run(){
		while(count<100){
			count++;
		}
	}
}
public class Test{
	public static void main(Stirng[] args){
		Thread thread = new Thread(new MyThread());
		thread.start();
	}
}

比較兩種建立執行緒的方式

  • 繼承Thread類:
    編寫簡單,可直接操作執行緒
    適用於單繼承
  • 實現Runnable介面:
    避免單繼承侷限性
    便於共享資源

執行緒的狀態

在這裡插入圖片描述

執行緒排程

在這裡插入圖片描述
實現執行緒排程的方法:

  • join()方法
for(int i=0;i<10;i++){
	if(i==5){
		MyThread mt = new MyThread('MyThread');
		try{
			mt.start();
			mt.join();
		} catch...
	}
}
  • sleep方法
try{
	Thread.sleep(100) //睡眠100毫秒
} catch...
  • yield方法
for(int i=0;i<5;i++){
	System.out.println(Thread.currentThread().getName()+"正在執行:"+i);
	if(i==3){
		System.out.print("執行緒禮讓:");
		Thread.yield();	
	}
}

執行緒同步

多個執行緒操作同一共享資源時,將引發資料不安全問題

實現執行緒同步

1.同步方法
通過在方法宣告中加入synchronized關鍵字來宣告同步方法

訪問修飾符 synchronized 返回型別 方法名{}
//或者
synchronized 訪問修飾符 返回型別 方法名{}

2.同步程式碼塊
同步程式碼塊的語法格式如下:

synchronized(synObjcet){ //通常填寫this
	//需要同步訪問控制的程式碼
}

執行緒通訊

Java提供瞭如下三個方法實現執行緒之間的通訊:

  • wait():呼叫wait()方法會掛起當前執行緒,並釋放共享資源的鎖
  • notify():呼叫任意物件的notify()方法會在因呼叫該物件的wait()方法而阻塞的執行緒中隨機選擇一個執行緒解除阻塞,但要等到獲得鎖之後才可以真正執行
  • notifyall():呼叫了notifyall()方法會將因呼叫該物件的wait()方法而阻塞的所有執行緒一次性全部解除阻塞

注意: 這三個方法被所有的類繼承並且不允許重寫,只能在同步方法或同步程式碼塊中使用。

XML

XML簡介

  • XML(EXtensible Markup Language),可擴充套件標記語言
  • 特點:
    XML與作業系統、程式語言的開發平臺無關
    實現不同系統之間的資料交換
  • 作用:
    資料互動
    配置應用程式和網站
    Ajax基石

XML文字結構

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <!--圖書資訊 -->
    <book id="bk101">
        <author>王珊</author>
        <title>.NET高階程式設計</title>
        <description>包含C#框架和網路程式設計等</description>
    </book>
    <book id="bk102">
        <author>李明明</author>
        <title>XML基礎程式設計</title>
        <description>包含XML基礎概念和基本作用</description>
    </book>
</books>

XML宣告

<?xml version="1.0" encoding="UTF-8"?>

XML宣告由以下幾個部分組成:
version:文件符合XML1.0規範
encoding:文件字元編碼,預設為“UTF-8”

XML標籤

在XML中用<>括起來的各種標籤來標記資料,如<author></author>

根元素

每個XML文件必須有且僅有一個根元素,如<book></book>
根元素的特點如下:
根元素是一個完全包括文件中其他所有元素的元素
根元素的起始標籤要放在所有其他元素的起始標籤之前
根元素的結束標籤要放在所有其他元素的結束標籤之後

元素與屬性

XML文件內容由一系列標籤元素組成

<元素名 屬性名=“屬性值”>元素內容</元素名>

編寫注意事項

  • 所有XML元素都必須有結束標籤
  • XML標籤對大小寫敏感
  • XML必須正確的巢狀
  • 同級標籤以縮排對齊
  • 元素名稱可以包含字母、數字或其他的字元
  • 元素名稱不能以數字或者標點符號開始
  • 元素名稱中不能含空格

注意!!
屬性值用雙引號包裹
一個元素可以有多個屬性
屬性值中不能直接包含<、“、&
不建議使用的字元:‘、>

名稱空間

名稱空間的必要性: XML解析器在解析XML文件時,對於重名元素,可能會出現解析衝突。名稱空間有助於標準化元素和屬性,併為它們加上唯一的標識

宣告名稱空間:

xmlns:[prefix]="[名稱空間的URL]"

屬性和名稱空間: 除非帶有字首,否則屬性屬於它們的元素所在的名稱空間

例項應用

<?xml version="1.0" encoding="UTF-8"?>
<cameras xmlns:canon="http://www.canon"
    xmlns:nikon="http://www.nikon.com">
    <canon:camera prodID="P663" name="Camera傻瓜相機"/>
    <nikon:camera prodID=“K29B3” name=“Camera超級35毫米相機"/>
</cameras>

解析XML技術

  • DOM
    基於XML文件樹結構的解析
    適用於多次訪問的XML文件
    特點:比較消耗資源
  • SAX
    基於事件的解析
    適用於大資料量的XML文件
    特點:佔用資源少,記憶體消耗小
  • DOM4J
    非常優秀的Java XML API
    效能優異、功能強大
    開放原始碼

這裡著重講述使用DOM讀取XML資料

DOM

DOM介紹:文件物件模型(Document Object Model)
DOM把XML文件對映成一個倒掛的樹
在這裡插入圖片描述

訪問DOM樹節點

在這裡插入圖片描述

public class TestXML {
    Document document;
    public void setDocument(String xmlPath){
        DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder=factory.newDocumentBuilder();
            document=builder.parse(xmlPath);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception{
        TestXML xml = new TestXML();
        xml.setDocument("D:\\KB09\\JAVA\\2020.8.7\\src\\Demo\\phone_info.xml");
        NodeList brands = xml.document.getElementsByTagName("brand");
        Node item = brands.item(0);

        Element e = (Element) item;
        System.out.println(e.getAttribute("name"));
//        for (int i = 0; i < brands.getLength(); i++) {
//            Node n = brands.item(i);
//            if (n instanceof Element){
//                Element e = (Element) n;
//                System.out.println(e.getAttribute("name"));
//            }
//        }
        NodeList hwTypes = e.getChildNodes();
        Node root = xml.document.getElementsByTagName("phoneInfo").item(0);
        Element sx = xml.document.createElement("brand");
        sx.setAttribute("name","三星");
        Element sxType = xml.document.createElement("type");
        sxType.setAttribute("name", "note20");
        sx.appendChild(sxType);
        root.appendChild(sx);


        //儲存更改
        TransformerFactory tf = TransformerFactory.newInstance();
        tf.setAttribute("indent-number", 4);
        Transformer t = tf.newTransformer();
        t.setOutputProperty(OutputKeys.INDENT,"yes");
        t.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
        DOMSource source = new DOMSource(xml.document);
        FileOutputStream fos = new FileOutputStream("D:\\KB09\\JAVA\\2020.8.7\\src\\Demo\\phone_info.xml");
        StreamResult sr = new StreamResult(new OutputStreamWriter(fos));
        t.transform(source, sr);

//        for (int i = 0; i < hwTypes.getLength(); i++) {
//            Node n = hwTypes.item(i);
//            if (n instanceof Element){
//                Element type = (Element) n;
//                Node firstChild = type.getFirstChild();
//                if (firstChild instanceof Text){
//                    Text t=(Text) firstChild;
//                    System.out.println(t.getWholeText().trim());
//                }
//            }
//        }
//
//        Node item2 =brands.item(1);
//        Element e2 = (Element) item2;
//        System.out.println(e2.getAttribute("name"));
//        NodeList appleTypes = e2.getChildNodes();
//        for (int i = 0; i < appleTypes.getLength(); i++) {
//            Node n = appleTypes.item(i);
//            if (n instanceof Element){
//                Element type = (Element) n;
//                Node firstChild = type.getFirstChild();
//                if (firstChild instanceof Text){
//                    Text t=(Text) firstChild;
//                    System.out.println(t.getWholeText().trim());
//                }
//            }
//        }


//        for (int i = 0; i < brands.getLength(); i++) {
//            Node item = brands.item(i);
//            Element e = (Element) item;
//            System.out.println(e.getAttribute("name"));
//            NodeList Types = e.getChildNodes();
//            for (int j = 0; j < Types.getLength(); j++) {
//                Node node = Types.item(j);
//                if (node instanceof Element){
//                    Element type = (Element) node;
//                    Node firstChild = type.getFirstChild();
//                    if (firstChild instanceof Text){
//                        Text t = (Text) firstChild;
//                        System.out.println(t.getWholeText().trim());
//                    }
//                }
//            }
//            System.out.println();
//        }

    }
}

JSON

JSON簡介

  • JSON(JavaScript Object Notation)是JavaScript中的物件表示法
  • 輕量級的文字資料交換格式,獨立於JavaScript語言
  • 具有自我描述性
  • 比XML傳輸速度快

JSON語法規則

  • 資料由名稱/值對構成
  • 資料之間由逗號分隔
  • 大括號內為物件
  • 中括號內為陣列

Java物件轉為JSON字串

使用FastJason(下載地址:FastJason

class Student{
    private String name="";
    private int age;
    private List<String> skills;

    public Student(String name, int age, List<String> skills) {
        this.name = name;
        this.age = age;
        this.skills = skills;
    }
......//省略getter和setter
}

Student stu=new Student("Jason",20, Arrays.asList("Java", "Hadoop", "Python"));
String stuJson=com.alibaba.fastjson.JSON.toJSON(stu).toString();
System.out.println(stuJson);

JSON字串轉為Java物件

String json="{\"skills\":[\"Java\",\"Hadoop\",\"Python\"],
			\"name\":\"Jason\",
			\"age\":20
			}";
Student stuNew = com.alibaba.fastjson.JSON.parseObject(json,Student.class);
System.out.println(stuNew.getName());

正規表示式

正規表示式簡介:

  • 正規表示式描述了一種字串匹配的模式,也稱規則表示式
  • 常用於檢索、替換符合指定模式(規則)的文字
  • 大多數語言都支援正規表示式

正規表示式語法

  • 正規表示式是由普通字元與特殊字元組成的字串

  • 普通字元
    原義字元、非列印字元

  • 特殊字元
    元字元:* + ? $ ^ . | \ ( ) { } [ ]
    在這裡插入圖片描述

  • 非列印字元
    在這裡插入圖片描述

  • 預定義字元
    在這裡插入圖片描述

JAVA正規表示式

java.util.regex包
Pattern類:表示一個正規表示式,或者說匹配模式
Matcher類:Pattern物件matcher()方法的返回值,表示正規表示式對輸入字串的匹配結果

分組

如何獲取字串“hello”中的字元“e”?

Pattern p=Pattern.compile("h(\\w*)llo");
Matcher matcher=p.matcher("hello");
if(matcher.matches())
	System.out.println(matcher.group(1));//輸出匹配結果

命名分組

如何獲取字串“hello”中的字元“e”?

Pattern p=Pattern.compile("h(?<result>\\w*)llo");
Matcher matcher=p.matcher("hello");
if(matcher.matches())
	System.out.println(matcher.group("result"));//輸出匹配結果

相關文章