IO流與多執行緒總結
IO流
1.概念
IO流用來處理裝置之間的資料傳輸
Java對資料的操作是通過流的方式
Java用於操作流的類都在IO包中
流按流向分為兩種:輸入流,輸出流。
流按操作型別分為兩種:位元組流與字元流。 位元組流可以操作任何資料,字元流只能操作純字元資料,比較方便。
2.IO流常用父類
位元組流的抽象父類:
InputStream ,OutputStream
字元流的抽象父類:
Reader , Writer
3.IO程式書寫
使用前,匯入IO包中的類
使用時,進行IO異常處理
使用後,釋放資源
位元組流
1.讀取檔案
建立FileInputStream物件, 指定一個檔案. 檔案必須存在, 不存在則會丟擲FileNotFoundException
使用read()方法可以從檔案中讀取一個位元組. 如果讀取到檔案末尾會讀到-1
讀取結束後需要釋放資源, 呼叫close()方法關閉輸入流
2.寫出檔案
建立FileOutputStream物件, 指定一個檔案. 檔案不存在會建立新檔案, 存在則清空原內容. 如果需要追加, 在建構函式中傳入true.
使用write()方法可以向檔案寫出一個位元組.
寫出結束後同樣需要呼叫close()
3.拷貝檔案
可以從檔案中逐個位元組讀取, 逐個位元組寫出, 但這樣做效率非常低
我們可以定義一個數組作為緩衝區, 一次讀取多個位元組裝入陣列, 然後再一次性把陣列中的位元組寫出1byte = 8bit
4.常用方法
InputStream:
read() 讀取一個位元組
read(byte[]) 讀取若干(陣列長度)位元組
available() 獲取可讀的位元組數
close() 關閉流, 釋放資源
OutputStream:
write(int) 寫出一個位元組
write(byte[]) 寫出陣列中的所有位元組
write(byte[],start,len);
close() 關閉流, 釋放資源
5.BufferedInputStream
BufferedInputStream內建了一個緩衝區(陣列)
從BufferedInputStream中讀取一個位元組時
BufferedInputStream會一次性從檔案中讀取8192個, 存在緩衝區中, 返回給程式一個
程式再次讀取時, 就不用找檔案了, 直接從緩衝區中獲取
直到緩衝區中所有的都被使用過, 才重新從檔案中讀取8192個
6.BufferedOutputStream
BufferedOutputStream也內建了一個緩衝區(陣列)
程式向流中寫出位元組時, 不會直接寫到檔案, 先寫到緩衝區中
直到緩衝區寫滿, BufferedOutputStream才會把緩衝區中的資料一次性寫到檔案裡
字元流
1.字元流是什麼
字元流是可以直接讀寫字元的IO流
位元組流只能讀寫位元組. 如果要讀取字元, 就要先讀取到位元組資料, 然後轉為字元. 如果要寫出字元, 需要把字元轉為位元組再寫出.
2.FileReader, FileWriter
FileReader類的read()方法可以按照字元大小讀取
FileWriter類的write()方法可以自動把字元轉為位元組寫出
3.什麼情況下使用字元流
字元流也可以拷貝文字檔案, 但不推薦使用. 因為讀取時會把位元組轉為字元, 寫出時還要把字元轉回位元組.
程式需要讀取一段文字, 或者需要寫出一段文字的時候可以使用字元流
4.帶緩衝的字元流
BufferedReader的read()方法讀取字元時會一次讀取若干字元到緩衝區, 然後逐個返回給程式, 降低讀取檔案的次數, 提高效率
BufferedWriter的write()方法寫出字元時會先寫到緩衝區, 緩衝區寫滿時才會寫到檔案, 降低寫檔案的次數, 提高效率
BufferedReader的readLine()方法可以讀取一行字元(不包含換行符號)
BufferedWriter的newLine()可以輸出一個跨平臺的換行符號
5.LineNumberReader
LineNumberReader是BufferedReader的子類, 具有相同的功能, 並且可以統計行號
呼叫getLineNumber()方法可以獲取當前行號
呼叫setLineNumber()方法可以設定當前行號
6.使用指定的碼錶讀取字元
FileReader是使用預設碼錶讀取檔案, 如果需要使用指定碼錶讀取, 那麼可以使用InputStreamReader
FileWriter是使用預設碼錶寫出檔案, 如果需要使用指定碼錶寫出, 那麼可以使用OutputStreamWriter
File
1.什麼是File類
File類物件可以代表一個路徑, 此路徑可以是檔案也可以是資料夾, 該類方法可以對這個路徑進行各種操作
2.建立物件
給File類建構函式傳一個String型別的路徑就可以建立物件
路徑分為兩種: 絕對路徑, 相對路徑
絕對路徑: 從碟符開始, 是一個固定的路徑
相對路徑: 不從碟符開始, 相對於某個位置. 在Eclipse中的Java工程如果使用相對路徑, 那麼就相對於工程根目錄. cmd則相對應於當前目錄.
3.常用方法
boolean exists() 判斷是否存在
boolean isAbsolute(); 是否是絕對路徑
boolean isDirectory(); 是否是資料夾
boolean isFile(); 是否是檔案
boolean isHidden(); 是否是隱藏
getAbsolutePath(); 獲取絕對路徑
getFreeSpace(); 獲取當前碟符剩餘空間
getTotalSpace(); 獲取當前碟符總空間
getUsableSpace(); 獲取當前碟符可用空間
getParent()); 獲取父級路徑
getName()); 獲取檔名
setReadable(false); 設定是否可讀
setWritable(false); 設定是否可寫
setExecutable(false); 設定是否可執行
canRead(); 是否可讀
canWrite(); 是否可寫
canExecute(); 是否可執行
setLastModified(); 設定檔案的最後修改時間
lastModified(); 獲取檔案的最後修改時間
createNewFile() 建立檔案
mkdir(); 建立資料夾(僅一級)
mkdirs(); 建立資料夾(父級不存在也建立)
renameTo(); 改名, 可以移動檔案
delete() 刪除, 檔案可以直接刪除, 資料夾只能刪空的
length() 檔案大小
String[] list()
File[] listFiles()
異常
(1)就是程式執行過程中,遇到了問題,這就叫異常。
(2)異常的體系
Throwable 其實應該分三種
Error
通常出現重大問題如:伺服器宕機資料庫崩潰。
不編寫針對程式碼對其處理(這個我們是無能為力的)。
Exception
除了 RuntimeException 和其所有子類,其他所有的異常類都是在編譯的時候必須要處理的
要麼try,要麼拋
RuntimeException
RuntimeException 和其所有的子類,都不會在編譯的時候報異常,而是在執行時報異常,這時候我們
就需要回頭看看我們的程式碼是否有問題,比如角標越界,空指標等
(3)Throwable
A:getMessage() :返回此 throwable 的詳細訊息字串。
B:toString():獲取異常類名和異常資訊,返回字串。
C:printStackTrace():獲取異常類名和異常資訊,以及異常出現在程式中的位置。返回值void。
(4)處理異常處理方式:
A:try...catch...finally
格式:
try {
需要檢測的程式碼;
}
catch(異常類 變數) {
異常處理程式碼;
}
...
finally {
一定會執行的程式碼;
}
可以有下面三種搭配形式:
**try...catch(...)
**try...finally
**try...catch(...)...finally
B:丟擲 throws throw
throws:用於標識函式暴露出的異常。thorws用在函式上,後面跟異常類名(可以由多個,隔開)。
throw:用於丟擲異常物件。throw用在函式內,後面跟異常物件。new Exception();
C:到底用誰?
**你能處理,建議處理。try...catch...finally
**你處理不了,丟擲。
**在實際開發中,是分層開發,底層程式碼是能丟擲儘量丟擲,到頂層的用日誌記錄住異常資訊,並提供解決方案
D:自定義異常
自定義類繼承Exception或者其子類(RuntimeException)
class MyException extends Exception{
MyException(){}
MyException(String message){
super(message); //將資訊傳遞給父類,呼叫父類封裝好的構造方法
}
}
class Student {
public void giveAge(int age) throws MyException {
if(age>40 || age<16) {
//throw new MyExcetpion("建議不學了");
MyExcepiont my = new MyExcetpion("建議不學了");
throw my;
}
else {
System.out.println("可以學習Java");
}
}
}
E:RuntimeException和Exception
區別:RuntimeException就是要你改程式碼的。你可以不處理。
序列流
1.什麼是序列流
序列流可以把多個位元組輸入流整合成一個, 從序列流中讀取資料時, 將從被整合的第一個流開始讀, 讀完一個之後繼續讀第二個, 以此類推.
2.使用方式
整合兩個: SequenceInputStream(InputStream, InputStream)
整合多個: SequenceInputStream(Enumeration)
記憶體輸出流
1.什麼是記憶體輸出流
該輸出流可以向記憶體中寫資料, 把記憶體當作一個緩衝區, 寫出之後可以一次性獲取出所有資料
2.使用方式
建立物件: new ByteArrayOutputStream()
寫出資料: write(int), write(byte[])
獲取資料: toByteArray()
物件操作流
1.什麼是物件操作流
該流可以將一個物件寫出, 或者讀取一個物件到程式中. 也就是執行了序列化和反序列化的操作.
2.使用方式
寫出: new ObjectOutputStream(OutputStream), writeObject()
讀取: new ObjectInputStream(InputStream), readObject()
3.注意
要寫出的物件必須實現Serializable接口才能被序列化
列印流
1.什麼是列印流(PrintStream)
該流可以很方便的將物件的toString()結果輸出, 並且自動加上換行, 而且可以使用自動刷出的模式
System.out就是一個PrintStream, 其預設向控制檯輸出資訊
2.使用方式
列印: print(), println()
自動刷出: PrintWriter(OutputStream out, boolean autoFlush)
標準輸入輸出流
1.什麼是標準輸入輸出流
System.in是InputStream, 標準輸入流, 預設可以從鍵盤輸入讀取位元組資料
System.out是PrintStream, 標準輸出流, 預設可以向Console中輸出字元和位元組資料
2.修改標準輸入輸出流
修改輸入流: System.setIn(InputStream)
修改輸出流: System.setOut(PrintStream)
資料輸入輸出流
1.什麼是資料輸入輸出流
DataInputStream, DataOutputStream可以按照基本資料型別大小讀寫資料
例如按Long大小寫出一個數字, 寫出時該資料佔8位元組. 讀取的時候也可以按照Long型別讀取, 一次讀取8個位元組.
2.使用方式
DataInputStream(InputStream), readInt(), readLong()
DataOutputStream(OutputStream), writeInt(), writeLong()
Properties 集合
1.向記憶體中存入值,並通過鍵獲取值setProperty(key,value) getProperty(key);
2.通過load方法,讀取配置檔案,propertyNames獲取所有的key,返回Enumeration
3.根據鍵改值,並重新存入到配置檔案setProperty(key,value),list(new PrintStream())
System.getProperties();獲取系統屬性,propertyNames將所有的鍵返回到列舉裡,就可以迭代了
執行緒的概念
1.什麼是執行緒
執行緒是程式執行的一條路徑, 一個程序中可以包含多條執行緒
多執行緒併發執行可以提高程式的效率, 可以同時完成多項工作
2.多執行緒的應用場景
紅蜘蛛同時共享螢幕給多個電腦
迅雷開啟多條執行緒一起下載
QQ同時和多個人一起視訊
伺服器同時處理多個客戶端請求
開啟新執行緒
1.繼承Thread
定義類繼承Thread
重寫run方法
把新執行緒要做的事寫在run方法中
建立執行緒物件
開啟新執行緒, 內部會自動執行run方法
2.實現Runnable
定義類實現Runnable介面
實現run方法
把新執行緒要做的事寫在run方法中
建立自定義的Runnable物件
建立Thread物件, 傳入Runnable
呼叫start()開啟新執行緒, 內部會自動呼叫Runnable的run()方法
3.兩種方式的區別
區別一:
a.由於子類重寫了Thread類的run(), 當呼叫start()時, 直接找子類的run()方法
b.建構函式中傳入了Runnable的引用, 成員變數記住了它, start()呼叫run()方法時內部判斷成員變數Runnable是否為空, 不為空則執行Runnable的run()
區別二:
a.繼承Thread只能是單繼承,如果自己定義的執行緒類已經有了父類,就不能再繼承了
b.實現Runnable介面可以多實現,即使自己定義執行緒類已經有父類可以實現Runnable介面
總結:繼承Thread的好處是:可以直接使用Thread類中的方法,程式碼簡單
弊端是:如果已經有了父類,就不能用這種方法
實現Runnable介面的好處是:即使自己定義的執行緒類有了父類也沒關係,因為有了父類也可以實現介面,而且介面是可以多實現的
弊端是:不能直接使用Thread中的方法需要先獲取到執行緒物件後,才能得到Thread的方法,程式碼複雜
Thread類常用方法
1.設定名字
通過建構函式可以傳入String型別的名字
通過setName(String)方法可以設定執行緒物件的名字
2.獲取名字
通過getName()方法獲取執行緒物件的名字
3.獲取當前執行緒物件
Thread.currentThread(), 主執行緒也可以獲取
4.休眠
Thread.sleep(毫秒,納秒), 控制當前執行緒休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000納秒 1000000000
5.守護
setDaemon(), 設定一個執行緒為守護執行緒, 該執行緒不會單獨執行, 當其他非守護執行緒都執行結束後, 自動退出
6.加入
join(), 當前執行緒暫停, 等待指定的執行緒執行結束後, 當前執行緒再繼續
join(int), 可以等待指定的毫秒之後繼續
執行緒之間的同步
1.什麼情況下需要同步
當多執行緒併發, 有多段程式碼同時執行時, 我們希望某一段程式碼執行的過程中CPU不要切換到其他執行緒工作. 這時就需要同步.
如果兩段程式碼是同步的, 那麼同一時間只能執行一段, 在一段程式碼沒執行結束之前, 不會執行另外一段程式碼.
2.同步程式碼塊
使用synchronized關鍵字加上一個鎖物件來定義一段程式碼, 這就叫同步程式碼塊
多個同步程式碼塊如果使用相同的鎖物件, 那麼他們就是同步的
3.同步方法
使用synchronized關鍵字修飾一個方法, 該方法中所有的程式碼都是同步的
非靜態同步方法預設使用當前物件this作為鎖物件
靜態方法同步預設的鎖物件是,所在類的位元組碼物件
4.執行緒安全問題
多執行緒併發操作同一資料時, 就有可能出現執行緒安全問題
使用同步技術可以解決這種問題, 把操作資料的程式碼進行同步, 不要多個執行緒一起操作
5.死鎖問題
多執行緒同步的時候, 如果同步程式碼巢狀, 使用相同鎖, 就有可能出現死鎖
儘量不要巢狀使用
一.執行緒的方法
1.yield讓出cpu
2.setPriority()設定執行緒的優先順序
二.執行緒之間的通訊
1.什麼時候需要通訊
多個執行緒併發執行時, 在預設情況下CPU是隨機切換執行緒的
如果我們希望他們有規律的執行, 就可以使用通訊, 例如每個執行緒執行一次列印
2.怎麼通訊
如果希望執行緒等待, 就呼叫wait()
如果希望喚醒等待的執行緒, 就呼叫notify();
這兩個方法必須在同步程式碼中執行, 並且使用同步鎖物件來呼叫
3.多個執行緒通訊的問題
notify()方法是隨機喚醒一個執行緒
notifyAll()方法是喚醒所有執行緒
JDK5之前無法喚醒指定的一個執行緒
如果多個執行緒之間通訊, 需要使用notifyAll()通知所有執行緒, 用while來反覆判斷條件
三.JDK5之後的執行緒控制
1.同步
使用ReentrantLock類的lock()和unlock()方法進行同步
2.通訊
使用ReentrantLock類的newCondition()方法可以獲取Condition物件
需要等待的時候使用Condition的await()方法, 喚醒的時候用signal()方法
不同的執行緒使用不同的Condition, 這樣就能區分喚醒的時候找哪個執行緒了
四.GUI
1.事件處理
使用者的一個操作就是一個事件, 事件處理就是在事件發生的時候程式做出什麼反應
事件發生在哪個元件上, 哪個元件就是事件源
給事件源新增一個監聽器物件
監聽器物件中包含若干事件處理方法
如果時間發生了, 事件處理方法就會自動執行
3.介面卡
a.什麼是介面卡
在使用監聽器的時候, 需要定義一個類時間監聽器介面.
通常介面中有多個方法, 而程式中不一定所有的都用到, 但又必須重寫, 這很繁瑣.
介面卡簡化了這些操作, 我們定義監聽器時只要繼承介面卡, 然後重寫需要的方法即可.
b.介面卡原理
介面卡就是一個類, 實現了監聽器介面, 所有抽象方法都重寫了, 但是方法全是空的.
目的就是為了簡化程式設計師的操作, 定義監聽器時繼承介面卡, 只重寫需要的方法就可以了.
IP地址
每個裝置在網路中的唯一標識
每臺網絡終端在網路中都有一個獨立的地址,我們在網路中傳輸資料就是使用這個地址。
ipconfig:檢視本機IP
ping:測試連線
本地迴路地址:127.0.0.1 255.255.255.255是廣播地址
IPv4:4個位元組組成,4個0-255。大概42億,30億都在北美,亞洲4億。2011年初已經用盡。
IPv6:8組,每組4個16進位制數。
1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f
1a2b::aaaa:0000:0000:0000:aabb:1f2f
1a2b:0000:aaaa::aabb:1f2f
1a2b:0000:aaaa::0000:aabb:1f2f
1a2b:0000:aaaa:0000::aabb:1f2f
埠號
每個程式在裝置上的唯一標識
每個網路程式都需要繫結一個埠號,傳輸資料的時候除了確定發到哪臺機器上,還要明確發到哪個程式。
埠號範圍從0-65535
編寫網路應用就需要繫結一個埠號,儘量使用1024以上的,1024以下的基本上都被系統程式佔用了。
常用埠
mysql: 3306
oracle: 1521
web: 80
tomcat: 8080
QQ: 4000
feiQ: 2425
網路協議
為計算機網路中進行資料交換而建立的規則、標準或約定的集合。
UDP
面向無連線,資料不安全,速度快。不區分客戶端與服務端。
TCP
面向連線(三次握手),資料安全,速度略低。分為客戶端和服務端。
三次握手: 客戶端先向服務端發起請求, 服務端響應請求, 傳輸資料
Socket
通訊的兩端都有Socket。
網路通訊其實就是Socket間的通訊。
資料在兩個Socket間通過IO流傳輸。
Socket在應用程式中建立,通過一種繫結機制與驅動程式建立關係,告訴自己所對應的IP和port。
UDP傳輸
1.傳送
建立DatagramSocket, 隨機埠號
建立DatagramPacket, 指定資料, 長度, 地址, 埠
使用DatagramSocket傳送DatagramPacket(send)
關閉DatagramSocket
2.接收
建立DatagramSocket, 指定埠號
建立DatagramPacket, 指定陣列, 長度
使用DatagramSocket接收DatagramPacket()
關閉DatagramSocket
從DatagramPacket中獲取資料
3.接收方獲取ip和埠號
String ip = packet.getAddress().getHostAddress();
int port = packet.getPort();
TCP傳輸
1.客戶端
建立Socket連線服務端(指定ip地址,埠號)通過ip地址找對應的伺服器
呼叫Socket的getInputStream()和getOutputStream()方法獲取和服務端相連的IO流
輸入流可以讀取服務端輸出流寫出的資料
輸出流可以寫出資料到服務端的輸入流
2.服務端
建立ServerSocket(需要指定埠號)
呼叫ServerSocket的accept()方法接收一個客戶端請求,得到一個Socket
呼叫Socket的getInputStream()和getOutputStream()方法獲取和客戶端相連的IO流
輸入流可以讀取客戶端輸出流寫出的資料
輸出流可以寫出資料到客戶端的輸
synchronized的問題
StringBuffer和StringBuilder
StringBuffer是執行緒安全的,效率低
StringBuilder是執行緒不安全的,效率高
Vector和ArrayList
Vector是執行緒安全的,效率低
ArrayList是執行緒不安全的,效率高
Hashtable和HashMap
Hashtable是執行緒安全的,效率低
Hashtable不能存null鍵和null值
HashMap是執行緒不安全的,效率高
HashMap可以儲存null鍵和null值