轉換流,打印流,序列化

第11天 IO流
今日內容介紹
    轉換流
    序列化流
    打印流
    Properties
今日學習目標
    可以闡述編碼表的意義
    可以使用轉換流讀取指定編碼的文本文件
    可以使用轉換流寫入指定編碼的文本文件
    可以使用Properties的load方法加載文件中配置信息
    可以使用Properties的store方法保存配置信息到文件
    可以說出打印流的特色
    可以使用序列化流寫出對象到文件
    可以使用反序列化流讀取文件到程序中
    可以導入commons-io工具包
    可以配置classpath添加jar包到項目中
    可以使用FileUtils經常使用方法操做文件java

第1章    字符流
通過前面的學習,咱們基本掌握的文件的讀寫操做,在操做過程當中字節流能夠操做全部數據,但是當咱們操做的文件中有中文字符,而且須要對中文字符作出處理時怎麼辦呢?
1.1    字節流讀取字符的問題
經過如下程序讀取帶有中文件的文件。程序員

public class CharStreamDemo {
    public static void main(String[] args) throws IOException {
        //給文件中寫中文
        writeCNText();
        //讀取文件中的中文
        readCNText();
    }   
    //讀取中文
    public static void readCNText() throws IOException {
        FileInputStream fis = new FileInputStream("c:\\cn.txt");
        int ch = 0;
        while((ch = fis.read())!=-1){
            System.out.println(ch);
        }

    }
    //寫中文
    public static void writeCNText() throws IOException {
        FileOutputStream fos = new FileOutputStream("c:\\cn.txt");
        fos.write("a歡迎你".getBytes());
        fos.close();
    }
}

上面程序在讀取含有中文的文件時,咱們並無看到具體的中文,而是看到一些數字,這是什麼緣由呢?既然看不到中文,那麼咱們如何對其中的中文作處理呢?要解決這個問題,咱們必須研究下字符的編碼過程。api

1.2    字符編碼表
咱們知道計算機底層數據存儲的都是二進制數據(二進制瞭解嗎?最高位表示什麼?),而咱們生活中的各類各樣的數據,如何才能和計算機中存儲的二進制數據對應起來呢?
這時老美他們就把每個字符和一個整數對應起來,就造成了一張編碼表,老美他們的編碼表就是ASCII表。其中就是各類英文字符對應的編碼。
編碼表:其實就是生活中字符和計算機二進制的對應關係表。
一、ascii: 一個字節中的7位就能夠表示。對應的碼值都是正數。00000000-0xxxxxxx(其餘碼錶都要兼容它)  0000 0000
二、iso-8859-1:拉丁碼錶 latin,用了一個字節用的8位。1-xxxxxxx  負數。
三、GB2312:簡體中文碼錶。包含6000-7000中文和符號。用兩個字節表示。中文的兩個字節都是開頭爲1 ,即兩個字節都是負數。
         GBK:目前最經常使用的中文碼錶,2萬的中文和符號。用兩個字節表示,其中的一部分文字,第一個字節開頭是1,第二字節開頭是0
          GB18030:最新的中文碼錶,目前尚未正式使用。やめて
四、unicode:國際標準碼錶:不管是什麼文字,都用兩個字節存儲。
    Java中的char類型用的就是這個碼錶。char c = 'a';佔兩個字節。
    Java中的字符串是按照系統默認碼錶來解析的。簡體中文版 字符串默認的碼錶是GBK。
五、UTF-8:基於unicode,一個字節就能夠存儲的數據,不用兩個字節存儲,並且這個碼錶更加的標準化,在每個字節頭加入了編碼信息(後期到api中查找)。
能識別中文的碼錶:GBK、UTF-8;正由於識別中文碼錶不惟一,涉及到了編碼解碼問題。
對於咱們開發而言;常見的編碼 GBK  UTF-8  ISO-8859-1
1.GBK:中文2字節
2.UTF-8:中文3字節ide

文字--->(數字) :編碼: 就是看能看懂內容,轉換成看不懂的內容。a 0110 0001
(數字)--->文字  : 解碼: 就是把看不懂的內容,轉換成看懂的內容。0110 0001   --a工具

1.3    方便程序員的IO流
在IO開發過程當中,咱們傳輸最頻繁的數據爲字符,學習

我愛你---> 10100101010100110010101001--->妹子測試

而以字節方式傳輸字符須要每次將字符串轉換成字節再處理,並且也喪失了程序員對數據內容的判斷(由於程序員只認識字符,不認識字節)。因此,爲了讓程序員方便對字符進行操做,Java提供了專門以字符做爲操做單位的類——字符流,其底層仍然爲字節流。
顯然,字符流只能操做字符,沒法操做其餘數據,如聲音、視頻等。
在基礎班咱們已經學習過字符流了,這裏就進行一個簡單的回顧
1.4    字符流回顧
    IO流的分類
|- 字節流
   |- 字節輸入流 InputStream 抽象類
       |-  FileInputStream 操做文件的字節輸入流
|- 字節輸出流 OuputStream抽象類
   |- FileOutputStream 操做文件的字節輸出流
|- 字符流
   |- 字符輸入流 Reader抽象類
           |- FileReader 用來操做文件的字符輸入流(簡便的流)
   |- 字符輸出流 Writer抽象類
           |- FileWriter 用來操做文件的字符輸出流(簡便的流)
Java中流的命名 規範  功能+類型ui

第2章    轉換流this

在學習字符流(FileReader、FileWriter)的時候,其中說若是須要指定編碼和高效區大小時,能夠在字節流的基礎上,構造一個InputStreamReader或者OutputStreamWriter,這又是什麼意思呢?
2.1    字符編碼表(重複內容)
咱們知道計算機底層數據存儲的都是二進制數據,而咱們生活中的各類各樣的數據,如何才能和計算機中存儲的二進制數據對應起來呢?
這時老美他們就把每個字符和一個整數對應起來,就造成了一張編碼表,老美他們的編碼表就是ASCII表。其中就是各類英文字符對應的編碼。
編碼表:其實就是生活中字符和計算機二進制的對應關係表。
一、ascii: 一個字節中的7位就能夠表示。對應的字節都是正數。0-xxxxxxx
二、iso-8859-1:拉丁碼錶 latin,用了一個字節用的8位。1-xxxxxxx  負數。
三、GB2312:簡體中文碼錶。包含6000-7000中文和符號。用兩個字節表示。兩個字節都是開頭爲1 ,兩個字節都是負數。
         GBK:目前最經常使用的中文碼錶,2萬的中文和符號。用兩個字節表示,其中的一部分文字,第一個字節開頭是1,第二字節開頭是0
          GB18030:最新的中文碼錶,目前尚未正式使用。
四、unicode:國際標準碼錶:不管是什麼文字,都用兩個字節存儲。
    Java中的char類型用的就是這個碼錶。char c = 'a';佔兩個字節。
    Java中的字符串是按照系統默認碼錶來解析的。簡體中文版 字符串默認的碼錶是GBK。
五、UTF-8:基於unicode,一個字節就能夠存儲數據,不要用兩個字節存儲,並且這個碼錶更加的標準化,在每個字節頭加入了編碼信息(後期到api中查找)。
能識別中文的碼錶:GBK、UTF-8;正由於識別中文碼錶不惟一,涉及到了編碼解碼問題。
對於咱們開發而言;常見的編碼 GBK  UTF-8  ISO-8859-1
文字--->(數字) :編碼: 就是看能看懂內容,轉換成看不懂的內容。
(數字)--->文字  : 解碼: 就是把看不懂的內容,轉換成看懂的內容。編碼

2.2    OutputStreamWriter類(轉換流,快遞運輸的思想)
查閱OutputStreamWriter的API介紹,OutputStreamWriter 是字符流通向字節流的橋樑:可以使用指定的字符編碼表,將要寫入流中的字符編碼成字節。它的做用的就是,將字符串按照指定的編碼表轉成字節,在使用字節流將這些字節寫出去。


    代碼演示:

public static void writeCN() throws Exception {
        //建立與文件關聯的字節輸出流對象
        FileOutputStream fos = new FileOutputStream("c:\\cn8.txt");
        //建立能夠把字符轉成字節的轉換流對象,並指定編碼
        OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
        //調用轉換流,把文字寫出去,實際上是寫到轉換流的高效區中
        osw.write("你好");//寫入高效區。
        osw.close();
    }

OutputStreamWriter流對象,它到底如何把字符轉成字節輸出的呢?
其實在OutputStreamWriter流中維護本身的高效區,當咱們調用OutputStreamWriter對象的write方法時,會拿着字符到指定的碼錶中進行查詢,把查到的字符編碼值轉成字節數存放到OutputStreamWriter高效區中。而後再調用刷新功能,或者關閉流,或者高效區存滿後會把高效區中的字節數據使用字節流寫到指定的文件中。

2.3    InputStreamReader類
查閱InputStreamReader的API介紹,InputStreamReader 是字節流通向字符流的橋樑:它使用指定的字符編碼表讀取字節並將其解碼爲字符。它使用的字符集能夠由名稱指定或顯式給定,或者能夠接受平臺默認的字符集。


    代碼演示

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        //演示字節轉字符流的轉換流
        readCN();
    }
    public static void readCN() throws IOException{
        //建立讀取文件的字節流對象
        InputStream in = new FileInputStream("c:\\cn8.txt");
        //建立轉換流對象 
        //InputStreamReader isr = new InputStreamReader(in);這樣建立對象,會用本地默認碼錶讀取,將會發生錯誤解碼的錯誤
        InputStreamReader isr = new InputStreamReader(in,"utf-8");
        //使用轉換流去讀字節流中的字節
        int ch = 0;
        while((ch = isr.read())!=-1){
            System.out.println((char)ch);
        }
        //關閉流
        isr.close();
    }
}

注意:在讀取指定的編碼的文件時,必定要指定編碼格式,不然就會發生解碼錯誤,而發生亂碼現象。

2.4    轉換流和子類區別
發現有以下繼承關係:
Writer 字符輸出流
|- OutputStreamWriter   轉換流(字符流—>字節流)(屬於字符輸出流, 能夠指定字符編碼表,用來寫入數據到文件)
           |--FileWriter 操做文件中字符輸出流,採用默認的字符編碼表
fw.write(「你好」)
Reader 字符輸入流
|- InputStreamReader: 轉換流(字節流字符流)(屬於字符輸入流,能夠指定字符編碼表,用來從文件中讀數據)
|--FileReader操做文件中字符輸入流,採用默認的字符編碼表

父類和子類的功能有什麼區別呢?
OutputStreamWriter和InputStreamReader是字符和字節的橋樑:也能夠稱之爲字符轉換流。字符轉換流原理:字節流+編碼表。
FileWriter和FileReader:做爲子類,僅做爲操做字符文件的便捷類存在。當操做的字符文件,使用的是默認編碼表時能夠不用父類,而直接用子類就完成操做了,簡化了代碼。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默認GBK字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(「a.txt」));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(「a.txt」),」GBK」);

FileWriter fw = new FileWriter(「a.txt」)

這三句代碼的功能是同樣的,其中第三句最爲便捷。
注意:一旦要指定其餘編碼時,絕對不能用子類,必須使用字符轉換流。何時用子類呢?
條件:
一、操做的是文件。二、使用默認編碼。
總結:
字節--->解碼--->字符 : 看不懂的--->看的懂的。  須要讀。輸入流。 InputStreamReader
字符--->編碼--->字節 : 看的懂的--->看不懂的。  須要寫。輸出流。 OutputStreamWriter

第3章    序列化流與反序列化流
用於從流中讀取對象的操做流 ObjectInputStream   稱爲 反序列化流
用於向流中寫入對象的操做流 ObjectOutputStream   稱爲 序列化流
    特色:用於操做對象。能夠將對象寫入到文件中,也能夠從文件中讀取對象。

3.1    對象序列化流ObjectOutputStream
ObjectOutputStream 將 Java 對象的基本數據類型和圖形寫入 OutputStream。可使用 ObjectInputStream 讀取(重構)對象。經過在流中使用文件能夠實現對象的持久存儲。
注意:只能將支持 java.io.Serializable 接口的對象寫入流中


    代碼演示:

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*          * 將一個對象存儲到持久化(硬盤)的設備上。          */
        writeObj();//對象的序列化。
    }
    public static void writeObj() throws IOException {
        //1,明確存儲對象的文件。
        FileOutputStream fos = new FileOutputStream("tempfile\\obj.object");
        //2,給操做文件對象加入寫入對象功能。
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //3,調用了寫入對象的方法。
        oos.writeObject(new Person("wangcai",20));
        //關閉資源。
        oos.close();
    }
}

    Person類

public class Person implements Serializable {
    private String name;
    private int age;
    public Person() {
        super();
    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

3.2    對象反序列化流ObjectInputStream
ObjectInputStream 對之前使用 ObjectOutputStream 寫入的基本數據和對象進行反序列化。支持 java.io.Serializable接口的對象才能從流讀取。


    代碼演示

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        readObj();//對象的反序列化。
    }
    public static void readObj() throws IOException, ClassNotFoundException {

        //1,定義流對象關聯存儲了對象文件。
        FileInputStream fis = new FileInputStream("tempfile\\obj.object");

        //2,創建用於讀取對象的功能對象。
        ObjectInputStream ois = new ObjectInputStream(fis);

        Person obj = (Person)ois.readObject();

        System.out.println(obj.toString());

    }
}

3.3    序列化接口(主要是介紹兩個異常)
當一個對象要能被序列化,這個對象所屬的類必須實現Serializable接口。不然會發生異常NotSerializableException異常。
同時當反序列化對象時,若是對象所屬的class文件在序列化以後進行的修改,那麼進行反序列化也會發生異常InvalidClassException。發生這個異常的緣由以下:
    該類的序列版本號與從流中讀取的類描述符的版本號不匹配
    該類包含未知數據類型
    該類沒有可訪問的無參數構造方法
Serializable標記接口。該接口給須要序列化的類,提供了一個序列版本號。serialVersionUID. 該版本號的目的在於驗證序列化的對象和對應類是否版本匹配。

    代碼修改以下,修改後再次寫入對象,讀取對象測試

public class Person implements Serializable {
    //給類顯示聲明一個序列版本號。
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    public Person() {
        super();

    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

3.4    瞬態關鍵字transient
當一個類的對象須要被序列化時,某些屬性不須要被序列化,這時不須要序列化的屬性可使用關鍵字transient修飾。只要被transient修飾了,序列化時這個屬性就不會被序列化了。
同時靜態修飾也不會被序列化,由於序列化是把對象數據進行持久化存儲,而靜態的屬於類加載時的數據,不會被序列化。
    代碼修改以下,修改後再次寫入對象,讀取對象測試

public class Person implements Serializable {
    /*      * 給類顯示聲明一個序列版本號。      */
    private static final long serialVersionUID = 1L;
    private static String name;
    private transient/*瞬態*/ int age;

    public Person() {
        super();

    }

    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

第4章    打印流
4.1    打印流的概述
打印流添加輸出數據的功能,使它們可以方便地打印各類數據值表示形式.
打印流根據流的分類:
    字節打印流   PrintStream
    字符打印流   PrintWriter
    方法:
void print(String str): 輸出任意類型的數據,
void println(String str): 輸出任意類型的數據,自動寫入換行操做
    代碼演示:

 /*   * 需求:把指定的數據,寫入到printFile.txt文件中  *   * 分析:  *     1,建立流  *     2,寫數據  *     3,關閉流  */
public class PrintWriterDemo {
    public static void main(String[] args) throws IOException {
        //建立流
        //PrintWriter out = new PrintWriter(new FileWriter("printFile.txt"));
        PrintWriter out = new PrintWriter("printFile.txt");
        //2,寫數據
        for (int i=0; i<5; i++) {
            out.println("helloWorld");
        }
        //3,關閉流
        out.close();
    }
}

第5章    commons-IO

5.1    導入classpath
加入classpath的第三方jar包內的class文件才能在項目中使用
使用第三方類庫的步驟:
1.建立lib文件夾
2.將commons-io.jar拷貝到lib文件夾
3.右鍵點擊commons-io.jar,Build Path→Add to Build Path

5.2    FileUtils

提供文件操做(移動文件,讀取文件,檢查文件是否存在等等)的方法。
    經常使用方法:

readFileToString(File file):讀取文件內容,並返回一個String;
writeStringToFile(File file,String content):將內容content寫入到file中;
copyFile(File srcFile, File destFile): 文件複製
copyDirectoryToDirectory(File srcDir,File destDir);文件夾複製

    代碼演示:

/*  * 普通方式,完成文件的複製  */
public class CommonsIODemo01 {
    public static void main(String[] args) throws IOException {
        //method1("D:\\test.avi", "D:\\copy.avi");

        //經過Commons-IO完成了文件複製的功能
        FileUtils.copyFile(new File("D:\\test.avi"), new File("D:\\copy.avi"));
    }

    //文件的複製
    private static void method1(String src, String dest) throws IOException {
        //1,指定數據源 
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
        //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
        //3,讀
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len = in.read(buffer)) != -1) {
            //4,寫
            out.write(buffer, 0, len);
        }
        //5,關閉流
        in.close();
        out.close();
    }
}

/*  * 使用commons-io完成文件、文件夾的複製  */
public class CommonsIODemo02 {
    public static void main(String[] args) throws IOException {
        //經過Commons-IO完成了文件複製的功能
        FileUtils.copyFile(new File("D:\\test.avi"), new File("D:\\copy.avi"));

        //經過Commons-IO完成了文件夾複製的功能
        //D:\基礎班 複製到 C:\\abc文件夾下
        FileUtils.copyDirectoryToDirectory(new File("D:\\基礎班"), new File("C:\\abc"));
    }
}

     ​歡迎關注菜鳥永恆,點滴記錄,共同進步。

相關文章
相關標籤/搜索