Java IO7:管道流、對象流
一、前言
前面的文章主要講了文件字節輸入流FileInputStream、文件字節輸出流FileOutputStream以及對應的字節緩沖流,文件字符輸入流FileReader、文件字符輸出流FileWriter以及對應的字符緩沖流,這些都是常見的流類。當然,除了這些流類,Java還提供了別的流類供用戶使用,接下來看一下管道流和對象流。
二、管道流
管道流主要用於連接兩個線程的通信,充當一個信息傳遞的管道。管道流也分為字節流(PipedInputStream、PipedOutputStream)和字符流(PipedReader、PipedWriter)。比如一個管道輸出流PipedOutputStream必須和一個管道輸入流PipedInputStream進行連接而產生一個通信管道,管道輸出流PipedOutputStream向管道中寫入數據,管道輸入流PipedInputStream從管道中讀取數據。管道流的工作如下圖所示:
舉例說明管道流的用法。既然管道流的作用適用於線程之間的通信,那麽勢必會有發送信息的線程和接收信息的線程,先定義一個發送數據的線程,通過管道輸出流的write()方法將數據寫入到管道中。
public class SendDataThread implements Runnable{ private PipedOutputStream pipedOutputStream = new PipedOutputStream(); public PipedOutputStream getPipedOutputStream() { returnpipedOutputStream; } @Override public void run() { String sendData = "receiveDataThread:hello!!!"; try { //像管道輸出流中寫入數據(發送數據) pipedOutputStream.write(sendData.getBytes()); pipedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
在定義一個接收數據的線程,通過管道輸入流的read()方法將數據從管道中讀取過來
public class ReceiveDataThread implements Runnable{ private PipedInputStream pipedInputStream = new PipedInputStream(); public PipedInputStream getPipedInputStream() { return pipedInputStream; } @Override public void run() { byte[] bytes = new byte[1024]; try { //將管道輸出流中的內容讀取到字節數組中 int read = pipedInputStream.read(bytes); if(read != -1){ System.out.println("發送過來的信息為:" + new String(bytes)); } pipedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
發送數據的線程和接收數據的線程都定義好了,接下來在利用管道輸出流的connect()方法將管道輸出流和管道輸入流連接起來,main線程中測試
public class ReceiveDataThread implements Runnable{ private PipedInputStream pipedInputStream = new PipedInputStream(); public PipedInputStream getPipedInputStream() { return pipedInputStream; } @Override public void run() { byte[] bytes = new byte[1024]; try { //將管道輸出流中的內容讀取到字節數組中 int read = pipedInputStream.read(bytes); if(read != -1){ System.out.println("發送過來的信息為:" + new String(bytes)); } pipedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
結果如下:
發送過來的信息為:receiveDataThread:hello!!!
註意一下,PipedInputStream運用的是一個1024字節固定大小的字節數組當做循環緩沖區,寫入PipedOutputStream的數據實際上保存到了對應的PipedInputStream的內部緩沖區。PipedInputStream執行讀操作時,讀取的數據實際上來自這個內部緩沖區。如果對應的PipedInputStream輸入緩沖區已滿,任何企圖寫入PipedOutputStream的線程都將被阻塞。而且這個寫操作線程將一直阻塞,直至出現讀取PipedInputStream的操作從緩沖區刪除數據。
這意味著,向PipedOutputStream寫入數據的線程不應該是負責從對應PipedInputStream讀取數據的那個線程(所以這裏開了兩個線程分別用於讀寫)。假定t線程試圖一次對PipedOutputStream的write()方法的調用中向對應的PipedOutputStream寫入2000字節的數據,在t線程阻塞之前,它最多能夠寫入1024字節的數據(PipedInputStream內部緩沖區的大小)。然而,一旦t被阻塞,讀取PipedInputStream的操作就再也不能出現了,因為t是唯一讀取PipedInputStream的線程,這樣,t線程已經完全被阻塞。
三、對象流
Java中提供了ObjectInputStream和ObjectOutputStream這兩個類來進行對象的序列化操作,是完成對象的存儲和讀取的輸入/輸出流類(字節流),只要把對象中的所有成員變量都存儲起來,就等於保存了這個對象,之後從保存的對象之中再將對象讀取進來就可以繼續使用此對象。ObjectInputStream、ObjectOutputStream可以幫助開發者完成保存和讀取對象成員變量取值的過程,但要求讀寫或存儲的對象必須實現了Serializable接口。
舉例:定義一個Person類,完成該對象的存儲和讀取
public class Person implements Serializable{ private static final long serialVersionUID = 8411755225402460289L; private String name; private transient int age; private final String country = "中國"; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name=‘" + name + ‘\‘‘ + ", age=" + age + ", country=‘" + country + ‘\‘‘ + ‘}‘; } }
進行對象的存儲和讀取(序列化和反序列化)
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { File file = new File("D:" +File.separator + "person.txt"); OutputStream outputStream = new FileOutputStream(file); //創建對象輸出流 ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); Person person = new Person("zhangsan",15); //將對象存儲到文件中 objectOutputStream.writeObject(person); objectOutputStream.close(); //創建對象輸入流 InputStream inputStream = new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); //讀取文件中的對象信息 Person person1 = (Person) objectInputStream.readObject(); System.out.println(person1); objectInputStream.close(); } }
結果:person.txt文件的內容
看到亂碼,因為序列化之後本身就是按照一定的二進制格式組織的文件,這些二進制格式不能被文本文件所識別,所以亂碼也是正常的。
控制臺輸出結果:
Person{name=‘zhangsan‘, age=0, country=‘中國‘}
可以看到age為0,而不是15,這也證明了對象中被transient修飾的成員變量不會被序列化。
參考資料:Java IO7:管道流、對象流
Java IO7:管道流、對象流