1. 程式人生 > >Java IO7:管道流、對象流

Java IO7:管道流、對象流

edi 通信 讀取文件 fin .html 分享 識別 tps ali

一、前言

  前面的文章主要講了文件字節輸入流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() {
        return
pipedOutputStream; } @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:管道流、對象流