Java IO7:管道流、對象流

前言html

前面的文章主要講了文件字符輸入流FileWriter、文件字符輸出流FileReader、文件字節輸出流FileOutputStream、文件字節輸入流FileInputStream,這些都是常見的流類。固然除了這些流類以外,Java還提供了不少的流類給用戶使用,本文就看一下別的流。this

 

管道流spa

管道流主要用於鏈接兩個線程的通訊。管道流也分爲字節流(PipedInputStream、PipedOutputStream)和字符流(PipedReader、PipedWriter)。好比一個PipedInputStream必須和一個PipedOutputStream對象進行鏈接而產生一個通訊管道,PipedOutputStream向管道中寫入數據,PipedInputStream從管道中讀取數據。管道流的工做以下圖所示:線程

下面看一下管道流的用法。既然管道流的做用是用於線程間的通訊,那麼勢必有發送線程和接收線程,兩個線程經過管道流交互數據。首先寫一個發送數據的線程:3d

public class Sender implements Runnable
{
    private PipedOutputStream out = new PipedOutputStream();
    
    public PipedOutputStream getOutputStream()
    {
        return out;
    }
    
    public void run()
    {
        String str = "Receiver, 你好!";
        try
        {
            out.write(str.getBytes()); // 向管道流中寫入數據(發送)
            out.close();
        } 
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }    
}

用流寫數據的時候注意關注一下,該流是否支持直接寫String,不能夠的話要用String的getBytes()方法獲取字符串的字節。既然有一個發送數據的線程了,接下來來一個接收數據的線程:code

public class Receiver implements Runnable
{
    private PipedInputStream in = new PipedInputStream();
    
    public PipedInputStream getInputStream()
    {
        return in;
    }
    
    public void run()
    {
        String s = null;
        byte b0[] = new byte[1024];
        try
        {
            int length = in.read(b0);
            if (-1 != length)
            {
                s = new String(b0, 0 , length);
                System.out.println("收到了如下信息:" + s);
            }
            in.close();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

兩個線程都有了,寫一個main線程,利用管道輸出流的connect方法鏈接管道輸出流和管道輸入流:htm

public static void main(String[] args)
{
    try
    {
        Sender sender = new Sender();
        Receiver receiver = new Receiver();
        Thread senderThread = new Thread(sender);
        Thread receiverThread = new Thread(receiver);
        PipedOutputStream out = sender.getOutputStream(); // 寫入
        PipedInputStream in = receiver.getInputStream(); // 讀出
        out.connect(in);// 將輸出發送到輸入
        senderThread.start();
        receiverThread.start();
    } 
    catch (IOException e)
    {
        e.printStackTrace();
    } 
}

輸出結果應該很明顯了,你們都知道,接收線程接收到了來自發送線程經過管道流輸出流發送的數據:對象

收到了如下信息:Receiver, 你好!

注意一下,PipedInputStream運用的是一個1024字節固定大小的循環緩衝區,寫入PipedOutputStream的數據實際上保存到了對應的PipedInputStream的內部緩衝區。PipedInputStream執行讀操做時,讀取的數據實際上來自這個內部緩衝區。若是對應的PipedInputStream輸入緩衝區已滿,任何企圖寫入PipedOutputStream的線程都將被阻塞。並且這個寫操做線程將一直阻塞,直至出現讀取PipedInputStream的操做從緩衝區刪除數據。blog

這意味着,向PipedOutputStream寫入數據的線程不該該是負責從對應PipedInputStream讀取數據的惟一線程(因此這裏開了兩個線程分別用於讀寫)。假定t線程試圖一次對PipedOutputStream的write()方法的調用中向對應的PipedOutputStream寫入2000字節的數據,在t線程阻塞以前,它最多可以寫入1024字節的數據(PipedInputStream內部緩衝區的大小)。然而,一旦t被阻塞,讀取PipedInputStream的操做就不再能出現了,由於t是惟一讀取PipedInputStream的線程,這樣,t線程已經徹底被阻塞。接口

 

對象流

序列化,在這篇文章中已經講得比較清楚了,這一部分主要是再次簡單過一下對象流的知識而已。

Java中提供了ObjectInputStream、ObjectOutputStream這兩個類用於對象序列化操做,這兩個類是用於存儲和讀取對象的輸入輸出流類,只要把對象中的全部成員變量都存儲起來,就等於保存了這個對象,以後從保存的對象之中再將對象讀取進來就能夠繼續使用此對象。ObjectInputStream、ObjectOutputStream能夠幫助開發者完成保存和讀取對象成員變量取值的過程,但要求讀寫或存儲的對象必須實現了Serializable接口。

看一下例子,先來一個實現了Serializable接口的實體類Person:

public class Person implements Serializable
{
    /**
     * 序列化
     */
    private static final long serialVersionUID = 7827863437931135333L;
    
    private transient String     name;
    private int                    age;
    private final static String sex = "man";
    
    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    
    public String toString()
    {
        return "姓名:" + this.name + ", 年齡:" + this.age + ", 性別:" + sex;
    }
}

調用ObjectOutputStream和ObjectInputStream寫一個序列化和反序列化的方法,我如今D盤下沒有"serializable.txt":

public static void main(String[] args) throws Exception
{
    File file = new File("D:/serializable.txt");
    serializable(file);
    deserializable(file);
}
    
// 序列化對象方法
public static void serializable(File file) throws Exception
{
    OutputStream outputFile = new FileOutputStream(file);
    ObjectOutputStream oos = new ObjectOutputStream(outputFile);
    oos.writeObject(new Person("張三", 25));
    oos.close();
}
    
// 反序列化對象方法
public static void deserializable(File file) throws Exception
{
    InputStream inputFile = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(inputFile);
    Person p = (Person)ois.readObject();
    System.out.println(p);
}

如今運行一下,D盤下多了一個"serializable.txt",文件裏面的內容是:

看到亂碼,由於序列化以後自己就是按照必定的二進制格式組織的文件,這些二進制格式不能被文本文件所識別,因此亂碼也是正常的。

固然,控制檯上也是有輸出的:

姓名:null, 年齡:25, 性別:man

這證實了被transient修飾的成員變量不會被序列化。

相關文章
相關標籤/搜索