_04_java核心類庫2
1. 異常機制
1.1 簡介
- Throwable是所有錯誤和異常的超類。
- Error 是嚴重的JVM無法解決的問題,無法通過程式設計解決
- Exception 類是指一些輕微的可以程式設計解決的問題
1.2 分類
-
Throwable 是 Error 和 Exception 的父類。
-
Error:
目前唯一見過的Error,NoClassDefFoundError 繼承關係:Error --> LinkageError --> NoClassDefFoundError
-
Exception:
-
執行時異常,RuntimeException 的子類
包括:ArithmeticException、NullPointerException、ClassCastException等
編譯時檢查不出來,編碼時不會要求必須進行處理,在執行時可能出現,
-
檢測異常,除RuntimeException之外的其他異常。
如 IOException
指編譯時可以檢測出來的異常,編寫程式碼時,如果存在這些異常就會標紅,必須處理了才能編譯。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-PKHQu5RC-1606327865373)(C:/Users/%E9%BB%98%E5%B0%98/Desktop/image-20201120133225305.png)]
-
1.3 異常捕獲,執行流程
-
構造方法 中 throws 丟擲了異常,那麼物件是不會被建立出來的,也就是說,物件在構造完成後才能產生(或者是這時候我們才能拿到????)
-
考點
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-d0DyD71p-1606327865376)(C:/Users/%E9%BB%98%E5%B0%98/Desktop/image-20201120140238103.png)]
1.4 繼承中的異常丟擲
子類throws的異常 不能 比父類throws的更大。
也就是為了保證按照父類丟擲的異常進行處理,可以解決子類丟擲的異常。
1.5 throws 和 try…catch 的使用場景
- 父類沒有寫throws時,子類只能 try…catch 處理
- 當有連續的多層函式呼叫時,推薦使用throws並在 頂層 統一處理。
1.6 自定義異常
-
定義要求
- 繼承Exception類或其子類。
- 定 義一個無參構造和一個有參構造。
public class MyException extends Exception{ public MyException(){} public MyException(String massage){ super(message); } }
-
產生並丟擲異常
... public void func() throws MyException{ // 注意 ... throw new MyException("***異常發生了。"); ... }
1.7 異常機制的意義
將異常處理的程式程式碼集中在一起,與正常的程式碼分開,使得程式簡潔、優雅並易於維護。
目前使用過的異常機制,應該都是被迫使用,它的好處當真是還沒有體會到,不過也不急,畢竟現在自己的程式碼量還是太少啊 ! 沒有經歷過真實的應用場景,就不要談意義。
2. 檔案操作
2.1 File類
-
作用:描述檔案及目錄的資訊
-
常用方法:
File(String path); File(String parent, String child); File(File parent, String child); boolean exists(); String getName(); long length(); // 注意直接就是 length long lastModifed(); String getAbsolutePath(); File getAbsoluteFile(); String getParent(); File getParentFile(); // 若是相對路徑,無法獲取到給定路徑之前的路徑 boolean delete(); // 刪除資料夾必須為空,否則返回false, 檔案不存在也是返回false,不會報異常 boolean createNewFile(); // 檔案路徑 不存在 就會 報IOException異常。 !!! boolean mkdir(); // 父路徑不存在時返回 false boolean mkdirs(); File[] listFiles([FileFilter filter]); public String list([FileFilter filter]); // 注意只是子檔名,不是完整路徑 boolean isFile(); boolean ifDirectory();
例項:
file.listFiles(new FileFilter(){ @Override public boolean accept(File file){ return file.isFile(); // 返回 true 則保留。 } });
-
注意:
檔案與資料夾也不能重名,這是系統的限制。建立重名物件時返回false。
-
案例:
- 如何通過File獲取當前路徑?
String path = new File("").getAbsolutePath(); // 獲取的應該是啟動啟動程式時的路徑 A.class.getResource("/").getPath(); // 類路徑的 根目錄 A.class.getResource("").getPath(); // A類的class檔案所在路徑
2.2 IO流簡介
-
基本分類
-
位元組流、字元流
-
輸入流、輸出流
-
節點流、處理流
節點流:直接與輸入輸出 源 對接的流。
處理流:建立在節點流基礎之上的流。
-
-
結構框架
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-MRlYw7Wf-1606327865377)(C:/Users/%E9%BB%98%E5%B0%98/Desktop/image-20201120165628842.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-i6AEFALK-1606327865380)(C:/Users/%E9%BB%98%E5%B0%98/Desktop/image-20201120170048991.png)]
2.3 位元組流
位元組輸入流:InputStream 【抽象】
位元組輸出流:OutputStream 【抽象】
這兩個是所有位元組流的超類。直接子類:FileOutputStream 、ByteArrayOutputStream 。。。
2.3.1 FileOutputStream
-
構造方法
public FileOutputStream(String path); public FileOutputStream(File path); // 以上兩個方法都可以再跟一個引數 Boolean append,預設false,表示新建檔案以寫入資料,設定為true時,表示向已有檔案追加 // 如果檔案不存在都會先建立,如果父目錄不存在,報FileNotFoundException
-
write方法
void write(int b); // 注意 int只能寫入底八位其他的會被捨棄 void write(byte[] b); void write(byte[] b,int offset,int len);
注意:預設換行符:window: \r\n linux: \n mac: \r
-
其他
void close(); void flush();
2.3.2 FileInputStream
-
構造方法
public FileInputStream(File path); public FileInputStream(String path);
-
read方法
int read(); // 注意它的返回值也是int型別,雖然只有底八位 int read(byte[] buff); // 如果讀完了 返回值 都是 -1 int available(); // 獲取檔案的大小
-
其他方法
long skip(long n); // 跳過 n 個位元組,據測試返回的都是n,不知道返回值有什麼用。
2.3.3BufferedInputStream & BufferedOutputStream
-
位元組緩衝流是對其他IO流的封裝(包括但不限於檔案IO流),使其具備緩衝區的功能。
-
優點
- 在他們的內部自動維護一個緩衝陣列,預設大小8192B,以此來減少對底層的讀寫次數,可以提高效率。
- 增加了Mark的功能【暫時還未用到】。
-
構造方法:
public BufferedInputStream(InputStream in,[int size]); public BufferedOutputStream(OutputStream out,[int size]); // 是對其他IO流的封裝,size 為緩衝區的大小,預設8192,也可自行指定
-
讀寫
// 讀寫同 FileInputStream FileOutputStream
-
如下是複製一個視訊的用時
// 基本位元組流 1 // 245390 // 基本位元組流 1024 // 437 [8192 140] [8192+1024 124] // 緩衝位元組流 1 // 1828 // 緩衝位元組流 1024 // 125
可見使用緩衝流確實可以提高效率【底層呼叫減少了】
但是先要提高效率,好像直接加大緩衝陣列就好了呀,為啥要有緩衝流呢?他到底有什麼優勢。-------或許是我沒有資料操作的原因。
2.4 字元流
2.4.1 字元編碼
-
常用字符集
ASCII、Unicode、GB系列
注意:Unicode是一個字元編碼解決方案,它給每一個字元編了一個號,也就是一個十進位制的數字。那怎麼將這個號存入計算機呢?為此,就有了utf-8,utf-16,utf-32三種編碼,它們通過自己的規則將字元的Unicode號轉換為二進位制存入記憶體。
GB系列包括:GBK和GB2312,GBK完全相容GB2312。
Unicode這個號,到底存不存在,Java中字串是不是用的這個號。如果是字元不可能用兩個位元組就能完全存下,那麼java中可不可能出現不能表示的字元。??????
java 中的編碼問題:java檔案編碼、JDK預設編碼(編譯時可指定encoding)、JVM預設編碼(getBytes是使用的預設編碼,目前不會自行指定)
這是一個複雜的問題,要慢慢解決,先要逐漸明確各種編碼區別聯絡,還要了解java的編解碼都在什麼時候發生。
-
java字串編解碼方法
"".getBytes(String charset); // 編碼 new String(byte[] data,String charset); // 解碼 // charset可以省略,使用平臺預設字符集, // 平臺的預設字符集,也就是JVM預設編碼,不同平臺不一樣,也是可以認為指定的。
2.4.2 Reader、Writer
-
為什麼需要字元流:如上所述字元的編碼是很複雜的,如果讓我們在InputStream中拿到資料後,自己轉化為字串,這時自己面對各種各樣的編碼,著實不太美妙。
-
字元流不是什麼新東西,他就像BufferedInputStream一樣,是對位元組流的封裝,只不過是提供了一些特殊功能而已。
也就是說,java的IO流就兩種InputStream、OutputStream,其他的要麼是它們的子類,也就是提供這倆東西的實現;要麼就是對這兩個東西的封裝,即實現特殊功能,如緩衝,字元編解碼。
-
抽象基類:Reader、Write,這倆東西也就是一個規範,即說明字元流應該具有哪些功能。
2.4.3 InputStreamReader & OutputStreamWriter
-
構造方法
public InputStreamReader(InputStream in, String charset); public OutputStreamWriter(OutputStream out, String charset); // 注意:charset指定編解碼字符集,可有可無預設應該是JVM的預設編碼,應該是平臺相關,但是沒試過。
-
讀寫方法
// 寫 void write(int c); // 給定字元的Unicode編碼,也就是\u後邊的東西,可以有字元強轉為int得到,用的應該不多。 void write(char[] cbuff); void write(char[] cbuff,int offset,int len); void write(String s); void write(String s,int offset,int len); void flush(); // 它是由緩衝的。 // 讀 int read(); // 讀一個字元,雖然返回值是int但強轉為char就行。 也就是說,輸入字元流,已經把字元轉化為Java字串預設的Unicode編碼。 int read(char[] cbuff); int read(char[] cbuff,int offset,int len); // offset 表示char[]中的便宜,len表示最多讀幾個字元 public String(char[] date,int offset,int len); // 將字元陣列轉換為字串
2.4.4 FileReader & FileWriter
-
構造方法
public FileReader(String file); public FileReader(File file); public FileWriter(String file, boolean append); public FileWruter(File file, boolean append); // append 可省,預設false。 // FileReader和FileWriter使用平臺預設字符集,無法指定編碼型別。 // FileReader和FileWriter都是對 對應 InputStreamReader和OutputStreamWriter的分裝,且只不過是改了一下構造方法而已,其他的兩者完全一樣。
-
作用:
只是為了簡化建立程式碼,且命名格式上與FileInputStream和FileOutputStream對應而已。
2.4.5 BufferedReader & BufferedWriter
-
與BufferedInputStream 對應,新增了緩衝功能。
-
作用:當然可以提高讀寫效率,更重要的是他提供了一些針對字元IO的方便方法,如讀一行。
-
構造方法
public BufferedReader(Reader in,int size); public BufferedWriter(Writer our,int size); // size 可省,預設8192 // 只有這4種構造方法。
-
讀寫方法
// 寫方法 // 同OutputStreamWriter,5種方法,int,char[] 2種,String 2種。 // 特有寫方法 public void newLine(); // 輸出 系統項關 的換行符 public void flush(); // 讀方法 // 繼承自Reader,可以使用read()和read(char[]) // 特有讀方法 public String readLine(); // 讀一行,不包括換行符,讀完後返回null void newLine();
2.4.6 PrintStream
PrintStream(OutputStream out);
void print(String s);
void println(String s);
void flush();
void close();
作用:System.out就是 PrintStream 的例項。將各種資料進行格式化輸出,使用上就跟向控制檯輸出一樣。
2.4.7 PrintWriter
PrintWriter(Writer out);
// 用法上與PrintStream相似。
2.4.8 DataInputStream & DataOutputStream
// 構造方法
DataInputStream(InputStream is);
DataOutputStream(OutputStream os);
void writeInt(int v);
int readInt();
void close();
作用:
提供了將各種基本資料型別的資料,直接寫到流中的方法,然後可以用對應的read方法讀出。
優點就在於讀寫時 不用進行資料型別和資料格式的轉化。
重要的檔案不會使用這種隨意的格式進行讀寫,快捷的儲存臨時資料也有Object-io可以選擇。所以它的用處就不是很大。
注意:
writeInt(10) 與 write(10) 的區別,前者寫四個位元組,後者寫一個位元組。
2.4.9 ObjectInputStream & ObjectOutputStream
-
注意:
讀寫物件的型別必須實現 java.io.Serializable 介面來開啟類的序列化功能。所謂序列化就是將一個物件 需要儲存的相關資訊 有效組織成 位元組序列 的過程。逆向過程就稱之為反序列化。
-
常用方法:
ObjectOutputStream(OutputStream out); ObjectInputStream(InputStream in); void writeObject(Object obj); Object readObject(); void close();
-
序列化版本號
序列化版本號的作用是什麼?
標識這個類是否發生改變,序列化時 版本號會一起寫入檔案,反序列化時,會判斷檔案中的版本號與類中的版本號是否一致,如果不一致就認為類的結構發生了變化,這時候就會報 InvalidClassException 。所以serialVersionUID應該與類結構對應,當對類進行了修改後(如加了一個欄位),就應該換一個新的版本號。
版本號一致就可以轉換,類中如果有新增的欄位會以預設方式初始化,檔案比類中多字元段也不會報錯。一般寫版本號的目的就是保證類修改後依然可以反序列化。
如何生成序列化版本號?
因為版本號只是當前類結構的一種標識,所以隨便寫一個就好,只要保證類修改後換一個新的版本號行。但是每次修改類都要自己編一個版本號也比較麻煩,所以方式二就是通過IDE生成,IDE會自動根據當前類的內容生成一個版本號,每次修改類後只要重新生成一次就好,不用擔心會不會和以前的版本號重複。
為什麼不寫也可以?
可以發現實現serializable介面後,不顯示書寫serialVersionUID也能行啊?如果不寫,編譯器在編譯時自動根據類的內容計算並新增一個版本號。這時只要修改類的結構,增刪欄位或方法就會導致版本號不同(方法的內容修改不會導致版本號不同)。但是多數情況下我們並不希望它如此敏感。所以一般都會寫serialVersionUID,來自行控制版本問題。
-
transient關鍵字
用於標識不需要進行序列化的欄位。
private transient String name;
這時name欄位序列化時就不會寫入檔案,反序列化時會以預設方式初始化,也就是 null 。
-
經驗
同一個檔案中可以寫入多個物件,但讀取時無法判斷是否讀到檔案末尾,如果讀到了末尾還進行讀取,會報EOFException。所以一個檔案中儲存多個物件時,可以將多個物件放入一個ArrayList物件中,一起儲存。
2.4.10 RandomAccessFile
-
簡介
實現檔案的隨機讀寫。
-
常用方法
RandomAccessFile(String path, String mode); // mode 取值:r 只讀,rw:讀寫,rwd:讀寫,同步檔案內容更新,rws:讀寫,同步檔案內容和原資料的更新 int read(); void seek(long pos); // 從開頭向後偏移pos個位元組 void write(int b); // 輸出一個字元,並覆蓋原有字元 close();
此類由於在檔案的指定位置進行操作,目前還用不明白,等需要了再說吧。
2.4.11 問題
-
為什麼使用字元流複製圖片會失敗?
-
問題描述:字元流就是在位元組流的基礎上新增了字元的編解碼功能,那麼 解碼、編碼 兩相對應不應該內容不變嗎?
-
實驗:
public static void main(String[] args) throws IOException { // 自己輸出一個二進位制檔案, FileOutputStream out = new FileOutputStream("C:\\Users\\默塵\\Desktop\\test01.png"); byte[] data = {-128,-128,-128,-128,-128,-128,-128,-128,-128,-128,-128}; out.write(data); out.close(); // 使用字元流拷貝這個 位元組二進位制檔案 FileReader fr = new FileReader("C:\\Users\\默塵\\Desktop\\test01.png"); FileWriter fw = new FileWriter("C:\\Users\\默塵\\Desktop\\test02.bmp"); int buff = 0; while(-1!=(buff=fr.read())){ fw.write(buff); } fw.close(); fr.close(); // 使用InputStream讀取拷貝的檔案,看看是否還是原資料。 FileInputStream in = new FileInputStream("C:\\Users\\默塵\\Desktop\\test02.bmp"); byte[] b2 = new byte[data.length]; in.read(b2); in.close(); // 列印 System.out.println("原資料:"+Arrays.toString(data)); System.out.println("拷貝後:"+Arrays.toString(b2)); }
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-0b7NnwZy-1606327865383)(C:/Users/%E9%BB%98%E5%B0%98/Desktop/image-20201120230222831.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-wgMDV83y-1606327865383)(C:/Users/%E9%BB%98%E5%B0%98/Desktop/image-20201120230309329.png)]
-
分析
複製二進位制檔案有成功的,也有失敗的。故猜測,是因為不是所有的二進位制資料都有與之對應的字元,像上例中 -128 二進位制位全1組合,就沒有與之對應的字元,解碼時肯定有它的處理機制,但這樣編碼回去後就肯定不是原來的值了。這也就是問題的根源。
-
結論
因為我們無法確定圖片等二進位制檔案中是否存在這樣 沒有字元對應的二進位制組合,所以也就無法保證複製成功,所以不能用字元流複製二進位制檔案。
-
2.5 總結
-
檔案項關的IO流只有兩個FileInputStream & FileOutputStream
-
對IO流的讀寫【包括但不限於檔案IO流】,又分為兩類,即位元組和字元讀寫。
對於位元組讀寫:多使用Buffered… 只是宣告時多一個殼兒,用法一樣,效率還高。
對於字元讀寫:也多使用Buffered… 加 FileReader宣告,效率高。還好用寫,readLine和newLine更便於書寫。
所以,就是將Buffered用成習慣。
-
對於效能問題:
一次測試:
我用位元組流複製五個視訊檔案到一個檔案,總大小100M
使用5M的陣列空間,不論使用者用buffered用時都是200ms的樣子。
但使用1024B的陣列空間,不用Buffered耗時約700ms,但用了Buffered時間還是200ms。
而且後來我給出了10M的陣列空間,速度依然只有200ms,可能達到了物理上限。
按我想的Buffered的空間只有8K,按道理我的陣列空間只要比8K大,那不用Buffered就會的到更優的效果,但並非如此,至於原因,等你把這些用成條件反射的時候再看原始碼分析吧。原因可能是多方面的,可能不止java的實現方法。
3. 多執行緒
3.1 程式和執行緒的概念
通俗來講就是程式就是程式的一次執行過程,是系統進行資源分配和排程的一個單位。執行緒就是輕量級的程式,它的建立、排程、銷燬 所花費的開銷要比程式更小,是系統資源排程的更小的單位。一個程式可以包含多個執行緒。
從程式設計角度講,執行緒就是一個可以獨立進行的任務流程。我們可以通過執行緒實現多個操作的併發執行。
3.2 執行緒的兩種建立方式
- 繼承 java.lang.Thread 類,並重寫run方法。
- 以Runnable的例項為實參構建Thread物件。
3.3 Thread類
// 構造方法
Thread([Runnable trget][, String threadName]);
void start(); // 啟動執行緒,,是由JVM去掉run方法,而非在start中呼叫run方法
long getId(); // 獲取執行緒的唯一標識,
String getName(); // 獲取執行緒名稱
void setName(String name); // 設定執行緒的名稱
static Thread currentThread(); // 獲取當前正在執行的執行緒的引用,也就是獲取程式碼所在的執行緒。
static void yield(); // 讓步,執行狀態的執行緒退出到就緒狀態排隊。
static void sleep(long ms);
int getPriority();
void setPriority(int p);
// 優先順序 [1,10],預設為5;優先順序越高也不一定先執行,只是獲取時間片的機會更大而已。
// 常量:Thread.MAX_PRIORITY, Thread.MIN_PRIORITY
void join([long ms]); // 當前執行緒等待,呼叫者執行緒結束後,再繼續執行,若有ms,表示最多等待的時間。
boolean isDaemon(); // 守護執行緒在主執行緒結束後停止(但也不是立即停止),非守護執行緒繼續執行,預設為非守護執行緒。
void setDaemon(true); // 將執行緒設定為守護執行緒,也就是設定它在main結束後停止。
// 注意:setDaemon必須在start之前。
3.4 執行緒的生命週期
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-rgLKwcw6-1606327865384)(img/image-20201122114102128.png)]
- 新建狀態:使用new關鍵字建立之後的狀態,並未開始執行。
- 就緒狀態:呼叫start()之後進入的狀態,依然沒有開始執行。
- 執行狀態:執行緒排程器呼叫該程後進入的狀態,執行緒開始執行,當時間片耗盡且人物未執行完時,就返回就緒狀態。
- 消亡狀態:執行緒任務完成後進入的狀態,此時執行緒已終止。
- 阻塞狀態:執行過程中發生阻塞事件進入的狀態。阻塞解除後進入的是就緒狀態,而非執行狀態。
3.5 執行緒同步機制
3.5.1 相關概念
-
同步機制:
保證多個執行緒有條不紊的同時(巨集觀)進行的機制,它的作用在於保證多個執行緒同時進行不出錯,所以它的主要任務是對多個執行緒在執行次序上進行協調,使併發執行的多個執行緒之間能按照一定的規則共享臨界資源。
-
**臨界資源:**一次僅允許一個執行緒使用的資源。
-
**臨界區:**訪問臨界資源的程式碼區域。
3.5.2 實現方式:
對臨界區加鎖,不允許兩個執行緒同時進入臨界區。synchronized加鎖或lock加鎖。
3.5.3 synchronized
-
部分程式碼鎖定
synchronized(var){ // 注意這裡的var可以是任意引用型別物件的引用,一個物件就是一把鎖。 // 臨界區程式碼 }
-
方法鎖定
public synchronized void func(){ } public static synchronized void func(){ }
相當於:
synchronized(this){ 整個方法體 } synchronized(A.class){ 整個方法體 } // A 表示方法所在的類
也就是說能不能鎖住要看,呼叫方法時兩個this是不是同一個物件。
-
注意事項
- synchronized括號中的量必須是同一個物件才能鎖的住。
- 儘量減少同步程式碼塊的範圍以提高效率。
3.5.4 Lock介面
常用方法:
ReentrantLock(); // Lock的實現類
void lock(); // 獲取鎖
void unlock(); // 釋放鎖
使用方法:
ReentrantLock lock = new ReentrantLock();
lock.lock();
// 同步程式碼塊
lock.unlock();
3.6 執行緒協作
3.6.1 相關方法
Object中的三個方法用於執行緒間的協作:
void wait([long timeout]); // 釋放物件的鎖,進入等待,timeout表示最多等待的毫秒數,沒有給出就一直等
void notify();
void notifyAll();
注意:
- wait()、notify()和notifyAll()方法是本地方法,並且為final方法,無法被重寫。
- 呼叫某個物件的wait()方法能讓當前執行緒阻塞,並且當前執行緒必須擁有此物件的monitor(即鎖)
- 呼叫某個物件的notify()方法能夠喚醒一個正在等待這個物件的monitor的執行緒,如果有多個執行緒都在等待這個物件的monitor,則只能喚醒其中一個執行緒;
- 呼叫notifyAll()方法能夠喚醒所有正在等待這個物件的monitor的執行緒;
3.6.2 生產者消費者問題
-
問題描述
有兩個程式:一組生產者程式和一組消費者程式共享一個初始為空、固定大小為n的快取(緩衝區)。生產者的工作是製造一段資料,只有緩衝區沒滿時,生產者才能把訊息放入到緩衝區,否則必須等待,如此反覆; 同時,只有緩衝區不空時,消費者才能從中取出訊息,一次消費一段資料(即將其從快取中移出),否則必須等待。由於緩衝區是臨界資源,它只允許一個生產者放入訊息,或者一個消費者從中取出訊息。
3.8 Callable介面
- 定義如下
public interface Callable<V> {
V call() throws Exception;
}
-
特點
作用同Runnable差不多,特點是它支援泛型,可以有返回值。
預設丟擲異常
3.9 Future 介面
- Future介面
public interface Future<V> {
boolean cancel(boolean var1);
boolean isCancelled();
boolean isDone();
V get();
V get(long var1, TimeUnit var3);
}
說明:
- cancel方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false。引數mayInterruptIfRunning表示是否允許取消正在執行卻沒有執行完畢的任務,如果設定true,則表示可以取消正在執行過程中的任務。如果任務已經完成,則無論mayInterruptIfRunning為true還是false,此方法肯定返回false,即如果取消已經完成的任務會返回false;如果任務正在執行,若mayInterruptIfRunning設定為true,則返回true,若mayInterruptIfRunning設定為false,則返回false;如果任務還沒有執行,則無論mayInterruptIfRunning為true還是false,肯定返回true。
- isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true。
- isDone方法表示任務是否已經完成,若任務完成,則返回true;
- get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
- get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。
作用:定義了獲取返回值,取消任務的等功能。
-
FutureTask類
FutureTask類繼承自RunnableFuture介面,RunnableFuture又繼承自Runnable介面和Future介面。
故FutureTask介面可作為Runnable的實現類有執行緒執行,又可以使用Future宣告的功能獲取執行結果。
使用方法:
public static void main(String[] args) { FutureTask<String> ft = new FutureTask<>(new Callable<String>() { @Override public String call() throws Exception { System.out.println("我是子執行緒的執行過程。等3秒後才能看到我的返回值。"); Thread.sleep(3000); return "我是子執行緒的返回值"; } }); new Thread(ft).start(); try { System.out.println("----"+ft.get()); // 主執行緒會被阻塞在這裡知道子執行緒執行完 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("----主執行緒的輸出"); }
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-mV356lCY-1606327865385)(img/image-20201123152302250.png)]
大致原理:
Thread呼叫FutureTask的run方法,run方法中又呼叫了Callable中的call方法,並將其返回值儲存outcome欄位中,當呼叫get方法時,執行緒執行完了就將outcome返回。run方法中還將當前執行緒的引用存放了成員變數runner中,這樣就可以cancel方法中中斷執行緒了。
3.10 執行緒池
-
相關概念
執行緒池:首先建立一些執行緒,把它們的集合稱為執行緒池,當需要時從執行緒池中取出一個空閒的執行緒為之服務,用完後不關閉該執行緒,而是將該執行緒換回到執行緒池中。
線上程池程式設計模式下,任務是提交給執行緒池的,而不是直接交給某個Thread。任務提交後再由執行緒池分配執行緒處理。一個執行緒只能執行一個任務,但可以同時向一個執行緒池提交多個任務。
執行緒池從Java1.5開始支援。
-
相關類和方法
ExecutorService介面 定義了執行緒池的功能,定義了和後臺任務執行相關的方法
ThreadPoolExecutor類 是ExecutorService的主要實現類。然而他的例項建立起來很是麻煩,有多個引數需要制定。
Executor 是執行緒池的工廠類,一般用它來幫我們建立執行緒池,它建立的例項也一般為ThreadPoolExecutor類的例項。
-
Executors的常用方法
static ExecutorService newCachedThreadPool(); // 可以根據需要新建立執行緒 static ExecutorService newFixedThreadPool(int n); // 固定執行緒數的 static ExecutorService newSingleThreadExecutor(); // 只有一個執行緒的執行緒池
-
ExecutorService介面的常用方法
void execute(Runnable runner); <T> Future<T> submit(Callable<T> | Runnable task); // 它返回的就是 FutureTask 物件;它是一個泛型方法 // 傳入Runnable的例項時,返回值為Future<?> ,故get只能得到Object。 void shutdown(); void shutdownNow();
-
舉例
public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(5); Future<String> task = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "123456"; } }); System.out.println(task.get()); executorService.shutdown(); // 否則main不會退出 }
多個執行緒使用相同的所鎖定不同的程式碼塊,還可以執行嗎?
只要是用同一個物件為鎖,不論是否在同一個run函式中,不論加鎖的內容是否相同,都能鎖住。
4. 網路程式設計
4.1 相關協議
-
TCP協議
TCP協議的三次握手
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-UglxS9aK-1606327865386)(img/image-20201126010515332.png)]
四次揮手
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-ZbcdTFEI-1606327865387)(img/image-20201126010606878.png)]
-
UDP協議
1、UDP是無連線的,即傳送資料之前不需要建立連線;
2、UDP使用盡最大努力交付,即不保證可靠交付;
3、UDP是面向報文的;
4、UDP支援一對一、一對多、多對一和多對多的互動通訊等。
4.2 TCP程式設計模板
public static void main(String[] args) throws IOException {
//建立服務端物件,繫結4574埠。
ServerSocket serv =new ServerSocket(4574);
System.out.println("伺服器啟動成功,等待使用者接入");
//accept()等待使用者接入,如果沒有使用者連線,會一直等待。
//有客戶連線後,accept()方法會返回一個Socket物件,代表客戶端
Socket sc=serv.accept();
System.out.println("有客戶端接入,客戶端ip:"+sc.getInetAddress());
//從Socket中得到網路輸入流,接收來自網路的資料
InputStream in=sc.getInputStream();
//從Socket中得到網路輸出流,將資料傳送到網路上
OutputStream out=sc.getOutputStream();
//接收客戶端發來的資料
byte[] bs=new byte[1024];
//將資料存入bs陣列中,返回值為陣列的長度
int len=in.read(bs);
String str=new String(bs,0,len);
System.out.println("來自客戶端的訊息: "+str);
//向客戶端寫資料,注意客戶端程式碼中別忘記寫read()方法,否則會拋異常
out.write("歡迎訪問,你好,我是服務端".getBytes());
System.out.println("服務端正常結束");
//關閉流!
sc.close();
}
public static void main(String[] args) throws UnknownHostException, IOException {
//建立Socket連線,new Socket(服務端ip,埠port);
Socket so=new Socket("192.168.0.104",4574);
System.out.println("連線伺服器成功");
//從Socket中得到網路輸入流,接收來自網路的資料
InputStream in=so.getInputStream();
//從Socket中得到網路輸出流,將資料傳送到網路上
OutputStream out=so.getOutputStream();
//write方法中只能為byte[],或者int。
//若服務端沒有read(),則客戶端會一直等。即停留在write方法中。
out.write("你好,我是客戶端".getBytes());
//接收服務端發來的資料
byte[] bs=new byte[1024];
//將資料存入bs陣列中,返回值為陣列的長度
int len=in.read(bs);
String str=new String(bs,0,len);
System.out.println("來自服務端的訊息: "+str);
//關閉流
so.close();
}
注意事項:
檔案的IO流結束一定要關閉
Tcp的IO流不能關閉,但結束一定要flush
4.3 UDP程式設計模板
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-7J3f2QQa-1606327865387)(img/image-20201126011006621.png)]
5. 反射
反射是指:在執行階段決定建立什麼物件,呼叫什麼方法的程式設計機制
5.1 Class類
-
基本概念
- java.lang.Class類的例項可以描述Java的類和介面,也就是一種資料型別。
- 它沒有公共構造方法,其例項由 JVM 和 ClassLoader 自動建立。
-
獲取方式
// 1. 資料型別 * String.class(); // class java.lang.String int.class(); // int void.class(); // void // 2. 物件.getClass() obj.getClass(); // 3. 包裝類.type() // 獲取其基本型別的 Class 物件 Integer.type; // int Integer.class; // java.lang.Integer // 4. forName方法 * Class.forName("完全限定名"); // 5. 類載入器 ClassLoader cl = A.class.getClassLoader(); cl.loadClass("完全限定名")
-
常用方法
newInstance(); // 呼叫無參構造,建立物件,已過時 Constructor<T> getConstructor(Class<?>... parameterTypes); // 獲取Class物件代表的型別中對應引數的公共構造方法。 // 注意這裡引數 必須是Class的物件 Constructor<T>[] getConstructors(); // 獲取 所代表的 型別的所有公 共構造器 Field getDeclaredField(String name); // 獲取 欄位物件,Declared表示獲取所有的,不加只能獲取public的。 Field[] getDeclareFields(); // 獲取所有欄位 Method getMethod(String name,Class<?>... parameterTypes); // 用於獲取該Class物件表示類中名字為name引數為parameterTypes的指定公共成員方法 Method[] getMethods(); // 用於獲取該Class物件表示類中所有公共成員方法
5.2 Constructor類
-
獲取例項
通過Class物件的getConstructor方法獲取
-
常用方法
T newInstance(Object... initargs); // 建立例項 ** int getModifiers(); // 獲取方法的訪問修飾符,【因為不只可以獲取到共有構造方法】 String getName(); // 獲取方法名,不知有何用? Class<?>[] getParameterTypes(); // 獲取方法的所有引數型別
-
示例
public static void main(String[] args) throws Exception{ Class pclass = Class.forName("mc.PC.Person"); Constructor constructor = pclass.getConstructor(String.class,int.class); Object o = constructor.newInstance("Jsumy",15); System.out.println(o); }
5.3 Feild類
常用方法:
Object get(Object obj);
void set(Object obj,);
void setAccessible(boolean flag); //當實參傳遞true時,則反射物件在使用時應該取消 Java 語言訪問檢查
int getModifiers(); // 獲取成員變數的訪問修飾符
Class<?> getType(); // 獲取成員變數的資料型別
String getName(); // 獲取成員變數的名稱
5.4 Method類
Object invoke(Object obj,Object... args); // 使用物件obj來呼叫此Method物件所表示的成員方法,實參傳遞args
int getModifiers(); // 獲取方法的訪問修飾符
Class<?> getReturnType(); // 獲取方法的返回值型別
String getName(); // 獲取方法的名稱
Class<?>[] getParameterTypes(); // 獲取方法所有引數的型別
Class<?>[] getExceptionTypes(); // 獲取方法的異常資訊
5.5 其他資訊
Package getPackage(); // 獲取所在的包資訊
Class<? super T> getSuperclass(); // 獲取繼承的父類資訊
Class<?>[] getInterfaces(); // 獲取實現的所有介面
Annotation[] getAnnotations(); // 獲取註解資訊
Type[] getGenericInterfaces(); // 獲取泛型資訊
相關文章
- 03-Java核心類庫_多執行緒Java執行緒
- 03-Java核心類庫_列舉、註解與反射Java反射
- Java核心技術第五章——2.Object類JavaObject
- Android核心庫Android
- smash:一個類unix核心
- EOL 筆記:核心類解析筆記
- Spring MVC 核心類和介面SpringMVC
- javaSE從零開始04_java的基礎語法Java
- 30個類手寫Spring核心原理之Ioc頂層架構設計(2)Spring架構
- Uniswap V2 核心
- webpack(2)webpack核心概念Web
- 備忘錄八:Shiro核心類
- vue 核心加解密工具類 方法Vue解密
- 分類2
- Angular2如何使用第三方類庫(如:jQuery)AngularjQuery
- J2EE 核心模式模式
- Groovy核心類原始碼講解(上)原始碼
- Android工具類庫Android
- Java 工具類庫Java
- Pyhton-類(2)
- 基礎類庫積累--ExeclHelper類
- Vue原始碼探究-核心類的實現Vue原始碼
- DIY 實現 ThinkPHP 核心框架 (十)App 類PHP框架APP
- DIY 實現 ThinkPHP 核心框架 (九)Container 類PHP框架AI
- DIY 實現 ThinkPHP 核心框架 (十二)Facade 類PHP框架
- JAVA類庫之——Character類(持續更新)Java
- Java:常用類庫——未完Java
- ThinkPHP6 核心分析之Http 類跟Request類的例項化PHPHTTP
- Java 集合類——Collections(2)Java
- String類的使用2
- 深入koa原始碼(二):核心庫原理原始碼
- 鴻蒙HarmonyOS實戰-ArkTS語言基礎類庫(容器類庫)鴻蒙
- Java Thread 類相關的幾個核心方法Javathread
- DIY 實現 ThinkPHP 核心框架 (十一)完善App 類PHP框架APP
- 2張圖理解resnet核心思想
- koa2核心原始碼淺析原始碼
- linuxI2C驅動核心梳理Linux
- 【linux】驅動-2-核心模組Linux