Java基礎(11)— IO流

IO流

File類

  • java.io.File 類是文件和目錄路徑名的抽象表示,主要用於文件和目錄的建立、查找和刪除等操做

構造方法

  • public File(String pathname),經過將給定的路徑名字符串轉換爲抽象路徑名來建立新的 File實例。java

  • public File(String parent, String child),從父子路徑字符串建立新的 File實例。程序員

  • public File(File parent, String child),從父級File對象和子路徑字符串建立新的 File實例。windows

    public static void main(String[] args) {
        /*
        public File(String pathname)
         */
        String pathName = "E:\\hello.txt"; //路徑字符串
        File file1 = new File(pathName);
        /*
        public File(String parent, String child) 父子文件路徑字符串
         */
        String parent = "D:\\hello";
        String child = "world.txt";
        /*
        public File(File parent, String child) 父級File對象和子路徑字符串
         */
        File file3 = new File("D:\\hello");
        child = "java.txt";
    }
    /*
    tips: 
    	1. 一個File對象表明硬盤中實際存在的一個文件或者目錄
        2. 不管該路徑下是否存在文件或者目錄,都不影響File對象的建立
    */

經常使用方法

  • public String getAbsolutePath() ,返回此File的絕對路徑名字符串數組

  • public String getPath()將此File轉換爲路徑名字符串app

  • public String getName(),返回由此File表示的文件或目錄的名稱ide

  • public long length() ,返回由此File表示的文件的長度測試

    public static void main(String[] args) {
        File file1 = new File("D:/hello/world.txt");
        System.out.println("文件絕對路徑:" + file1.getAbsolutePath());
        System.out.println("文件構造路徑:" + file1.getPath());
        System.out.println("文件名稱:" + file1.getName());
        System.out.println("文件長度:" + file1.length());
        File file2 = new File("D:/hello");
        System.out.println("目錄絕對路徑:" + file2.getAbsolutePath());
        System.out.println("目錄構造路徑:" + file2.getPath());
        System.out.println("目錄名稱:" + file2.getName());
        System.out.println("目錄長度:" + file2.length());
    }
    /*
    文件絕對路徑:D:\hello\world.txt
    文件構造路徑:D:\hello\world.txt
    文件名稱:world.txt
    文件長度:288字節
    文件絕對路徑:D:\hello
    文件構造路徑:D:\hello
    文件名稱:hello
    文件長度:
    */
    // API中說明:length(),表示文件的長度。可是File對象表示目錄,則返回值未指定。

絕對路徑和相對路徑

  • 絕對路徑:從盤符開始的路徑,這是一個完整的路徑優化

  • 相對路徑:相對於項目目錄的路徑,這是一個便捷的路徑,開發中常用編碼

    public static void main(String[] args) { 
        // D盤下的hello.java文件 
        File f = new File("D:\\hello.java"); 
        System.out.println(f.getAbsolutePath()); 
        // 項目下的hello.java文件 
        File f2 = new File("hello.java"); 
        System.out.println(f2.getAbsolutePath()); 
    }
    /*
    D:\hello.java 
    D:\idea_project\hello.java
    */

判斷功能的方法

  • public boolean exists(),此File表示的文件或目錄是否實際存在idea

  • **public boolean isDirectory() **,此File是否爲目錄

  • public boolean isFile() ,此File是否爲文件

    public static void main(String[] args) { 
        File file1 = new File("d:\\hello\\world.java"); 
        File file2 = new File("d:\\java"); 
        // 判斷是否存在 
        System.out.println("d:\\hello\\world.java 是否存在:"+f.exists()); 
        System.out.println("d:\\java 是否存在:"+f2.exists()); 
        // 判斷是文件仍是目錄 
        System.out.println("d:\\java 文件?:"+f2.isFile());
        System.out.println("d:\\java 目錄?:"+f2.isDirectory()); 
    } 
    /*
    輸出結果: 
    	d:\hello\world.javaa 是否存在:true 
    	d:\java 是否存在:true 
    	d:\java 文件?:false 
    	d:\java 目錄?:true
    */

建立刪除功能的方法

  • public boolean createNewFile(),當且僅當具備該名稱的文件尚不存在時,建立一個新的空文件

  • public boolean delete(),刪除由此File表示的文件或目錄。

  • public boolean mkdir(),建立由此File表示的目錄

  • public boolean mkdirs(),建立由此File表示的目錄,包括任何須需但不存在的父目錄

    public static void main(String[] args) throws IOException { 
        // 文件的建立 
        File f = new File("aaa.txt"); 
        System.out.println("是否存在:"+f.exists()); // false 
        System.out.println("是否建立:"+f.createNewFile()); // true 
        System.out.println("是否存在:"+f.exists()); // true 
        // 目錄的建立 
        File f2= new File("newDir"); 
        System.out.println("是否存在:"+f2.exists());// false 
        System.out.println("是否建立:"+f2.mkdir()); // true 
        System.out.println("是否存在:"+f2.exists());// true 
        // 建立多級目錄 
        File f3= new File("newDira\\newDirb"); 
        System.out.println(f3.mkdir());// false 
        File f4= new File("newDira\\newDirb"); 
        System.out.println(f4.mkdirs());// true 
        // 文件的刪除 
        System.out.println(f.delete());// true 
        // 目錄的刪除 
        System.out.println(f2.delete());// true 
        System.out.println(f4.delete());// false
    } 
    /*
    	API中說明:delete方法,若是此File表示目錄,則目錄必須爲空才能刪除。
    */

目錄的遍歷

  • public String[] list(),返回一個String數組,表示該File目錄中的全部子文件或目錄

  • public File[] listFiles(),返回一個File數組,表示該File目錄中的全部的子文件或目錄

    public static void main(String[] args) { 
        File dir = new File("d:\\java_code"); 
        //獲取當前目錄下的文件以及文件夾的名稱。 
        String[] names = dir.list(); 
        for(String name : names){ 
            System.out.println(name);
        }
        //獲取當前目錄下的文件以及文件夾對象,只要拿到了文件對象,那麼就能夠獲取更多信息 
        File[] files = dir.listFiles(); 
        for (File file : files) { 
            System.out.println(file); 
        } 
    }
    /*
    	tips: 調用listFiles方法的File對象,表示的必須是實際存在的目錄,不然返回null,沒法進行遍歷。
    */

IO概述

什麼是IO流

  • 生活中,你確定經歷過這樣的場景。當你編輯一個文本文件,忘記了 ctrl+s ,可能文件就白白編輯了。當你電腦 上插入一個U盤,能夠把一個視頻,拷貝到你的電腦硬盤裏。那麼數據都是在哪些設備上的呢?鍵盤、內存、硬 盤、外接設備等等。

  • 咱們把這種數據的傳輸,能夠看作是一種數據的流動,按照流動的方向,之內存爲基準,分爲 輸入input 和 輸出 output ,即流向內存是輸入流,流出內存的輸出流。

  • Java中I/O操做主要是指使用 java.io 包下的內容,進行輸入、輸出操做。輸入也叫作讀取數據,輸出也叫作做寫出數據

  • IO的流向說明圖解

    截圖

IO的分類

  • 根據數據的流向分爲:輸入流和輸出流
    • 輸入流 :把數據從 其餘設備 上讀取到 內存 中的流。
    • 輸出流 :把數據從 內存 中寫出到 其餘設備 上的流。
  • 格局數據的類型分爲:字節流和字符流
    • 字節流 :以字節爲單位,讀寫數據的流。
    • 字符流 :以字符爲單位,讀寫數據的流。

IO流的頂級父類

輸入流 輸出流
字節流 字節輸入流
InputStream
字節輸出流
OutPutStream
字符流 字符輸入流
Reader
字符輸出流
Writer

字節流

一切皆爲字節

  • 一切文件數據(文本、圖片、視頻等)在存儲時,都是以二進制數字的形式保存,都一個一個的字節,那麼傳輸時一 樣如此。因此,字節流能夠傳輸任意文件數據。在操做流的時候,咱們要時刻明確,不管使用什麼樣的流對象,底 層傳輸的始終爲二進制數據。

字節輸出流

  • java.io.OutputStream,抽象類是表示字節輸出流的全部類的超類,將指定的字節信息寫出到目的地。它定義了字節輸出流的基本共性功能方法。

    • public void close()關閉此輸出流並釋放與此流相關聯的全部系統資源
    • public void flush()刷新此輸出流並強制任何緩衝的輸出字節被寫出
    • public void write(byte[] b),將 b.length字節從指定的字節數組寫入此輸出流
    • public void write(byte[] b, int off, int len)從指定的字節數組寫入 len字節,從偏移量 offff開始輸 出到此輸出流
    • public abstract void write(int b),將指定的字節輸出流
    /*
    tips:
    	當完成流操做時,必須調用close(),關閉並釋放資源
    */

文件輸出流

  • java.io.FileOutputStream 是 OutPutStream 的子類,名爲文件輸出流,用於將數據寫出到文件

  • 構造方法(輸出=寫 / 輸入=讀)

    • public FileOutputStream(File file),建立文件輸出流以寫出由指定的 File對象表示的文件
    • public FileOutputStream(String name), 建立文件輸出流以指定的名稱寫出文件
  • 當你建立一個流對象時,必須傳入一個文件路徑。該路徑下,若是沒有這個文件,會建立該文件。若是有這個文 件,會清空這個文件的數據

    public class FileOutputStreamConstructor throws IOException { 
        public static void main(String[] args) { 
            // 使用File對象建立流對象
            File file = new File("a.txt"); 
            FileOutputStream fos = new FileOutputStream(file); 
            // 使用文件名稱建立流對象 
            FileOutputStream fos = new FileOutputStream("b.txt"); 
        } 
    }
  • 寫出字節數據

    • 寫入字節 write(int byte),每次能夠寫出一個字節數據
    public class FOSWrite {
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 寫出數據 
            fos.write(97); // 寫出第1個字節 
            fos.write(98); // 寫出第2個字節 
            fos.write(99); // 寫出第3個字節 
            // 關閉資源 
            fos.close(); 
        } 
    }
    /*
    tips:
    	1. 雖然參數爲int類型四個字節,可是隻會保留一個字節的信息寫出
        2. 流操做完畢後,必須釋放系統資源,調用close(),千萬記得
    */
    • 寫出字節數組 write(byte[] byte),每次能夠寫出數組中的數據
    public class FOSWrite { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 字符串轉換爲字節數組 
            byte[] b = "完美世界".getBytes(); 
            // 寫出字節數組數據 
            fos.write(b); 
            // 關閉資源 
            fos.close(); 
        } 
    }
    /*
    輸出結果:
    	完美世界
    */
    • 寫出指定長度字節數組 write(byte[] byte, int off, int len),每次寫出從off索引開始,len個字節
    public class FOSWrite { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 字符串轉換爲字節數組 
            byte[] b = "abcde".getBytes(); 
            // 寫出從索引2開始,2個字節。索引2是c,兩個字節,也就是cd。 
            fos.write(b,2,2);
            // 關閉資源
            fos.close();
        } 
    }
    /*
    輸出結果:
    	cd
    */
  • 數組追加續寫

    • 通過以上的演示,每次程序運行,建立輸出流對象,都會清空目標文件中的數據。如何保留目標文件中數據,還能繼續添加新數據呢?

    • public FileOutputStream(File file, boolean append),建立文件輸出流以指定的File對象續寫或重寫文件的數據

    • public FileOutputStream(String name, boolean append),建立文件輸出流以指定的名稱續寫或重寫文件的數據

    • 這兩個構造方法,參數中都須要傳入一個boolean類型的值, true 表示追加數據, false 表示清空原有數據。 這樣建立的輸出流對象,就能夠指定是否追加續寫了

    public class FOSWrite { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象
            FileOutputStream fos = new FileOutputStream("fos.txt",true); 
            // 字符串轉換爲字節數組
            byte[] b = "abcde".getBytes(); 
            // 寫出從索引2開始,2個字節。索引2是c,兩個字節,也就是cd。 fos.write(b);
            // 關閉資源
            fos.close(); 
        } 
    }
    /*
    	文件操做前:cd 
    	文件操做後:cdabcde
    */
  • 寫出換行

    • windows系統裏,換行符號是\r\n
    public class FOSWrite {
        public static void main(String[] args) throws IOException {
            // 使用文件名稱建立流對象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 定義字節數組 
            byte[] words = {97,98,99,100,101};
            // 遍歷數組 
            for (int i = 0; i < words.length; i++) { 
                // 寫出一個字節 
                fos.write(words[i]);
                // 寫出一個換行, 換行符號轉成數組寫出 
                fos.write("\r\n".getBytes()); 
            }
            // 關閉資源 
            fos.close();
        }
    }
    /*
    輸出結果:
    	a
    	b
    	c
    	d
    	e
    */ 
    /*
    回車符 \r 和換行符 \n :
    	回車符:回到一行的開頭(return)。 
    	換行符:下一行(newline)。 
    系統中的換行: 
    	Windows系統裏,每行結尾是 回車+換行 ,即 \r\n  
    	Unix系統裏,每行結尾只有 換行 ,即 \n  
    	Mac系統裏每行結尾是 回車 ,即 \r [從 Mac OS X開始與Linux統一]
    */

字節輸入流

  • java.io.InputStream抽象類是表示字節輸入流的全部類的超類,能夠讀取字節信息到內存中。它定義了字節輸入流的基本共性功能方法

    • public void close(),關閉此輸入流並釋放與此流相關聯的任何系統資源
    • public abstract int read(),從輸入流讀取數據的下一個字節
    • public int read(byte[] b),從輸入流中讀取一些字節數,並將它們存儲到字節數組 b中
  • tips:流操做完成時,必須調用close()方法關閉,釋放系統資源

文件輸入流

  • java.io.FileInputputStream 是 InputStream的子類,名爲文件輸出流,從文件中讀取字節

  • 構造方法

    • FileInputStream(File file),經過打開與實際文件的鏈接來建立一個FileInputStream,該文件由文件系統中的File對象file命名。
    • FileInputStream(String name),經過打開與實際文件的鏈接來建立一個FileInputStream,該文件由文件系統中的路徑名 name命名
  • tips:當建立一個流對象時,必須傳入一個文件路徑。若是路徑下沒有該文件會拋出FileNotFoundException異常

    public class FileInputStreamConstructor throws IOException{ 
        public static void main(String[] args) {
            // 使用File對象建立流對象 
            File file = new File("a.txt"); 
            FileInputStream fos = new FileInputStream(file); 
            // 使用文件名稱建立流對象 
            FileInputStream fos = new FileInputStream("b.txt"); 
        } 
    }
  • 讀取字節數據

    • read(),每次能夠讀取一個字節的數據,提高爲int類型,讀取到文件末尾返回-1
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
            // 使用文件名稱建立流對象
            FileInputStream fis = new FileInputStream("read.txt"); 
            // 讀取數據,返回一個字節 
            int read = fis.read(); 
            System.out.println((char) read); 
            read = fis.read(); 
            System.out.println((char) read); 
            read = fis.read(); 
            System.out.println((char) read);
            read = fis.read(); 
            System.out.println((char) read);
            read = fis.read(); 
            System.out.println((char) read); 
            // 讀取到末尾,返回‐1 
            read = fis.read(); 
            System.out.println( read);
            // 關閉資源 
            fis.close(); 
        } 
    }
    /*
    輸出結果:
    	a
    	b
    	c
    	d
    	e
    	-1
    */
    • 循環改進讀取方式
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
            // 使用文件名稱建立流對象
            FileInputStream fis = new FileInputStream("read.txt");
            // 定義變量,保存數據 
            int b ;
            // 循環讀取 
            while ((b = fis.read()) != -1) {
                System.out.println((char)b);//讀取字節會自動提高爲int,因此須要轉char
            }
            // 關閉資源 
            fis.close(); 
        } 
    }
    /*
    輸出結果:
    	a
    	b
    	c
    	d
    	e
    */
  • 使用字節數組讀取

    • read(byte[] b) ,每次讀取b的長度個字節到數組中,返回讀取到的有效字節個數,讀取到末尾時,返回 -1
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
          	// 使用文件名稱建立流對象. 
            FileInputStream fis = new FileInputStream("read.txt");// 文件中爲abcde
            // 定義變量,做爲有效個數 
            int len;
            // 定義字節數組,做爲裝字節數據的容器 
            byte[] b = new byte[2]; 
            // 循環讀取 
            while (( len= fis.read(b))!= -1) { 
                 // 每次讀取後,把數組變成字符串打印 
                 System.out.println(new String(b)); 
            }
            // 關閉資源 
            fis.close(); 
        }
    }
    /*
    輸出結果:
    	ab
    	cd
    	ed
    */
    • tips:錯誤數據ed是因爲最後一次讀取時,只讀取一個字節e,數組裏上次讀取的數據沒有被徹底替換,因此要經過len,獲取有效的字節
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
            // 使用文件名稱建立流對象
            FileInputStream fis = new FileInputStream("read.txt"); // 文件中爲abcde 
            // 定義變量,做爲有效個數
            int len;
            // 定義字節數組,做爲裝字節數據的容器 
            byte[] b = new byte[2];
            // 循環讀取 
            while (( len= fis.read(b))!=‐1) { 
                // 每次讀取後,把數組的有效字節部分,變成字符串打印 
                System.out.println(new String(b,0,len));// len 每次讀取的有效字節個數 
            }
            // 關閉資源 
            fis.close(); 
        }
    }
    /*
    輸出結果:
    	ab
    	cd
    	e
    */
  • 字節流練習(圖片複製)

    • 複製原理圖解

    截圖

    • 案例實現
    public class Copy {
        public static void main(String[] args) throws IOException {
            // 1.建立流對象
            // 1.1 指定數據源
            FileInputStream fis = new FileInputStream("D:\\test.jpg");
            // 1.2 指定目的地
            FileOutputStream fos = new FileOutputStream("test_copy.jpg");
            // 2.讀寫數據
            // 2.1 定義數組
            byte[] b = new byte[1024];
            // 2.2 定義長度
            int len;
            // 2.3 循環讀取
            while ((len = fis.read(b)) != -1) {
                // 2.4 寫出數據
                fos.write(b, 0 , len);
            }
            // 3.關閉資源
            fos.close(); fis.close();
        }
    }
    • tips:流關閉原則,先開後關,後開先關。

字符流

  • 當使用字節流讀取文本文件時,可能會有一個小問題。就是遇到中文字符時,可能不會顯示完整的字符,那是由於一箇中文字符可能佔用多個字節存儲。因此Java提供一些字符流類,以字符爲單位讀寫數據,專門用於處理文本文件。

字符輸入流

  • java.io.Reader 抽象類是表示用於讀取字符流的全部類的超類,能夠讀取字符信息到內存中。它定義了字符輸入 流的基本共性功能方法。

    • public void close(),關閉此流並釋放與此流相關聯的任何系統資源
    • public int read(),從輸入流讀取一個字符
    • public int read(char[] cbuf),從輸入流中讀取一些字符,並將它們存儲到字符數組 cbuf中
  • FileReader 類

    • java.io.FileReader 類是讀取字符文件的便利類。構造時使用系統默認的字符編碼和默認字節緩衝區

    ※ tips:

    • 字符編碼:字節與字符的對應規則。Windows系統的中文編碼默認是GBK編碼表。idea中UTF-8

    • 字節緩衝區:一個字節數組,用來臨時存儲字節數據

  • 構造方法

    • FileReader(File file),建立一個新的 FileReader ,給定要讀取的File對象。
    • FileReader(String fileName),建立一個新的 FileReader ,給定要讀取的文件的名稱‘
    • 當你建立一個流對象時,必須傳入一個文件路徑。相似於FileInputStream
    public class FileReaderConstructor throws IOException{ 
        public static void main(String[] args) { 
            // 使用File對象建立流對象 
            File file = new File("a.txt"); 
            FileReader fr = new FileReader(file); 
            // 使用文件名稱建立流對象 
            FileReader fr = new FileReader("b.txt"); 
        } 
    }
  • 讀取字符數據

    • read 方法,每次能夠讀取一個字符的數據,提高爲int類型,讀取到文件末尾返回 -1 ,循環讀取
    public class FRRead { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象
            FileReader fr = new FileReader("read.txt"); 
            // 定義變量,保存數據 
            int b;
            // 循環讀取 
            while ((b = fr.read())!=‐1) {
                System.out.println((char)b); 
            }
            // 關閉資源 
            fr.close(); 
        }
    }
    /*
    輸出結果:
    	葉
    	遮
    	天
    */

    ※ tips:雖然讀取了一個字符,可是會自動提高爲int類型

    • read(char[] cbuf) ,每次讀取b的長度個字符到數組中,返回讀取到的有效字符個數,讀取到末尾時返回 -1
    public class FRRead { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象 
            FileReader fr = new FileReader("read.txt"); 
            // 定義變量,保存有效字符個數 
            int len;
            // 定義字符數組,做爲裝字符數據的容器 
            char[] cbuf = new char[2]; 
            // 循環讀取 
            while ((len = fr.read(cbuf))!=‐1) { 
                System.out.println(new String(cbuf)); 
            }
            // 關閉資源 
            fr.close(); 
        } 
    }
    /*
    輸出結果:
    	葉遮
    	天遮
    */
    • 獲取有效字符改進
    public class FISRead { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名稱建立流對象 
            FileReader fr = new FileReader("read.txt"); 
            // 定義變量,保存有效字符個數 
            int len;
            // 定義字符數組,做爲裝字符數據的容器 
            char[] cbuf = new char[2]; 
            // 循環讀取 
            while ((len = fr.read(cbuf))!=‐1) { 
                System.out.println(new String(cbuf,0,len)); 
            }
            // 關閉資源 
            fr.close(); 
        } 
    }
    /*
    輸出結果:
    	葉遮
        天
    */

字符輸出流

  • java.io.Writer抽象類是表示用於寫出字符流的全部類的超類,將指定的字符信息寫出到目的地。它定義了字節 輸出流的基本共性功能方法。

    • void write(int c) 寫入單個字符
    • void write(char[] cbuf) 寫入字符數組
    • abstract void write(char[] cbuf, int off, int len) 寫入字符數組的某一部分,offff數組的開始索引,len 寫的字符個數。
    • void write(String str) 寫入字符串。
    • void write(String str, int off, int len) 寫入字符串的某一部分,offff字符串的開始索引,len寫的字符個 數。
    • void flush() 刷新該流的緩衝。
    • void close() 關閉此流,但要先刷新它。
  • FileWriter類

    • java.io.FileWriter 類是寫出字符到文件的便利類。構造時使用系統默認的字符編碼和默認字節緩衝區
  • 構造方法

    • FileWriter(File file) : 建立一個新的 FileWriter,給定要讀取的File對象
    • FileWriter(String fileName) : 建立一個新的 FileWriter,給定要讀取的文件的名稱
    public class FileWriterConstructor {     
        public static void main(String[] args) throws IOException { 
            // 使用File對象建立流對象               
            File file = new File("a.txt");        
            FileWriter fw = new FileWriter(file);                
            // 使用文件名稱建立流對象         
            FileWriter fw = new FileWriter("b.txt");    
        } 
    }
  • 基本輸出數據

    • write(int byte),每次能夠寫出一個字符數據,代碼使用演示:
    public class FWWrite {     
        public static void main(String[] args) throws IOException {         
            // 使用文件名稱建立流對象         
            FileWriter fw = new FileWriter("fw.txt");             
            // 寫出數據           
            fw.write(97); // 寫出第1個字符           
            fw.write('b'); // 寫出第2個字符           
            fw.write('C'); // 寫出第3個字符          
            fw.write(30000); // 寫出第4個字符,中文編碼表中30000對應一個漢字
            /*            
            【注意】關閉資源時,與FileOutputStream不一樣。         
             若是不關閉,數據只是保存到緩衝區,並未保存到文件。            
            */         
            // fw.close();     
        }
    }
    /*
    輸出結果:
    	abc田
    */

    ※ tips:

    • 雖然參數爲int類型四個字節,可是隻會保留一個字符的信息寫出
    • 未調用close方法,數據只是保存到了緩衝區,並未寫出到文件中
  • 關閉和刷新

    由於內置緩衝區的緣由,若是不關閉流輸出,沒法寫出字符到文件中。可是關閉的流對象,是沒法繼續寫出數據的。若是咱們既想寫出數據,又想繼續使用流,就須要flush方法了

    • flush,刷新緩衝區,流對象能夠繼續使用
    • close,先刷新緩衝區,而後通知系統釋放資源。流對象不能夠再被使用了
    public class FWWrite {     
        public static void main(String[] args) throws IOException {      
            // 使用文件名稱建立流對象        
            FileWriter fw = new FileWriter("fw.txt");        
            // 寫出數據,經過flush         
            fw.write('刷'); // 寫出第1個字符        
            fw.flush();         
            fw.write('新'); // 繼續寫出第2個字符,寫出成功         
            fw.flush();              
            // 寫出數據,經過close           
            fw.write('關'); // 寫出第1個字符        
            fw.close();         
            fw.write('閉'); // 繼續寫出第2個字符,【報錯】java.io.IOException: Stream closed         f
            w.close();    
        } 
    }

    ※ tips:即使是flush()寫出了數據,操做的最後仍是要調用close方法,釋放系統資源

  • 寫出其餘數據

    • 寫出字符組,write(char[] cbuf) 和 write(char[] cbuf, int off, int len),每次能夠寫出字符數組中的數據,用法相似FileOutStream
    public class FWWrite {     
        public static void main(String[] args) throws IOException {        
            // 使用文件名稱建立流對象
            FileWriter fw = new FileWriter("fw.txt");             
            // 字符串轉換爲字節數組           
            char[] chars = "完美世界".toCharArray();                  
            // 寫出字符數組          
            fw.write(chars); // 完美世界   
            // 寫出從索引2開始,2個字節。索引2是'美',兩個字節,也就是'美世'。                  
            fw.write(b,2,2); // 美世     
            // 關閉資源           
            fos.close();    
        }
    }
    • 寫出字符串,write(String str) 和 write(String str, int off, int len),每次能夠寫出字符串中的數據,更爲方便
    public class FWWrite {    
        public static void main(String[] args) throws IOException {        
            // 使用文件名稱建立流對象        
            FileWriter fw = new FileWriter("fw.txt");        
            // 字符串           
            String msg = "完美世界";                  
            // 寫出字符數組           
            fw.write(msg); //完美世界       
            // 寫出從索引2開始,2個字節。索引2是'美',兩個字節,也就是'美世'。              
            fw.write(msg,2,2); // 美世            
            // 關閉資源         
            fos.close();  
        }
    }
    • 續寫和換行,操做相似於FileOutStream
    public class FWWrite {    
        public static void main(String[] args) throws IOException {    
            // 使用文件名稱建立流對象,能夠續寫數據         
            FileWriter fw = new FileWriter("fw.txt",true);        
            // 寫出字符串           
            fw.write("遮天");        
            // 寫出換行          
            fw.write("\r\n");         
            // 寫出字符串      
            fw.write("完美世界");          
            // 關閉資源           
            fw.close();  
        }
    }
    /*
    輸出結果:
    	遮天
    	完美世界
    */

    ※ tips:字符流,只能操做文本文件,不能操做圖片,視頻等非文本文件。

IO異常處理

  • JDK 7 以前的處理

    public class HandleException1 {   
        public static void main(String[] args) {      
            // 聲明變量            
            FileWriter fw = null; 
            try {            
                //建立流對象       
                fw = new FileWriter("fw.txt");         
                // 寫出數據             
                fw.write("完美世界"); //完美世界       
            } catch (IOException e) {    
                e.printStackTrace();       
            } finally {            
                try {                
                    if (fw != null) {        
                        fw.close();         
                    }            
                } catch (IOException e) {   
                    e.printStackTrace();      
                }        
            }    
        }
    }
  • JDK 7 的處理

    /*還可使用JDK7優化後的 try-with-resource 語句,該語句確保了每一個資源在語句結束時關閉。所謂的資源 (resource)是指在程序完成後,必須關閉的對象*/
    //格式
    try (建立流對象語句,若是多個,使用';'隔開) { 
        // 讀寫數據      
    } catch (IOException e) {
        e.printStackTrace();    
    }
    public class HandleException2 {    
        public static void main(String[] args) {   
            // 建立流對象            
            try ( FileWriter fw = new FileWriter("fw.txt"); ) {   
                // 寫出數據             
                fw.write("黑馬程序員"); //黑馬程序員        
            } catch (IOException e) {  
                e.printStackTrace();     
            }   
        } 
    }
  • JDK 9的改進

    /*JDK9中 try-with-resource 的改進,對於引入對象的方式,支持的更加簡潔。被引入的對象,一樣能夠自動關閉, 無需手動close,咱們來了解一下格式。*/
    /*改進前*/
    // 被final修飾的對象 
    final Resource resource1 = new Resource("resource1"); 
    // 普通對象 
    Resource resource2 = new Resource("resource2"); 
    // 引入方式:建立新的變量保存 
    try (Resource r1 = resource1;
         Resource r2 = resource2) { 
        // 使用對象 
    }
    /*改進後*/
    // 被final修飾的對象
    final Resource resource1 = new Resource("resource1"); 
    // 普通對象 
    Resource resource2 = new Resource("resource2");   
    // 引入方式:直接引入 
    try (resource1; resource2) {      
        // 使用對象
    }
    public class TryDemo {     
        public static void main(String[] args) throws IOException {    
            // 建立流對象         
            final  FileReader fr  = new FileReader("in.txt");  
            FileWriter fw = new FileWriter("out.txt");
            // 引入到try中 
            try (fr; fw) {      
                // 定義變量         
                int b;            
                // 讀取數據           
                while ((b = fr.read())!=‐1) {    
                    // 寫出數據                 
                    fw.write(b);            
                }           
            } catch (IOException e) {  
                e.printStackTrace();   
            }    
        }
    }

屬性集

  • 概述
    • java.util.Properties 繼承於 Hashtable ,來表示一個持久的屬性集。它使用鍵值結構存儲數據,每一個鍵及其 對應值都是一個字符串。該類也被許多Java類使用,好比獲取系統屬性時, System.getProperties 方法就是返回 一個 Properties 對象。

Properties類

  • 構造方法

    • public Properties() ,建立一個空的屬性列表
  • 基本的存儲方法

    • public Object setProperty(String key, String value),保存一對屬性。
    • public String getProperty(String key),使用此屬性列表中指定的鍵搜索屬性值。
    • public Set stringPropertyNames(),全部鍵的名稱的集合。
    public class ProDemo {     
        public static void main(String[] args) throws FileNotFoundException {    
            // 建立屬性集對象        
            Properties properties = new Properties();     
            // 添加鍵值對元素        
            properties.setProperty("filename", "a.txt");       
            properties.setProperty("length", "209385038");     
            properties.setProperty("location", "D:\\a.txt");         
            // 打印屬性集對象        
            System.out.println(properties);     
            // 經過鍵,獲取屬性值         
            System.out.println(properties.getProperty("filename"));        
            System.out.println(properties.getProperty("length"));      
            System.out.println(properties.getProperty("location"));     
            // 遍歷屬性集,獲取全部鍵的集合
             Set<String> strings = properties.stringPropertyNames();   
            // 打印鍵值對         
            for (String key : strings ) {  
                System.out.println(key+" ‐‐ "+properties.getProperty(key));       
            }     
        } 
    } 
    /*
    輸出結果: 
    {filename=a.txt, length=209385038, location=D:\a.txt} 
    a.txt 
    209385038 
    D:\a.txt 
    filename ‐‐ a.txt 
    length ‐‐ 209385038 
    location ‐‐ D:\a.txt
    */
  • 與流相關的方法

    • public void load(InputStream inStream),從字節輸入流中讀取鍵值對。參數中使用了字節輸入流,經過流對象,能夠關聯到某文件上,這樣就可以加載文本中的數據了
    filename=a.txt 
    length=209385038
    location=D:\a.txt
    public class ProDemo2 {    
        public static void main(String[] args) throws FileNotFoundException {   
            // 建立屬性集對象        
            Properties pro = new Properties();   
            // 加載文本中信息到屬性集        
            pro.load(new FileInputStream("read.txt"));  
            // 遍歷集合並打印         
            Set<String> strings = pro.stringPropertyNames();    
            for (String key : strings ) {     
                System.out.println(key+" ‐‐ "+pro.getProperty(key));         
            }      
        }
    } 
    /*
    輸出結果:
    	filename ‐‐ a.txt 
    	length ‐‐ 209385038 
    	location ‐‐ D:\a.txt
    */

    ※ tips: 文本中的數據,必須是鍵值對形式,可使用空格,等號,冒號等符號分隔。

緩衝流

  • 緩衝流,也叫高效流,是對4個基本的 FileXxx 流的加強,因此也是4個流,按照數據類型分類

    • 字節緩衝流: BufferedInputStream , BufferedOutputStream
    • 字符緩衝流: BufferedReader , BufferedWriter
  • 緩衝流基本原理,實在建立流對象時會建立一個內置的默認大小的緩衝區數組,經過緩衝區讀寫,減小系統IO次數,從而提升讀寫的效率

字節緩衝流

構造方法

  • public BufferedInputStream(InputStream in),建立一個 新的緩衝輸入流

  • public BufferedOutputStream(OutputStream out),建立一個新的緩衝輸出流

    // 建立字節緩衝輸入流 
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
    // 建立字節緩衝輸出流 
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

效率測試

  • 查詢API,緩衝流讀寫方法與基本的流是一致的,咱們經過複製大文件(375MB),測試它的效率

    • 基本流
    public class BufferedDemo {    
        public static void main(String[] args) throws FileNotFoundException {     
            // 記錄開始時間        
            long start = System.currentTimeMillis();    
            // 建立流對象                 
            try (          
                FileInputStream fis = new FileInputStream("jdk9.exe");
                FileOutputStream fos = new FileOutputStream("copy.exe")
            ){  
                // 讀寫數據                 
                int b;            
                while ((b = fis.read()) != ‐1) {    
                    fos.write(b);            
                }         
            } catch (IOException e) {  
                e.printStackTrace();       
            } // 記錄結束時間               
            long end = System.currentTimeMillis();        
            System.out.println("普通流複製時間:"+(end ‐ start)+" 毫秒");  
        } 
    }
    //十幾分鍾過去了....
    • 緩衝流
    public class BufferedDemo {    
        public static void main(String[] args) throws FileNotFoundException {   
            // 記錄開始時間        
            long start = System.currentTimeMillis();    
            // 建立流對象                  
            try (          
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"))
            ){
                // 讀寫數據             
                int b;             
                while ((b = bis.read()) != ‐1) {                 
                    bos.write(b);             
                }         
            } catch (IOException e) {  
                e.printStackTrace();         
            } 
            // 記錄結束時間                 
            long end = System.currentTimeMillis();   
            System.out.println("緩衝流複製時間:"+(end ‐ start)+" 毫秒");    
        } 
    }
    //緩衝流複製時間:8016 毫秒
    • 如何更快呢?固然是使用數組的方式
    public class BufferedDemo {   
        public static void main(String[] args) throws FileNotFoundException {  
            // 記錄開始時間            
            long start = System.currentTimeMillis(); 
            // 建立流對象        
            try ( BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"))
                ){            
                // 讀寫數據        
                int len;           
                byte[] bytes = new byte[8*1024];    
                while ((len = bis.read(bytes)) != ‐1) {  
                    bos.write(bytes, 0 , len);       
                }         
            } catch (IOException e) {  
                e.printStackTrace();      
            } 
            // 記錄結束時間                 
            long end = System.currentTimeMillis();         
            System.out.println("緩衝流使用數組複製時間:"+(end ‐ start)+" 毫秒");    
        } 
    }
    //緩衝流使用數組複製時間 666毫秒

字符緩衝流

構造方法

  • public BufferedReader(Reader in),建立一個 新的緩衝輸入流。

  • public BufferedWriter(Writer out),建立一個新的緩衝輸出流

    // 建立字符緩衝輸入流 
    BufferedReader br = new BufferedReader(new FileReader("br.txt")); 
    // 建立字符緩衝輸出流 
    BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

特有方法

  • 字符緩衝流的基本方法與普通字符流調用方式一致,再也不闡述,咱們來看它們具有的特有方法

    • BufferedReader: public String readLine(),讀一行文字。
    • BufferedWriter: public void newLine(),寫一行行分隔符,由系統屬性定義符號
  • readLine

    public class BufferedReaderDemo {     
        public static void main(String[] args) throws IOException {        
            // 建立流對象            
            BufferedReader br = new BufferedReader(new FileReader("in.txt")); 
            // 定義字符串,保存讀取的一行文字                  
            String line  = null;        
            // 循環讀取,讀取到最後返回null      
            while ((line = br.readLine())!=null) {        
                System.out.print(line);             
                System.out.println("‐‐‐‐‐‐");        
            } // 釋放資源                 
            br.close();     
        } 
    }
  • newLine

    public class BufferedWriterDemo throws IOException {    
        public static void main(String[] args) throws IOException  {     
            // 建立流對象   
            BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));    
            // 寫出數據           
            bw.write("完");     
            // 寫出換行           
            bw.newLine();         
            bw.write("美");        
            bw.newLine();       
            bw.write("世界");      
            bw.newLine(); 
            // 釋放資源        
            bw.close();    
        }
    }
    /*
    輸出結果:
    	完
    	美
    	世界
    */
  • 練習,文本排序,請假下列信息恢復順序

    3..侍中、侍郎郭攸之、費禕、董允等,此皆良實,志慮忠純,是以先帝簡拔以遺陛下。愚覺得宮中之事,事無大小,悉 以諮之,而後施行,必得裨補闕漏,有所廣益。
    8.願陛下託臣以討賊興復之效,不效,則治臣之罪,以告先帝之靈。若無興德之言,則責攸之、禕、允等之慢,以彰其 咎;陛下亦宜自謀,以諮諏善道,察納雅言,深追先帝遺詔,臣不勝受恩感激。 
    4.將軍向寵,性行淑均,曉暢軍事,試用之於昔日,先帝稱之曰能,是以衆議舉寵爲督。愚覺得營中之事,悉以諮之, 必能使行陣和氣,優劣得所。
    2.宮中府中,俱爲一體,陟罰臧否,不宜異同。如有做奸犯科及爲忠善者,宜付有司論其刑賞,以昭陛下平明之理,不 宜偏私,使內外異法也。 
    1.先帝創業未半而中道崩殂,今天下三分,益州疲弊,此誠危急存亡之秋也。然侍衛之臣不懈於內,忠志之士忘身於外 者,蓋追先帝之殊遇,欲報之於陛下也。誠宜開張聖聽,以光先帝遺德,恢弘志士之氣,不宜妄自菲薄,引喻失義,以 塞忠諫之路也。 
    9.今當遠離,臨表涕零,不知所言。 
    6.臣本布衣,躬耕於南陽,苟全性命於亂世,不求聞達於諸侯。先帝不以臣卑鄙,猥自枉屈,三顧臣於草廬之中,諮 以當世之事,由是感激,遂許先帝以驅馳。後值傾覆,受任於敗軍之際,奉命於危難之間,爾來二十有一年矣。
    7.先帝知臣謹慎,故臨崩寄臣以大事也。受命以來,夙夜憂嘆,恐付託不效,以傷先帝之明,故五月渡瀘,深刻不毛。 今南方已定,兵甲已足,當獎率三軍,北定中原,庶竭駑鈍,攘除奸兇,興復漢室,還於舊都。此臣因此報先帝而忠陛 下之職分也。至於斟酌損益,進盡忠言,則攸之、禕、允之任也。
    5.親賢臣,遠小人,此先漢因此興隆也;親小人,遠賢臣,此後漢因此傾頹也。先帝在時,每與臣論此事,何嘗不嘆息 痛恨於桓、靈也。侍中、尚書、長史、參軍,此悉貞良死節之臣,願陛下親之信之,則漢室之隆,可計日而待也。
    
    案例分析 
    1. 逐行讀取文本信息。 
    2. 解析文本信息到集合中。 
    3. 遍歷集合,按順序,寫出文本信息
    public class BufferedTest {    
        public static void main(String[] args) throws IOException {      
            // 建立map集合,保存文本數據,鍵爲序號,值爲文字    
            HashMap<String, String> lineMap = new HashMap<>();  
            // 建立流對象         
            BufferedReader br = new BufferedReader(new FileReader("in.txt"));         BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt")); 
            // 讀取數據         
            String line  = null;    
            while ((line = br.readLine())!=null) {     
                // 解析文本           
                String[] split = line.split("\\.");       
                // 保存到集合             
                lineMap.put(split[0],split[1]);
            }        
            // 釋放資源       
            br.close();          
            // 遍歷map集合         
            for (int i = 1; i <= lineMap.size(); i++) {    
                String key = String.valueOf(i);         
                // 獲取map中文本            
                String value = lineMap.get(key);    
                // 寫出拼接文本               
                bw.write(key+"."+value);     
                // 寫出換行              
                bw.newLine();       
            }
            // 釋放資源            
            bw.close();    
        } 
    }

轉換流

字符編碼和字符集

字符編碼

  • 計算機中儲存的信息都是用二進制數表示的,而咱們在屏幕上看到的數字、英文、標點符號、漢字等字符是二進制 數轉換以後的結果。按照某種規則,將字符存儲到計算機中,稱爲編碼 。反之,將存儲在計算機中的二進制數按照 某種規則解析顯示出來,稱爲解碼 。好比說,按照A規則存儲,一樣按照A規則解析,那麼就能顯示正確的文本f符 號。反之,按照A規則存儲,再按照B規則解析,就會致使亂碼現象。
  • 字符編碼 Character Encoding : 就是一套天然語言的字符與二進制數之間的對應規則

字符集

  • 字符集 Charset :也叫編碼表。是一個系統支持的全部字符的集合,包括各國家文字、標點符號、圖形符 號、數字等。

    截圖

  • 計算機要準確的存儲和識別各類字符集符號,須要進行字符編碼,一套字符集必然至少有一套字符編碼。常見字符 集有ASCII字符集、GBK字符集、Unicode字符集等

  • 可見,當指定了編碼,它所對應的字符集天然就指定了,因此編碼纔是咱們最終要關心的

  • ASCII字符集

    • ASCII(American Standard Code for Information Interchange,美國信息交換標準代碼)是基於拉丁 字母的一套電腦編碼系統,用於顯示現代英語,主要包括控制字符(回車鍵、退格、換行鍵等)和可顯 示字符(英文大小寫字符、阿拉伯數字和西文符號)。
    • 基本的ASCII字符集,使用7位(bits)表示一個字符,共128字符。ASCII的擴展字符集使用8位(bits) 表示一個字符,共256字符,方便支持歐洲經常使用字符。
  • ISO-8859-1字符集

    • 拉丁碼錶,別名Latin-1,用於顯示歐洲使用的語言,包括荷蘭、丹麥、德語、意大利語、西班牙語等。
    • ISO-5559-1使用單字節編碼,兼容ASCII編碼。
  • GBxxx字符集

    • GB就是國標的意思,是爲了顯示中文而設計的一套字符集。
    • GB2312:簡體中文碼錶。一個小於127的字符的意義與原來相同。但兩個大於127的字符連在一塊兒時, 就表示一個漢字,這樣大約能夠組合了包含7000多個簡體漢字,此外數學符號、羅馬希臘的字母、日文 的假名們都編進去了,連在ASCII裏原本就有的數字、標點、字母都通通從新編了兩個字節長的編碼,這 就是常說的"全角"字符,而原來在127號如下的那些就叫"半角"字符了。
    • GBK:最經常使用的中文碼錶。是在GB2312標準基礎上的擴展規範,使用了雙字節編碼方案,共收錄了 21003個漢字,徹底兼容GB2312標準,同時支持繁體漢字以及日韓漢字等。
    • GB18030:最新的中文碼錶。收錄漢字70244個,採用多字節編碼,每一個字能夠由1個、2個或4個字節 組成。支持中國國內少數民族的文字,同時支持繁體漢字以及日韓漢字等。
  • Unicode字符集 :

    • Unicode編碼系統爲表達任意語言的任意字符而設計,是業界的一種標準,也稱爲統一碼、標準萬國 碼。
    • 它最多使用4個字節的數字來表達每一個字母、符號,或者文字。有三種編碼方案,UTF-八、UTF-16和UTF32。最爲經常使用的UTF-8編碼。
    • UTF-8編碼,能夠用來表示Unicode標準中任何字符,它是電子郵件、網頁及其餘存儲或傳送文字的應用 中,優先採用的編碼。互聯網工程工做小組(IETF)要求全部互聯網協議都必須支持UTF-8編碼。因此, 咱們開發Web應用,也要使用UTF-8編碼。它使用一至四個字節爲每一個字符編碼,編碼規則:
      1. 128個US-ASCII字符,只需一個字節編碼。
      2. 拉丁文等字符,須要二個字節編碼。
      3. 大部分經常使用字(含中文),使用三個字節編碼。
        1. 其餘極少使用的Unicode輔助字符,使用四字節編碼

編碼引出的問題

  • 在IDEA中,使用 FileReader 讀取項目中的文本文件。因爲IDEA的設置,都是默認的 UTF-8 編碼,因此沒有任何問題。可是,當讀取Windows系統中建立的文本文件時,因爲Windows系統的默認是GBK編碼,就會出現亂碼。

    public class ReaderDemo {     
        public static void main(String[] args) throws IOException { 
            FileReader fileReader = new FileReader("E:\\File_GBK.txt");
            int read;
            while ((read = fileReader.read()) != ‐1) { 
                System.out.print((char)read); 
            }         
            fileReader.close();   
        } 
    } 
    /*
    輸出結果: 
    	���
    */

InputStreamReader類

  • 轉換流 java.io.InputStreamReader ,是Reader的子類,是從字節流到字符流的橋樑。它讀取字節,並使用指定 的字符集將其解碼爲字符。它的字符集能夠由名稱指定,也能夠接受平臺的默認字符集。

構造方法

  • InputStreamReader(InputStream in),建立一個使用默認字符集的字符流。

  • InputStreamReader(InputStream in, String charsetName),建立一個指定字符集的字符流。

    InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt")); InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
  • 指定編碼讀取

    public class ReaderDemo2 {    
        public static void main(String[] args) throws IOException {    
            // 定義文件路徑,文件爲gbk編碼           
            String FileName = "E:\\file_gbk.txt";      
            // 建立流對象,默認UTF8編碼           
            InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));    
            // 建立流對象,指定GBK編碼 
            InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK"); 
            // 定義變量,保存字符       
            int read;        
            // 使用默認編碼字符流讀取,亂碼       
            while ((read = isr.read()) != ‐1) {
                System.out.print((char)read); // ��Һ�
            }     
            isr.close();            
            // 使用指定編碼字符流讀取,正常解析         
            while ((read = isr2.read()) != ‐1) {     
                System.out.print((char)read);// 你們好    
            }         
            isr2.close();    
        }
    }
  • 轉換流圖解

    截圖

  • 練習,轉換文件編碼

    • 指定GBK編碼的轉換流,讀取文本文件。
    • 使用UTF-8編碼的轉換流,寫出文本文件。
    public class TransDemo {    
        public static void main(String[] args) {       
            // 1.定義文件路徑           
            String srcFile = "file_gbk.txt";      
            String destFile = "file_utf8.txt"; 
            // 2.建立流對象               
            // 2.1 轉換輸入流,指定GBK編碼
            InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");    
            // 2.2 轉換輸出流,默認utf8編碼
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile)); 
            // 3.讀寫數據             
            // 3.1 定義數組           
            char[] cbuf = new char[1024];      
            // 3.2 定義長度             
            int len;     
            // 3.3 循環讀取      
            while ((len = isr.read(cbuf))!=‐1) {      
                // 循環寫出           
                osw.write(cbuf,0,len);       
            }      
            // 4.釋放資源     
            osw.close();        
            isr.close(); 
        }   
    }

序列化

概述

  • Java 提供了一種對象序列化的機制。用一個字節序列能夠表示一個對象,該字節序列包含該 對象的數據 、 對象的 類型 和 對象中存儲的屬性 等信息。字節序列寫出到文件以後,至關於文件中持久保存了一個對象的信息。

  • 反之,該字節序列還能夠從文件中讀取回來,重構對象,對它進行反序列化。 對象的數據 、 對象的類型 和 對象中 存儲的數據 信息,均可以用來在內存中建立對象。看圖理解序列化

    截圖

ObjectOutputStream類

  • java.io.ObjectOutputStream 類,將Java對象的原始數據類型寫出到文件,實現對象的持久存儲

構造方法

  • public ObjectOutputStream(OutputStream out) ,建立一個指定OutputStream的ObjectOutputStream。

    FileOutputStream fileOut = new FileOutputStream("employee.txt"); ObjectOutputStream out = new ObjectOutputStream(fileOut);

序列化操做

  • 一個對象要想序列化,必須知足兩個條件:

    • 必須實現 java.io.Serializable 接口, Serializable 是一個標記接口,不實現此接口的類將不會使任 何狀態序列化或反序列化,會拋出 NotSerializableException 。
    • 該類的全部屬性必須是可序列化的。若是有一個屬性不須要可序列化的,則該屬性必須註明是瞬態的,使用 transient 關鍵字修飾。
    public class Employee implements java.io.Serializable {     
        public String name;     
        public String address;    
        public transient int age; // transient瞬態修飾成員,不會被序列化
        public void addressCheck() {       
            System.out.println("Address  check : " + name + " ‐‐ " + address);   
        } 
    }
  • 寫出對象方法

    • public final void writeObject (Object obj),將指定的對象寫出
    public class SerializeDemo{     
        public static void main(String [] args)   { 
            Employee e = new Employee();          
            e.name = "zhangsan";          
            e.address = "beiqinglu";           
            e.age = 20;           
            try {             
                // 建立序列化流對象   
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));  
                // 寫出對象             
                out.writeObject(e);          
                // 釋放資源               
                out.close();            
                fileOut.close();         
                System.out.println("Serialized data is saved"); 
                // 姓名,地址被序列化,年齡沒有被序列 化。          
            } catch(IOException i)   {         
                i.printStackTrace();       
            }    
        }  
    }
    /*
    輸出結果:
    	Serialized data is saved
    */

ObjectInputStream類

  • ObjectInputStream反序列化流,將以前使用ObjectOutputStream序列化的原始數據恢復爲對象。

構造方法

  • public ObjectInputStream(InputStream in) ,建立一個指定InputStream的ObjectInputStream

反序列化操做1

  • 若是能找到一個對象的class文件,咱們能夠進行反序列化操做,調用 ObjectInputStream 讀取對象的方法:

    • public final Object readObject (),讀取一個對象
     public static void main(String [] args)   {        
         Employee e = null;         
         try {                   
             // 建立反序列化流             
             FileInputStream fileIn = new FileInputStream("employee.txt"); 
             ObjectInputStream in = new ObjectInputStream(fileIn);        
             // 讀取一個對象             
             e = (Employee) in.readObject();            
             // 釋放資源          
             in.close();           
             fileIn.close();        
         }catch(IOException i) {        
             // 捕獲其餘異常            
             i.printStackTrace();      
             return;        
         }catch(ClassNotFoundException c)  {  
             // 捕獲類找不到異常              
             System.out.println("Employee class not found");   
             c.printStackTrace();          
             return;        
         }         
         // 無異常,直接打印輸出        
         System.out.println("Name: " + e.name); // zhangsan     
         System.out.println("Address: " + e.address); // beiqinglu   
         System.out.println("age: " + e.age); // 0  
     }
    • 對於JVM能夠反序列化對象,它必須是可以找到class文件的類。若是找不到該類的class文件,則拋出一個 ClassNotFoundException 異常

反序列化操做2

  • 另外,當JVM反序列化對象時,能找到class文件,可是class文件在序列化對象以後發生了修改,那麼反序列化操 做也會失敗,拋出一個 InvalidClassException 異常。發生這個異常的緣由以下

    • 該類的序列版本號與從流中讀取的類描述符的版本號不匹配
    • 該類包含未知數據類型
    • 該類沒有可訪問的無參數構造方法
  • Serializable 接口給須要序列化的類,提供了一個序列版本號。 serialVersionUID 該版本號的目的在於驗證序 列化的對象和對應類是否版本匹配

    public class Employee implements java.io.Serializable {      
        // 加入序列版本號     
        private static final long serialVersionUID = 1L;     
        public String name;    
        public String address;     
        // 添加新的屬性 ,從新編譯, 能夠反序列化,該屬性賦爲默認值.  
        public int eid;      
        public void addressCheck() {       
            System.out.println("Address  check : " + name + " ‐‐ " + address);    
        } 
    }

序列化集合

  • 將存有多個自定義對象的集合序列化操做,保存到 list.txt 文件中

  • 反序列化 list.txt ,並遍歷集合,打印對象信息

  • 案例分析

    • 把若干學生對象 ,保存到集合中。
    • 把集合序列化。
    • 反序列化讀取時,只須要讀取一次,轉換爲集合類型。
    • 遍歷集合,能夠打印全部的學生信息
    public class SerTest { 
        public static void main(String[] args) throws Exception {   
            // 建立 學生對象        
            Student student = new Student("老王", "laow");      
            Student student2 = new Student("老張", "laoz");        
            Student student3 = new Student("老李", "laol");            
            ArrayList<Student> arrayList = new ArrayList<>();        
            arrayList.add(student);          
            arrayList.add(student2);        
            arrayList.add(student3);        
            // 序列化操做         
            // serializ(arrayList);     
            // 反序列化      
            ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));      
            // 讀取對象,強轉爲ArrayList類型       
            ArrayList<Student> list  = (ArrayList<Student>)ois.readObject();
            for (int i = 0; i < list.size(); i++ ){          
                Student s = list.get(i);          
                System.out.println(s.getName()+"‐‐"+ s.getPwd())
              }    
        }        
        private static void serializ(ArrayList<Student> arrayList) throws Exception {
            // 建立 序列化流          
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));     
            // 寫出對象         
            oos.writeObject(arrayList);    
            // 釋放資源         
            oos.close();  
        }
    }

打印流

概述

  • 平時咱們在控制檯打印輸出,是調用 print 方法和 println 方法完成的,這兩個方法都來自於 java.io.PrintStream 類,該類可以方便地打印各類數據類型的值,是一種便捷的輸出方式

PrintStream類

構造方法

  • public PrintStream(String fileName),使用指定的文件名建立一個新的打印流

    PrintStream ps = new PrintStream("ps.txt");

改變打印流向

  • System.out 就是 PrintStream 類型的,只不過它的流向是系統規定的,打印在控制檯上。不過,既然是流對象, 咱們就能夠玩一個"小把戲",改變它的流向。

    public class PrintDemo {     
        public static void main(String[] args) throws IOException { 
            // 調用系統的打印流,控制檯直接輸出97          
            System.out.println(97);       
            // 建立打印流,指定文件的名稱             
            PrintStream ps = new PrintStream("ps.txt");      
            // 設置系統的打印流流向,輸出到ps.txt       
            System.setOut(ps);       
            // 調用系統的打印流,ps.txt中輸出97       
            System.out.println(97);   
        }
    }
相關文章
相關標籤/搜索