理解Java之IO流
流是一種抽象概念,它代表了資料的無結構化傳遞。用來進行輸入輸出操作的流就稱為IO流。
IO流結構
流的分類方式
按流向分:從檔案/網路/記憶體等(資料來源)到程式是輸入流
從程式到檔案/網路/記憶體等(資料來源)是輸出流
按資料處理單位分
位元組流:以位元組為單位傳輸資料的流,以Stream結尾的都是位元組流。
字元流:以字元為單位傳輸資料的流,以Reader結尾的都是輸入字元流,以Writer結尾的都是輸出字元流。
按功能(層次)分
節點流:用於直接操作目標裝置的流
處理流(也叫過濾流):是對一個已存在的流的連線和封裝,通過對資料的處理為程式提供更為強大、靈活的讀寫功能。
IO流的結構
如下圖所示:
注意:
所有的位元組輸入流類都是InputStream的子類;所有的位元組符輸入流類都是Reader的子類;所有的位元組節輸出流類都是OutputStream的子類;所有的位元組符輸出流類都是Writer的子類,且他們都為抽象類。
IO流四大抽象類
InputStream的基本方法:
public abstract int read() throws IOException {}//從輸入流中讀取資料的下一個位元組, 返回讀到的位元組值.若遇到流的末尾,返回-1 public int read(byte[] b) throws IOException {}//從輸入流中讀取 b.length 個位元組的資料並存儲到緩衝區陣列b中.返回的是實際讀到的位元組總數 public int read(byte[] b, int off, int len) throws IOException {}//讀取 len 個位元組的資料,並從陣列b的off位置開始寫入到這個陣列中 public void close() throws IOException {}//關閉此輸入流並釋放與此流關聯的所有系統資源 public int available() throws IOException {}//返回此輸入流下一個方法呼叫可以不受阻塞地從此輸入流讀取(或跳過)的估計位元組數 public long skip(long n) throws IOException {}//跳過和丟棄此輸入流中資料的 n 個位元組,返回實現路過的位元組數。
OutputStream的基本方法:
public abstract void write(int b) throws IOException {}//將指定的位元組寫入此輸出流。 public void write(byte[] b) throws IOException {}// 將 b.length 個位元組從指定的 byte 陣列寫入此輸出流。 public void write(byte[] b, int off, int len) throws IOException {}//將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此輸出流。 public void flush() throws IOException {}//重新整理此輸出流並強制寫出所有緩衝的輸出位元組。 pulbic void close() throws IOException {}//關閉此輸出流並釋放與此流有關的所有系統資源。
Reader的基本方法:
public int read() throws IOException {}//讀取單個字元,返回作為整數讀取的字元,如果已到達流的末尾返回-1
public int read(char[] cbuf) throws IOException {}//將字元讀入陣列,返回讀取的字元數
public abstract int read(char[] cbuf, int off, int len) throws IOException {}//讀取 len 個字元的資料,並從陣列cbuf的off位置開始寫入到這個陣列中
public abstract void close() throws IOException {}//關閉該流並釋放與之關聯的所有資源
public long skip(long n) throws IOException {}//跳過n個字元。
public int available() //還可以有多少能讀到的位元組數
Writer的基本方法:
public void write(int c) throws IOException {} //寫入單個字元
public void write(char[] cbuf) throws IOException {} //寫入字元陣列
public abstract void write(char[] cbuf, int off, int len) throws IOException {} //寫入字元陣列的某一部分
public void write(String str) throws IOException {} //寫入字串
public void write(String str, int off, int len) throws IOException {}//寫字串的某一部分
public abstract void close() throws IOException {} //關閉此流,但要先重新整理它
public abstract void flush() throws IOException {} //重新整理該流的緩衝,將緩衝的資料全寫到目的地
IO流的具體使用
FileInputStream 和 FileOutputStream
public static void fileInputStreamTest() {
File f1 = new File("D:\\in.txt");
File f2 = new File("D:\\out.txt");
try {
FileInputStream fi = new FileInputStream(f1);
FileOutputStream fo = new FileOutputStream(f2);
byte[] buf = new byte[521];
int len = 0;
while((len = fi.read(buf)) != -1){
fo.write(buf, 0, len);
}
fo.flush();
fo.close();
fi.close();
} catch (Exception e) {
e.printStackTrace();
}
}
PipedOutputStream和PipedintputStream
Java裡的管道輸入流PipedInputStream與管道輸出流PipedOutputStream實現了類似管道的功能,用於不同執行緒之間的相互通訊。
Java的管道輸入與輸出實際上使用的是一個迴圈緩衝陣列來實現,這個陣列預設大小為1024位元組。輸入流PipedInputStream從這個迴圈緩衝陣列中讀資料,輸出流PipedOutputStream往這個迴圈緩衝陣列中寫入資料。當這個緩衝陣列已滿的時候,輸出流PipedOutputStream所在的執行緒將阻塞;當這個緩衝陣列首次為空的時候,輸入流PipedInputStream所在的執行緒將阻塞。Java在它的jdk文件中提到不要在一個執行緒中同時使用PipeInpuStream和PipeOutputStream,這會造成死鎖。
public class WriteThread implements Runnable{
private PipedOutputStream pout;
WriteThread(PipedOutputStream pout){
this.pout= pout;
}
@Override
public void run() {
try {
System.out.println("W:開始將資料寫入:但等個5秒讓我們觀察...");
Thread.sleep(5000); //釋放cpu執行權5秒
pout.write("writePiped 資料...".getBytes()); //管道輸出流
pout.close();
} catch(Exception e) {
throw new RuntimeException("W:WriteThread寫入失敗...");
}
}
}
public class ReadThread implements Runnable{
private PipedInputStream pin;
ReadThread(PipedInputStream pin) {
this.pin=pin;
}
@Override
public void run() { //由於必須要覆蓋run方法,所以這裡不能拋,只能try
try {
System.out.println("R:讀取前沒有資料,阻塞中...等待資料傳過來再輸出到控制檯...");
byte[] buf = new byte[1024];
int len = pin.read(buf); //read阻塞
System.out.println("R:讀取資料成功,阻塞解除...");
String s= new String(buf,0,len);
System.out.println(s); //將讀取的資料流用字串以字串打印出來
pin.close();
} catch(Exception e) {
throw new RuntimeException("R:管道讀取流失敗!");
}
}
}
public class Test {
public static void main(String[] args) throws IOException {
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream();
pin.connect(pout); //輸入流與輸出流連線
ReadThread readTh = new ReadThread(pin);
WriteThread writeTh = new WriteThread(pout);
new Thread(readTh).start();
new Thread(writeTh).start();
}
}
BufferedInputStream和BufferedOutputStream
FileInputStream和FileOutputStream 在使用時,我們介紹了可以用byte陣列作為資料讀入的快取區,以讀檔案為列,讀取硬碟的速度遠遠低於讀取記憶體的資料,為了減少對硬碟的讀取,通常從檔案中一次讀取一定長度的資料,把資料存入快取中,在寫入的時候也是一次寫入一定長度的資料,這樣可以增加檔案的讀取效率。我們在使用FileInputStream的時候是用byte陣列來做了快取,而BufferedInputStream and BufferedOutputStream已經為我們增加了這個快取功能。
public static void fileBufferTest() {
File f1 = new File("D:\\in.txt");
File f2 = new File("D:\\out.txt");
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(f1));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(f2));
byte[] data = new byte[1];
while(bufferedInputStream.read(data)!=-1) {
bufferedOutputStream.write(data);
}
//將緩衝區中的資料全部寫出
bufferedOutputStream.flush();
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
讀寫物件ObjectInputStream和ObjectOutputStream
public class ObjectStream {
public static void main(String[] args) {
ObjectStream.objectStreamTest();
}
public static void objectStreamTest() {
Demo newObject;
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:/123.obj")));
oos.writeObject(new Demo());
oos.flush();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:/123.obj")));
newObject = (Demo)ois.readObject();
System.out.println(newObject.num);
ois.close();
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Demo implements Serializable{
private static final long serialVersionUID = 1L;
int num = 30;
}
SequenceInputStream
合併流,將與之相連線的流集組合成一個輸入流並從第一個輸入流開始讀取, 直到到達檔案末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。 合併流的作用是將多個源合併合一個源。可接收列舉類所封閉的多個位元組流物件。
public class Test {
public static void main(String[] args) {
doSequence();
}
private static void doSequence() {
SequenceInputStream sis = null; // 建立一個合併流的物件
BufferedOutputStream bos = null; // 建立輸出流。
try {
// 構建流集合
Vector<InputStream> vector = new Vector<InputStream>();
vector.addElement(new FileInputStream("D:/in.txt"));
vector.addElement(new FileInputStream("D:/out.txt"));
Enumeration<InputStream> e = vector.elements();
sis = new SequenceInputStream(e);
bos = new BufferedOutputStream(new FileOutputStream("/Users/zhengchao/cctv/File_OUT.txt"));
// 讀寫資料
byte[] buf = new byte[1024];
int len = 0;
while ((len = sis.read(buf)) != -1) {
bos.write(buf, 0, len);
bos.flush();
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
try {
if (sis != null)
sis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileReader與FileWriter
public static void fileReaderTest() {
File f1 = new File("D:\\in.txt");
File f2 = new File("D:\\out.txt");
try {
FileReader fr = new FileReader(f1);
FileWriter fw = new FileWriter(f2);
char[] ch = new char[512];
int len = 0;
while((len = fr.read(ch)) != -1){
fw.write(ch, 0, ch.length);
}
fw.flush();
fw.close();
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
BufferedReader和BufferedWriter
public static void fileBufferReaderWriteTest() {
File f1 = new File("D:\\in.txt");
File f2 = new File("D:\\out.txt");
try {
BufferedReader br=new BufferedReader(new FileReader(f1));
BufferedWriter bw=new BufferedWriter(new FileWriter(f2));
String s=br.readLine();
while(null!=s) {
bw.write(s);
//由於BufferedReader的readLine()是不讀入換行符的,所以寫入換行時須用newLine()方法
bw.newLine();
s=br.readLine();
}
br.close();
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
注:在使用過程中應注意將編碼格式設定為UTF-8,否則檔案寫入會亂碼。
以上只是IO流的部分類,用於對IO流相關知識進行查閱,後面再實際的工作中也會根據實際需求不斷新增。
參考自:
https://blog.csdn.net/zhengchao1991/article/details/53033137
https://blog.csdn.net/SilenceOO/article/details/50995062
https://blog.csdn.net/Yue_Chen/article/details/72772445