Java基礎(十一)★流★線程

IO

流動的是數據。---用於傳輸數據的一套API---IO-> Input Output---輸入輸出流---當數據是從外部流向程序的時候,輸入流;數據是從程序流向外部的時候,輸出流。讀取文件---將數據從文件讀到程序中---輸入流;向文件中寫入數據---數據從程序流向了文件---輸出流。

根據數據的傳輸方向:輸入流、輸出流

根據數據的傳輸形式:字節流、字符流

 

輸入流

輸出流

字節流

InputStream

OutputStream

字符流

Reader

Writer

這四個基本流都是抽象類。

數據的來源(目的地):硬盤,網絡,輸入設備,內存

TXT文件寫入一個字符串---輸出流,字符流,和文件有關的流---FileWriter

FileWriter(String path)---創建一個新的文件。如果文件不存在,會創建一個新文件。

流中的異常處理

1. 將流對象放在try外定義並且賦值爲null,放在try中初始化

2. 在關流之前需要判斷流對象是否初始化成功

3. 需要將流強制置爲null以防關流失敗無法釋放文件

4. 需要在寫完數據之後手動添加flush操作以防關流失敗產生數據丟失

讀取一個TXT文件---輸入流 字符流 和文件有關的流---FileReader

練習:複製文件

練習:剪切文件

注意:Java中的原生的流對office組件支持的不好---POI


緩衝流

BufferedReader---需要傳入字符輸入流,真正讀取數據的是傳入的這個字符輸入流,緩衝流僅僅提供了一個緩衝區。

---從字符輸入流中讀取數據,提供一個緩衝區,允許進行按行讀取。

BufferedWriter---newLine()---寫入一個換行---產生新的一行

\r\n \n

writer.write(“abc”);

writer.newLine(); -> \r\n \n

練習:統計Java代碼的行數

StringReader

讀取字符串的流。

字節流

以字節形式讀取數據---沒有緩衝區

練習:用字節流來複制一個文件---統計時間

轉換流

InputStreamReader---轉換輸入流---將字節流轉化爲字符流---底層是以字節流形式讀取,但是表層是以字符流形式操作

OutputStreamWriter---轉換輸出流---將字符流轉化爲字節流---表層是以字符流形式寫出,底層是以字節流形式來傳輸

練習:轉換文件編碼

系統流/標準流

都是字節流

System.in 標準輸入流

System.out 標準輸出流

System.err 標準錯誤流

練習:從控制檯獲取一行數據

BufferedReader ---轉換--- System.in

打印流

PrintStream/PrintWriter---傳入流之後向指定的位置打入數據

合併流

序列化、反序列化流

Properties

靜態導入

枚舉

可變參數


合併流

SequenceInputStream---字節流---可以合併多個字節輸入流,將這多個字節輸入流進行合併的時候,需要將這個多個流放到一個Vector集合,利用Vector集合產生一個Enumeration對象,利用Enumeration對象來構造合併流對象---合併流只有輸入沒有輸出

---只有輸入流沒有輸出流---如果要合併多個流,需要將這些流放到一個Vector對象中,然後利用Vector產生Enumeration對象,然後利用Enumeration對象來構建一個合併流對象


序列化/反序列化流

序列化:將對象進行完整存儲---持久化

反序列化:將對象還原回來

注意:

1. 如果一個對象要想被序列化,這個對象對應的類必須實現一個接口---Serializable---這個接口中沒有任何的方法和屬性,僅僅用來標識這個類產生的對象可以被序列化

2. static/transient修飾的屬性不會被序列化

3. 每一個類在序列化的時候都會有一個版本號。如果沒有手動指定版本號,那麼在編譯的時候會根據當前類中的方法和屬性自動計算一個版本號。對象在反序列化的時候會比較對象中的版本號和當前類中的版本號是否一致。如果手動指定了版本號,就不再自動計算。---版本號serialVersionUID---默認用private static final long修飾

4. 集合/數組不能被序列化

盒子------鑰匙---產生器

Properties

是一個可以持久化的映射。鍵和值的類名默認爲String,而且只能是String.---Properties對象必須存儲到properties文件中。properties文件中不能存儲中文,一旦輸入中文,就會變成對應的編碼---properties文件的默認編碼格式爲ISO-8859-1

JDK5的特性

自動封箱拆箱 泛型 增強for循環 靜態導入 可變參數 枚舉 反射 註解 動態代理 內省

靜態導入

import static 包名.類名.方法名; ---只能夠導入靜態方法

可變參數

是指參數個數不做限定。用 ... 定義,本質上是一個數組。可變參數在使用的時候可以不傳值也可以傳入一個或者多個值。

枚舉

適用於一些取值相對固定並且能夠一一列舉的場景(星期、月份、季節)。用enum來定義一個枚舉類。枚舉常量必須定義在首行,而且之間用 , 隔開。

枚舉類中允許定義一切的方法和屬性。---可以定義抽象方法

所有的枚舉類的頂級父類---java.lang.Enum

switch---需要一個表達式,表達式的值是byte/short/char/int, JDK1.7允許使用StringJDK1.5開始支持枚舉常量


在取值相對固定且能夠一一列舉的情況下,建議使用枚舉類。

通過enum來定義一個枚舉類。枚舉常量必須放在枚舉類的首行。枚舉類中的構造函數只能是私有的。枚舉類中允許定義任何的方法和屬性,包括抽象方法。

Java中枚舉的頂級父類是java.lang.Enum---JDK1.5的特性之一

JDK1.5開始,枚舉常量支持switch-case



Junit測試

單元測試。需要導入Junit庫,在要測試的方法之上添加@Test---要求測試的方法無參無返回值非靜態

Debug調試

斷點調試。

F5---鑽入方法中,查看方法的執行

F6---執行到下一行

F7---返回上一步

F8 ---執行到下一個斷點

 

線程

進程---計算機在執行的任務或者邏輯---服務(沒有界面的進程)

線程---進程中任務的一個小任務---QQ,下載軟件,JVM

記事本是單進程多線程

自定義線程

1. 繼承 Thread類,重寫其中的run方法,將線程要執行的邏輯寫到run方法。通過線程對象身上的start方法來啓動線程

2. 實現Runnable接口,重寫的run方法。通過Thread對象來啓動這個線程

3. 實現Callable<T>接口,重寫的call方法。---現階段要求知道即可

線程的執行並不是有序的,是相互搶佔,而且搶佔並不是只在線程執行的開始而是發生在線程執行的每一步過程中---由於多個線程之間相互搶佔資源導致出現了不符合常理的情況,稱之爲多線程的併發安全問題

 

同步代碼塊---將可能出現問題的代碼放到了synchronized中,需要一個鎖對象---必須是所有線程都認識的資源---共享資源,類的字節碼,this

同步方法---synchronized修飾方法---鎖對象是this

死鎖---產生的原因:多個線程,共享資源過多,鎖對象不統一,鎖的嵌套---避免死鎖:統一鎖對象,減少鎖的嵌套

同步/異步---如果一個對象在某個時間段內只允許一個線程操作--同步

同步一定是安全的,不安全一定是異步的。

HashMap---異步式線程不安全

Hashtable---同步式線程安全

ConcurrentHashMap---異步式線程安全

通過等待喚醒機制調節線程之間的執行順序---線程之間的相互通信

線程在等待期間是位於線程池中的。---線程池本質上是一個存儲線程的隊列

a1,a2,c1,c2() -> a1 running -> a1,a2,c1,c2() -> a1 running -> a2,c1,c2(a1) -> a2 running -> c1,c2(a1,a2) -> c1 running -> a1,c1,c2(a2) -> c1 running -> a1,c2(a2,c1) -> c2 running -> a1(a2,c1,c2) -> a1 running -> a1,a2(c1,c2) -> a1 running -> a2(c1,c2,a1) -> a2 running -> (c1,c2,a1,a2)

總結:sleepwait有什麼區別?

sleep方法需要指定睡眠時間,到點自然醒。釋放執行權,不釋放鎖。被設計在Thread類上,是一個靜態方法

wait方法可以指定等待時間也可以不指定,如果不指定時間需要喚醒。釋放執行權,釋放鎖。被設計在了Object類上,是一個普通的方法、wait方法必須結合鎖來使用。

守護線程

守護其他線程的執行。當被守護的線程結束之後,守護線程無論完成與否都會隨之結束。

只要代碼中出現了守護線程,要麼這個線程是守護線程要麼就是被守護的線程---如果出現了多個被守護的線程,那麼以最後一個被守護的線程作爲結束標誌。

 

線程的狀態

 

守護線程

守護其他線程的執行。當被守護的線程結束之後,守護線程無論完成與否都會隨之結束。

只要代碼中出現了守護線程,要麼這個線程是守護線程要麼就是被守護的線程---如果出現了多個被守護的線程,那麼以最後一個被守護的線程作爲結束標誌。

線程的優先級

線程有1-1010個優先級。優先級越高,理論上線程搶到資源的概率越大。但是相鄰兩個優先級之間幾乎看不出差別,至少相差5個優先級纔會略有差別。

如果沒有設置優先級,默認優先級爲5

單例模式

保證全局過程中只存在一個唯一實例的模式

餓漢式相對懶漢式來說耗費內存。懶漢式相對餓漢式而言,存在線程安全問題。

練習:生產消費模型

一個線程表示生產者,另一個線程表示消費者。商品數量總數不超過1000

0 300 140 160

160 400 380 180

180 820 0 1000

1000 0 630 370

套接字

實際上是一套用於網絡通信的API---本質上是一套基於網絡傳輸數據的流

IP地址---IPv4---由四組數組成的IP地址,每組數的範圍在0~255之間---IPv6

端口---用於和外界進行信息交互的媒介--- 0~65535---其中0~1024已經被計算機內部以及一些常用應用佔用

news.baidu.com --- 域名

DNS服務器---將域名解析爲對應的IP地址

網絡模型

七層

物理層 數據鏈路層 網絡層---IP  傳輸層---UDP/TCP 會話層 應用層 表示層---http,ftp,POP3,SMTP,TELNET...

UDP

基於流的。不需要建立連接,不可靠。傳輸速度比較快,會對數據進行封包,每個包不超過64k。適用與一些要求速度而不要求可靠性的場景。

發送端:

1. 創建套接字對象

2. 準備數據包,綁定要發送的地址

3. 發送數據

4. 關流

接收端:

1. 創建套接字對象,綁定端口號

2. 準備數據包

3. 接收數據

4. 關流

5. 將數據從數據包解析出來

練習:單人聊天

255.255.255.255---廣播地址---只要是同一個網段內的計算機都可以收到這個數據

TCP

基於流的。建立連接,經過三次握手,可靠。傳輸速度相對較慢,理論上不限制數據大小。適用於要求可靠性而不要求速度的場景。

客戶端:

1. 創建客戶端套接字對象

2. 發起連接,綁定連接地址

3. 獲取輸出流,寫出數據

4. 通知服務器端數據寫出完畢

5. 關流

服務器端:

1. 創建服務器端套接字對象,綁定端口號

2. 接受連接,獲取一個Socket對象

3. 獲取輸入流,讀取數據

4. 通知客戶端數據已經讀取完畢

5. 關流

注意:receive/connect/accept/read/write都會產生阻塞

擴展:BIO---BlockingIO---同步式阻塞式IO

NIO---NewIO---NonBlockingIO---同步式非阻塞式IO---JDK1.4

AIO---AsynchronousIO---異步式非阻塞式IO---JDK1.8

練習:上傳文件

反射


 


發佈了1 篇原創文章 · 獲贊 12 · 訪問量 6812
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章