第18章 Stream(流)類

1. 流Stream的概念和分類

一個流(Stream)可以理解為一個數(shù)據(jù)的序列,是從某種介質(zhì)流向其他介質(zhì)的數(shù)據(jù)序列,比如從程序(內(nèi)存)保存數(shù)據(jù)至硬盤,從硬盤讀取數(shù)據(jù)至程序(內(nèi)存)。從程序向網(wǎng)絡(luò)發(fā)送消息,從網(wǎng)絡(luò)接收消息至程序等。

  • 按照流向分為輸入流和輸出流
    (1)輸入流:從其他介質(zhì)進入程序(內(nèi)存)
    (2)輸出流:從程序(內(nèi)存)離開進入其他介質(zhì)中
  • 按照處理單位分為字節(jié)流和字符流
    (1)字節(jié)流:處理單位為字節(jié)(byte),主要用于處理二進制數(shù)據(jù)。比如,計算機上的所有文件中的內(nèi)容基本都可以看做是二進制數(shù)據(jù)。
    (2)字符流:處理單位為字符(char),主要用于處理文本數(shù)據(jù)。比如計算機上的txt文檔中的內(nèi)容可以看做是文本數(shù)據(jù)。
  • 按照功能分為原始流和處理流
    (1)原始流:提供基礎(chǔ)功能的流
    (2)處理流:提供對基礎(chǔ)功能進行功能加強的流

按照流向和單位,流的父類如下

輸入 輸出
字節(jié) InputStream OutputStream
字符 Reader Writer

這些流的子類以上述的名字作為類名后綴。
比如:

  • FileInputStream: 關(guān)于文件操作的字節(jié)輸入流
  • FileInputOutputStream: 關(guān)于文件操作的字節(jié)輸出流
  • FileReader: 關(guān)于文件操作的字符輸入流
  • FileWriter: 關(guān)于文件操作的字符輸出流

原始流在使用后要進行關(guān)閉

2. 字節(jié)流(文件流為例)

以下是代碼的功能是復制c:/1.jpg至e:/1.jpg。復制的過程僅僅操作內(nèi)容復制,無需更改內(nèi)容,直接使用字節(jié)流完成。
可以理解為:
(1)將c:/1.jpg的數(shù)據(jù)輸入至內(nèi)存
(2)將輸入至內(nèi)存的數(shù)據(jù)輸出至e:/1.jpg

public class Test1 {
    public static void main(String[] args) {
        
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("c:/1.jpg");// 創(chuàng)建輸入流讀取源文件內(nèi)容
            fos = new FileOutputStream("e://1.jpg");// 創(chuàng)建輸出流寫出文件內(nèi)容
            byte[] b = new byte[2048]; //2KB區(qū)域,一次讀取源文件中的2KB數(shù)據(jù)
            // 從源文件中讀取2KB數(shù)據(jù)至b數(shù)組中
            int len = fis.read(b);   
            /*  len表示的是在源文件中讀取字節(jié)長度 
            len是-1表示讀取結(jié)束。最后一次讀取不一定讀了2KB數(shù)據(jù),
            需要用len確定讀了多少數(shù)據(jù),寫出時以免寫出冗余
            */
            while(len != -1) {      
                //將讀取到b數(shù)組中的數(shù)據(jù)寫出至目標文件
                //第二個參數(shù)表示偏移量,從數(shù)組的下標幾開始輸出
                //第三個參數(shù)表示輸出字節(jié)個數(shù)
                fos.write(b, 0, len);
                fos.flush(); //將數(shù)據(jù)真正刷出至目標文件中
                //再次讀入下一組數(shù)據(jù)至b中
                len = fis.read(b);
            }   
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關(guān)閉流
            try {
                if(fis != null) {
                    fis.close();
                }
                if(fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3. 字符流(文件流為例)

讀取c:/1.txt文檔中的內(nèi)容,將其中所有的字符'哈'替換為'*',并輸出至e:/1.txt中。其原理與文件復制一致,不過操作涉及對內(nèi)容的修改,需要在內(nèi)存中完成對字符的替換,所以使用字符流進行操作

public class Test2 {

    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        
        try {
            fr = new FileReader("D://1.txt");
            fw = new FileWriter("E://1.txt");
            char[] c = new char[1024]; //一次讀取1024個字符,字符流一次處理一個char,所以用char數(shù)組不使用byte數(shù)組
            int len = fr.read(c);  //將文本文件中的文字讀入的c數(shù)組中
            while(len != -1) {
                String temp = new String(c); //利用字符數(shù)組c構(gòu)造成一個字符串
                temp = temp.replaceAll("哈", "*"); //將字符串中的“哈”替換為“*”
                c = temp.toCharArray();//將替換后的字符串變回字符數(shù)組
                fw.write(c, 0, len);//將替換后的字符數(shù)組寫出至目標位置
                fw.flush();
                len = fr.read(c); //繼續(xù)讀取下一組字符
            }   
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fr != null) {
                    fr.close();
                }
                if(fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4. 處理流(緩沖流為例)

處理流本質(zhì)上不提供任何功能,它提供了對原始流功能的加強。
以字符緩沖輸入流為例,原始的字符輸入流只能以字符數(shù)組 (char[])讀取數(shù)據(jù),如果現(xiàn)在提出要讀取txt文件中整行數(shù)據(jù),原始的字符輸入流沒有相應的方法完成這個功能,但利用緩沖流提供的方法,可以容易的達到目的。
示例:以行(line)為單位,操作txt文檔的復制

public class Test1 {

    public static void main(String[] args) {
        FileReader fr = null; //原始的字符輸入流
        BufferedReader br = null; //緩沖字符輸入流
        
        FileWriter fw = null; //原始的字符輸出流
        BufferedWriter bw = null; //緩沖字符輸出流
        
        try {
            fr = new FileReader("d://1.txt");
            br = new BufferedReader(fr);    
            
            fw = new FileWriter("d://2.txt");
            bw = new BufferedWriter(fw);
                
            String line = br.readLine(); //讀取源文件中的一行文本
            while(line != null) { //如果不為null則認為讀取到了文本
                System.out.println(line); //在控制臺上顯示文本
                bw.write(line); //在目標文件中寫該行文本
                bw.newLine(); //目標文件新起一行
                bw.flush(); //刷入目標文件
                line = br.readLine(); //讀取源文件的下一行文本
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fr != null) {
                    fr.close();
                }
                if(fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5. 序列化

序列化是將對象以二進制數(shù)據(jù)的形式轉(zhuǎn)儲。比如將系統(tǒng)中的二進制數(shù)據(jù)暫時保存。

5.1 可被序列化的類

一個類如果可以被序列化,它需要實現(xiàn)java.io.Serializable接口,并生成一個常量值的序列碼,這個序列碼可以理解為是某個類的身份證號。

public class Student implements Serializable{   
    private static final long serialVersionUID = 4188262972381648549L;
    
    private int sno;
    private String sname;
    private Date birthday;
    
    public Student() {}
    
    public Student(int sno, String sname, Date birthday) {
        this.sno = sno;
        this.sname = sname;
        this.birthday = birthday;
    }
    public int getSno() {
        return sno;
    }
    public void setSno(int sno) {
        this.sno = sno;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

如果類中的某個屬性需要是其他類的對象并且也需要序列化,那么這個所謂的“其他類”也需要實現(xiàn)java.io.Serializable接口。比如上面這個類中的Date birthday屬性,java.util.Date類也實現(xiàn)了java.io.Serializable接口,系統(tǒng)中大部分的常用類都實現(xiàn)了java.io.Serializable接口,比如包裝類,集合類等。

5.2 序列化

將對象從內(nèi)存序列化至其他介質(zhì),本章示例將其序列化至c:/1.abc文件中

public class Test1 {

    /**
     * 序列化操作   將對象從內(nèi)存轉(zhuǎn)入其他介質(zhì)中
     * @param args
     */
    public static void main(String[] args) {
        Student s1 = new Student(101, "趙四", new Date());
        Student s2 = new Student(102, "哈哈", new Date());
        
        //序列化操作:   將s1對象保存在c:/1.abc文件中
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;  
        try {
            fos = new FileOutputStream("c:/1.abc");
            oos = new ObjectOutputStream(fos);//典型的處理流    操作oos相當于操作了fos
            
            oos.writeObject(s1); //將s1對象寫入到fos對應的文件中
            oos.writeObject(s2); //將s2對象寫入到fos對應的文件中
            oos.flush();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                if(fos != null) {
                    fos.close();//關(guān)閉原始流即可
                }   
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
5.3 反序列化

將對象從文件中讀取回內(nèi)存

public class Test2 {

    /**
     * 反序列化:將對象從其他介質(zhì)轉(zhuǎn)入到內(nèi)存中
     * @param args
     */
    public static void main(String[] args) {
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        
        try {
            fis = new FileInputStream("c:/1.abc");
            ois = new ObjectInputStream(fis);
            
            Student s1 = (Student)ois.readObject();
            Student s2 = (Student)ois.readObject();
            System.out.println(s1.getSno()+"\t"+s1.getSname()+"\t"+s1.getBirthday());
            System.out.println(s2.getSno()+"\t"+s2.getSname()+"\t"+s2.getBirthday());
            
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  finally {
            try {
                if(fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
5.4 transient關(guān)鍵字

該關(guān)鍵字用于修飾屬性,被這個關(guān)鍵字修飾的屬性不參與序列化和反序列化。比如
將Student類的birthday屬性加上transient關(guān)鍵字修飾

public class Student implements Serializable{   
    private static final long serialVersionUID = 4188262972381648549L;
    
    private int sno;
    private String sname;
    private transient Date birthday;
    
    public Student() {}
    
    public Student(int sno, String sname, Date birthday) {
        this.sno = sno;
        this.sname = sname;
        this.birthday = birthday;
    }
    public int getSno() {
        return sno;
    }
    public void setSno(int sno) {
        this.sno = sno;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

再次進行測驗會發(fā)現(xiàn)序列化后,反序列化回來的數(shù)據(jù)中birthday都是null。相當于birthday沒有參與序列化過程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容