Java基礎-IO篇

1. IO介紹

1.1 什麼是IO

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

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

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

1.2 IO的分類

IO

根據數據的流向分爲:輸入流和輸出流。ide

  • 輸入流 :把數據從 其餘設備 上讀取到 內存 中的流。
  • 輸出流 :把數據從 內存 中寫出到 其餘設備 上的流。

格局數據的類型分爲:字節流和字符流。函數

  • 字節流 :以字節爲單位,讀寫數據的流。
  • 字符流 :以字符爲單位,讀寫數據的流。

1.3 IO頂級的父類

頂級父類

2. 字節流

2.1 一切皆爲字節流

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

2.2 字節輸出流-OutputStream

​ 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字節,從偏移量 off開始輸出到此輸出流。
    • public abstract void write(int b) :將指定的字節輸出流。
  • 注意事項
    • close方法,當完成流的操做時,必須調用此方法,釋放系統資源。

2.3 FileOutputStream類

​ OutputStream 有不少子類,咱們從最簡單的一個子類開始。優化

java.io.FileOutputStream 類是文件輸出流,用於將數據寫出到文件。this

  • 構造方法編碼

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

    FileOutputStream fos = new FileOutputStream("a.txt");
        //  【write(int b)】
        // 寫入的十進制會轉換成二進制存儲到a.txt
        // 讀取文件時,文件會按照指定的編碼格式轉換爲對應的內容
        fos.write(97);  // 寫入→97→110 0001‬→內存→讀取→110 0001→ASCII→a
        fos.write(98);
        // 【 write(byte[] b)】
        byte[]bs = {97,98,99,101,102};
        fos.write(bs);
        // 【字符串轉換字節數組getBytes()】
        fos.write("你好".getBytes());
        // 【write(byte[] b, int off, int len)指定長度的字節數組】
        fos.write("xyz".getBytes(),0,2);
        fos.close();
  • 數據追加續寫

    • 問題

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

      • public FileOutputStream(File file, boolean append) : 建立文件輸出流以寫入由指定的 File對象表示的文件。
      • public FileOutputStream(String name, boolean append) : 建立文件輸出流以指定的名稱寫入文件。
      • 參數: true 表示追加數據, false 表示清空原有數據。
    • 代碼

      FileOutputStream fos = new FileOutputStream("a.txt",true);
          fos.write("我是新追加的數據".getBytes());
          fos.close();
  • 寫出換行

    • 系統中的換行

      • Windows系統裏,每行結尾是 回車+換行 ,即 \r\n ;
      • Unix系統裏,每行結尾只有 換行 ,即 \n ;
      • Mac系統裏,每行結尾是 回車 ,即 \r 。從 Mac OS X開始與Linux統一。
    • 代碼

      FileOutputStream fos = new FileOutputStream("b.txt",true);
          for (int i = 0; i < 10; i++) {
            fos.write(("\r\n第" + i +"行數據:" + i*100).getBytes() );
          }
          fos.close();

2.4 字節輸入流

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

  • public void close() :關閉此輸入流並釋放與此流相關聯的任何系統資源。

  • public abstract int read() : 從輸入流讀取數據的下一個字節。

  • public int read(byte[] b): 從輸入流中讀取一些字節數,並將它們存儲到字節數組 b中 。

    • 使用數組讀取,每次讀取多個字節,減小了系統間的IO操做次數,從而提升了讀寫的效率,建議開發中使

      用。

close方法,當完成流的操做時,必須調用此方法,釋放系統資源。

2.5 FileInputStream類

java.io.FileInputStream 類是文件輸入流,從文件中讀取字節。

  • 構造方法

    1. FileInputStream(File file) : 經過打開與實際文件的鏈接來建立一個 FileInputStream ,該文件由文件系 統中的 File對象 fifile命名。
    2. FileInputStream(String name): 經過打開與實際文件的鏈接來建立一個 FileInputStream ,該文件由文件 系統中的路徑名 name命名。
  • 讀取字節數據代碼

    // 建立字節輸入流對象
        FileInputStream fis = new FileInputStream("day07_IO//a.txt");
        //【一次讀取一個字節】
        // 循環讀取文件中的字節
        int len = 0;
        while ((len=fis.read())!=-1){
          System.out.print((char)len);
        }
        // 結果:ABC
        // 【一次讀取多個字節】
        // 將字節轉換爲字符串new Strig(bytes[]bts,int offset,int length);
        byte[]bts = new byte[10];
        int len2 = 0;
        while ((len2=fis.read(bts))!=-1){
          System.out.println(new String(bts,0,len2));
        }
      fis.close();

2.6 複製圖片文件

  • 原理
    原理

  • 代碼

    public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        // 建立輸入流對象-用來讀取本地文件
        FileInputStream fis = new FileInputStream("D:\\test.jpg");
        // 建立輸出流對象-用來寫入本地文件
        FileOutputStream fos = new FileOutputStream("day07_IO\\test_copy.jpg");
        // 建立字節數組,一次從本地讀取多個字節
        byte[]bts = new byte[1024];
        int len = 0; // 表示讀取的有效字節個數
        // 循環讀取本地數據
        while ((len=fis.read(bts))!=-1){
          // 把實際讀取的字節寫入本地文件
          fos.write(bts,0,len);
        }
        // 關閉輸出流資源
        fos.close();
        // 關閉輸入流資源
        fis.close();
        long e = System.currentTimeMillis();
        System.out.println("複製成功!");
        System.out.println("共耗時" + (e-s)+"毫秒");
      }

3. 字符流

3.1 爲何要學習字符流

當使用字節流讀取文本文件時,可能會有一個小問題。就是遇到中文字符時,可能不會顯示完整的字符,那是由於

一箇中文字符可能佔用多個字節存儲。因此Java提供一些字符流類,以字符爲單位讀寫數據,專門用於處理文本文

件。

3.2 字符輸入流-Reader

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

3.3 FileReader類

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

  • 注意事項

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

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

  • 構造函數

    1. FileReader(File file) : 建立一個新的 FileReader ,給定要讀取的File對象。
    2. FileReader(String fileName) : 建立一個新的 FileReader ,給定要讀取的文件的名稱。
  • 代碼

    public static void main(String[] args) throws IOException {
        FileReader reader = new FileReader("day07_IO//a.txt");
        // 【一次讀取一個字符】
        int len = 0;
        while ((len = reader.read())!=-1) {
          System.out.print((char)len);
        }
        // 【一次讀取多個字符】
        char[]chs = new char[1024];
        int len = 0;
        while ((len = reader.read(chs))!=-1) {
          System.out.print(new String(chs,0,len));
          // new String(char[]chars,int offset,int count);
        }
        reader.close();
    
      }

3.4 字符輸出流-Writer

java.io.Writer 抽象類是表示用於寫出字符流的全部類的超類,將指定的字符信息寫出到目的地。它定義了字節

輸出流的基本共性功能方法。

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

3.5 FileWriter類

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

  • 構造函數:

    1. FileWriter(File file) : 建立一個新的 FileWriter,給定要讀取的File對象。
    2. FileWriter(String fileName) : 建立一個新的 FileWriter,給定要讀取的文件的名稱。
  • 代碼:

    public static void main(String[] args) throws IOException {
        // 建立輸出字符流對象
        FileWriter fw = new FileWriter("day07_IO\\b.txt");
        fw.write("你好");
        fw.flush(); // flush將緩衝區的數據刷新到文件中,可繼續寫入
        fw.write("我好");
        fw.flush();
        fw.write("你們好");
        fw.close();// close 將緩衝區中的數據刷新到文件中,而且會釋放輸出流對象,後繼沒法繼續輸出
        fw.write("真好");   // 異常
    
        // 建立輸出流對象,向指定的文件中追加寫入數據
        FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true);
        for (int i = 0; i < 10; i++) {
          fw2.write("你好,新的世界!" + i + "\r\n");
        }
        fw2.close();
      }
  • 注意事項:

    • 由於內置緩衝區的緣由,若是不關閉輸出流,沒法寫出字符到文件中。可是關閉的流對象,是沒法繼續寫出數據的。若是咱們既想寫出數據,又想繼續使用流,就須要 flush 方法了。
    • flush :刷新緩衝區,流對象能夠繼續使用。
    • close :先刷新緩衝區,而後通知系統釋放資源。流對象不能夠再被使用了。

4. IO異常處理

4.1 JDK7以前的處理方式

  • 處理方式:try-catch-finally

    • 在finally中釋放資源
  • 代碼:

    public static void main(String[] args)  {
        // 建立輸出流對象,向指定的文件中追加寫入數據
        FileWriter fw2 = null;
        try{
          fw2 = new FileWriter("day07_IO\\b.txt",true);
          for (int i = 0; i < 10; i++) {
            fw2.write("你好,新的世界!" + i + "\r\n");
          }
        }catch (IOException e) {
          e.printStackTrace();
        }finally {
          if(fw2!=null){
            try {
              fw2.close();
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
    
      }

4.2 JDK7中的新特性

  • 處理方式:JDK7優化後的 try-with-resource 語句,該語句確保了每一個資源在語句結束時關閉。所謂的資源 (resource)是指在程序完成後,必須關閉的對象。

    try (建立流對象語句,若是多個,使用';'隔開) { 
        // 讀寫數據 
    } catch (IOException e) { 
        e.printStackTrace(); 
    }
  • 代碼:

    public static void main(String[] args)  {
        // 建立輸出流對象,向指定的文件中追加寫入數據
        try(FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true)){
          for (int i = 0; i < 100; i++) {
            fw2.write("你好,新的世界!" + i + "\r\n");
          }
        }catch (IOException e) {
          e.printStackTrace();
        }
      }

4.3 JDK9中的新特性

​ JDK9中 try-with-resource 的改進,對於引入對象的方式,支持的更加簡潔。被引入的對象,一樣能夠自動關閉, 無需手動close

  • 格式

    // 被final修飾的對象 
    final Resource resource1 = new Resource("resource1"); 
    // 普通對象 
    Resource resource2 = new Resource("resource2"); 
    // 引入方式:直接引入 
    try (resource1; resource2) { 
        // 使用對象 
    } catch (IOException e) { 
        e.printStackTrace(); 
    }
  • 代碼

    public static void main(String[] args) throws IOException  {
        // 建立輸出流對象,向指定的文件中追加寫入數據
        FileWriter fw2 = new FileWriter("day07_IO\\b.txt",true);
        try(fw2){
          for (int i = 0; i < 100; i++) {
            fw2.write("你好,新的世界!" + i + "\r\n");
          }
        }catch (IOException e) {
          e.printStackTrace();
        }
      }

5. 屬性集

5.1 屬性集合介紹

java.util.Properties 繼承於 Hashtable ,來表示一個持久的屬性集。它使用鍵值結構存儲數據,每一個鍵及其

對應值都是一個字符串。該類也被許多Java類使用,好比獲取系統屬性時, System.getProperties 方法就是返回

一個 Properties 對象。

5.2 Properties 類

  • 構造方法public Properties() :建立一個空的屬性列表。

  • 基本的存儲方法

    1. public Object setProperty(String key, String value): 保存一對屬性。
    2. public String getProperty(String key) :使用此屬性列表中指定的鍵搜索屬性值。
    3. public Set<String> stringPropertyNames() :全部鍵的名稱的集合。
  • 與流相關的方法

    1. public void load(InputStream inStream) : 從字節輸入流中讀取鍵值對。
      • 注意:文本中的數據,必須是鍵值對形式,可使用空格、等號、冒號等符號分隔。
  • 代碼

    public static void main(String[] args) throws IOException {
        // 獲取系統相關的信息
        System.out.println(System.getProperties());
        // 建立一個屬性集合
        Properties pro = new Properties();
        pro.setProperty("張三","180");
        pro.setProperty("李四","189");
        pro.setProperty("王五","178");
        pro.setProperty("趙六","188");
        // 讀取屬性集合中的數據
        Set set = pro.keySet();
        for (Object o : set) {
          System.out.println(pro.get(o));
        }
        // 把集合中臨時的數據輸出到硬盤中
        // pro.store(new FileWriter("day07_IO\\data.txt"),"save data");
    
        // 讀取本地文件中的數據(鍵值對的方式)到屬性集合中
        Properties pro2 = new Properties();
        pro2.load(new FileReader("day07_IO\\data.txt"));
        System.out.println(pro2);
      }

6. 緩衝流

6.1 緩衝流介紹

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

  • 字節緩衝流: BufferedInputStream , BufferedOutputStream
  • 字符緩衝流: BufferedReader , BufferedWriter

緩衝流的基本原理,是在建立流對象時,會建立一個內置的默認大小的緩衝區數組,經過緩衝區讀寫,減小系統IO次數,從而提升讀寫的效率。

6.2 字節緩衝流

  • 構造方法

    1. public BufferedInputStream(InputStream in):建立一個 新的緩衝輸入流。
    2. public BufferedOutputStream(OutputStream out) : 建立一個新的緩衝輸出流。
  • 代碼測試

    1. 基本方式一次讀寫一個字節

      public static void main(String[] args) throws IOException {
          long s = System.currentTimeMillis();
          // 建立字節輸入流,讀取本地文件數據
          FileInputStream fis = new FileInputStream("D:\\test.jpg");
          // 建立字節輸出流,向本地文件寫入數據
          FileOutputStream fos = new FileOutputStream("day08_IO\\test01.jpg");
          // 定義變量表示讀取的字節
          int len = 0;
          // 循環遍歷讀取本地文件
          while ((len=fis.read())!=-1) {
            // 把讀取的字節寫入本地
            fos.write(len);
          }
          // 關閉輸出流
          fos.close();
          // 關閉輸入流
          fis.close();
          long e = System.currentTimeMillis();
          System.out.println("共耗時:" + (e-s) + "毫秒"); // 161毫秒
      
        }
    2. 基本方式一次讀寫多個字節

      public static void main(String[] args) throws IOException {
          long s = System.currentTimeMillis();
          // 建立字節輸入流,讀取本地文件數據
          FileInputStream fis = new FileInputStream("D:\\test.jpg");
          // 建立字節輸出流,向本地文件寫入數據
          FileOutputStream fos = new FileOutputStream("day08_IO\\test02.jpg");
          // 定義變量表示讀取的有序字節個數
          int len = 0;
          // 定義字節數組,表示一次要讀取的字節數
          byte[]bts = new byte[1024];
          // 循環遍歷讀取本地文件
          while ((len=fis.read(bts))!=-1) {
            // 把讀取的字節寫入本地
            fos.write(bts,0,len);
          }
          // 關閉輸出流
          fos.close();
          // 關閉輸入流
          fis.close();
          long e = System.currentTimeMillis();
          System.out.println("共耗時:" + (e-s) + "毫秒"); // 共耗時:7毫秒
      
        }
    3. 緩衝流方式默認讀寫方式

      public static void main(String[] args) throws IOException {
          long s = System.currentTimeMillis();
          // 建立字節緩衝流輸入流
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test.jpg"));
          // 建立字節緩衝流輸出流f
          BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day08_IO\\test03.jpg"));
          // 循環讀取本地數據
          int len = 0;
          while ((len = bis.read())!=-1){
            bos.write(len);
          }
          bos.close();
          bis.close();
          long e = System.currentTimeMillis();
          System.out.println("共耗時:" + (e-s) + "毫秒"); // 共耗時:8毫秒
        }
    4. 緩衝流方式指定字節讀取

      public static void main(String[] args) throws IOException {
          long s = System.currentTimeMillis();
          // 建立字節緩衝流輸入流
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\test.jpg"));
          // 建立字節緩衝流輸出流f
          BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day08_IO\\test04.jpg"));
          // 循環讀取本地數據
          int len = 0;
          byte[]bts = new byte[1024];
          while ((len = bis.read(bts))!=-1){
            bos.write(bts,0,len);
          }
          bos.close();
          bis.close();
          long e = System.currentTimeMillis();
          System.out.println("共耗時:" + (e-s) + "毫秒"); // 共耗時:6毫秒
        }

6.3 字符緩衝流

  • 構造方法

    1. public BufferedReader(Reader in) :建立一個 新的緩衝輸入流。
    2. public BufferedWriter(Writer out) : 建立一個新的緩衝輸出流。
  • 特有方法:字符緩衝流的基本方法與普通字符流調用方式一致,再也不闡述,咱們來看它們具有的特有方法。

    1. BufffferedReader: public String readLine() : 讀一行文字。
    2. BufffferedWriter: public void newLine(): 寫一行行分隔符,由系統屬性定義符號。
  • 代碼:

    public static void main(String[] args) throws IOException {
        // 建立字符緩衝輸出流
        BufferedWriter bw = new BufferedWriter( new FileWriter("day08_IO\\01.txt"));
        for (int i = 0; i < 100; i++) {
          bw.write("貴在堅持!不僅是說說!" + i);
          bw.newLine();
        }
        bw.close();
        // 建立字符緩衝輸入流
        BufferedReader br = new BufferedReader(new FileReader("day08_IO\\01.txt"));
        String line;
        while ((line=br.readLine())!=null){
          System.out.println(line);
        }
        br.close();
      }

6.4 文本排序

  • 需求:讀取本地文件中的文本,並排序後從新寫入本地新的文件中

    2.JavaScript
    1.Java
    4.PHP
    6.C#
    7.C
    3.C++
    5.VB
  • 代碼:

    public static void main(String[] args) throws IOException {
        // 建立HashMap對象存放讀取的本地數據
        HashMap<String,String > map = new HashMap<>();
        // 建立字符緩衝區輸入流,逐行讀取本地數據
        BufferedReader br = new BufferedReader(new FileReader("day08_IO\\02.txt"));
        String line;
        while ((line=br.readLine())!=null) {
          // 分割
          String[]strs = line.split("\\.");
          map.put(strs[0],strs[1]);
        }
        br.close();
        // 建立字符輸出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("day08_IO\\03.txt"));
        for (int i = 1; i <= map.size(); i++) {
          bw.write(i +"."+ map.get(String.valueOf(i)));
          bw.newLine();
        }
        bw.close();
    
      }

7. 轉換流

7.1 字符編碼和字符集

字符集

​ 計算機中儲存的信息都是用二進制數表示的,而咱們在屏幕上看到的數字、英文、標點符號、漢字等字符是二進制數轉換以後的結果。按照某種規則,將字符存儲到計算機中,稱爲編碼 。反之,將存儲在計算機中的二進制數按照某種規則解析顯示出來,稱爲解碼 。好比說,按照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和UTF- 32。最爲經常使用的UTF-8編碼。
      • UTF-8編碼,能夠用來表示Unicode標準中任何字符,它是電子郵件、網頁及其餘存儲或傳送文字的應用中,優先採用的編碼。互聯網工程工做小組(IETF)要求全部互聯網協議都必須支持UTF-8編碼。因此,咱們開發Web應用,也要使用UTF-8編碼。它使用一至四個字節爲每一個字符編碼,編碼規則:
        1. 128個US-ASCII字符,只需一個字節編碼。
        2. 拉丁文等字符,須要二個字節編碼。
        3. 大部分經常使用字(含中文),使用三個字節編碼。
        4. 其餘極少使用的Unicode輔助字符,使用四字節編碼。

7.2 編碼引出的問題

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

public static void main(String[] args) throws IOException {
    FileReader fr = new FileReader("D:\\a.txt");
    int len = 0;
    while((len=fr.read())!=-1){
      System.out.print((char)len); 
    }
    fr.close();
  }
// 結果:��� 亂碼

那麼如何讀取GBK編碼的文件呢?

7.3 InputStreamReader類

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

  • 構造方法

    1. InputStreamReader(InputStream in): 建立一個使用默認字符集的字符流。
    2. InputStreamReader(InputStream in, String charsetName) : 建立一個指定字符集的字符流。
  • 代碼

    public static void main(String[] args) throws IOException {
        InputStreamReader fr = new InputStreamReader(new FileInputStream("D:\\a.txt"),"gbk");
        int len = 0;
        while((len=fr.read())!=-1){
          System.out.print((char)len);
        }
        fr.close();
      }
    // 結果:你好

7.4 OutputStreamWriter 類

​ 轉換流 java.io.OutputStreamWriter ,是Writer的子類,是從字符流到字節流的橋樑。使用指定的字符集將字符 編碼爲字節。它的字符集能夠由名稱指定,也能夠接受平臺的默認字符集。

  • 構造函數

    1. OutputStreamWriter(OutputStream in) : 建立一個使用默認字符集的字符流。
    2. OutputStreamWriter(OutputStream in, String charsetName) : 建立一個指定字符集的字符流。
  • 代碼

    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day08_IO\\04.txt"),"gbk");
        osw.write("你好");
        osw.close();
      }

7.5 圖解轉換流原理

原理圖

8. 序列化

8.1 對象序列化介紹

​ Java 提供了一種對象序列化的機制。用一個字節序列能夠表示一個對象,該字節序列包含該 對象的數據 、 對象的 類型 和 對象中存儲的屬性 等信息。字節序列寫出到文件以後,至關於文件中持久保存了一個對象的信息。 反之,該字節序列還能夠從文件中讀取回來,重構對象,對它進行反序列化。 對象的數據 、 對象的類型 和 對象中 存儲的數據 信息,均可以用來在內存中建立對象。看圖理解序列化:

原理圖

8.2 ObjectOutputStream類

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

  • 構造方法

    1. public ObjectOutputStream(OutputStream out) : 建立一個指定OutputStream的ObjectOutputStream。
  • 序列化操做

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

      • 該類必須實現java.io.Serializable 接口, Serializable 是一個標記接口,不實現此接口的類將不會使任何狀態序列化或反序列化,會拋出 NotSerializableException

      • 該類的全部屬性必須是可序列化的。若是有一個屬性不須要可序列化的,則該屬性必須註明是瞬態的,使用 transient 關鍵字修飾。

      • 代碼:

        public class Person implements Serializable { // 實現Serializable,表示可序列化
          private String name;
          private int age;
          private transient int height;  // 該屬性不會被序列化
          public Person(String name, int age, int height) {
            this.name = name;
            this.age = age;
            this.height = height;
          }
          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;
          }
          public int getHeight() {
            return height;
          }
          public void setHeight(int height) {
            this.height = height;
          }
          @Override
          public String toString() {
            return "姓名:" + name + ",年齡:"+ age + ",身高:" + height;
          }
        }
    2. 寫出對象方法

      • public final void writeObject (Object obj) : 將指定的對象寫出

      • 代碼

        public static void main(String[] args) throws IOException {
            // 建立一我的類對象
            Person p = new Person("張三",18,180);
            // 建立一個序列化輸出流對象
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("day08_IO\\person.txt"));
            // 把指定的對象序列化後寫入本地文件
            os.writeObject(p);
            os.close();
            System.out.println("save a Object success");
          }

8.3 ObjectInputStream類

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

  • 構造方法

  • public ObjectInputStream(InputStream in);: 建立一個指定InputStream的ObjectInputStream。

  • 反序列化操做1

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

      • public final Object readObject () : 讀取一個對象。
    • 代碼:

      public static void main(String[] args) throws IOException, ClassNotFoundException {
          ObjectInputStream os = new ObjectInputStream(new FileInputStream("day08_IO\\person.txt"));
          Person p = (Person) os.readObject();
          System.out.println(p); // 姓名:張三,年齡:18,身高:0
          os.close();
        }
      
      // 對於JVM能夠反序列化對象,它必須是可以找到class文件的類。若是找不到該類的class文件,則拋出一個ClassNotFoundException 異常。
  • 反序列化操做2

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

      • 該類的序列版本號與從流中讀取的類描述符的版本號不匹配
      • 該類包含未知數據類型
      • 該類沒有可訪問的無參數構造方法
    • 解決方案

      • Serializable 接口給須要序列化的類,提供了一個序列版本號。 serialVersionUID 該版本號的目的在於驗證序 列化的對象和對應類是否版本匹配。
    • 代碼

      public class Person implements Serializable { // 實現Serializable,表示可序列化
        // 定義序列化版本號
        private static final long serialVersionUID = 10L;
        private String name;
        public int age;
        private transient int height;  // 該屬性不會被序列化
        public Person(String name, int age, int height) {
          this.name = name;
          this.age = age;
          this.height = height;
        }
        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;
        }
        public int getHeight() {
          return height;
        }
        public void setHeight(int height) {
          this.height = height;
        }
        @Override
        public String toString() {
          return "姓名:" + name + ",年齡:"+ age + ",身高:" + height;
        }
      }

9. 打印流

9.1 打印流介紹

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

9.2 PrintStream類

  • 構造方法

    • public PrintStream(String fileName): 使用指定的文件名建立一個新的打印流。
  • System.out 就是 PrintStream 類型的,只不過它的流向是系統規定的,打印在控制檯上。不過,既然是流對象, 咱們就能夠玩一個"小把戲",改變它的流向。

  • 代碼

    public static void main(String[] args) throws IOException {
        // 打印在控制檯
        System.out.println("你好");
        // 建立打印流,流向指定的本地文件
        PrintStream ps = new PrintStream(new FileOutputStream("day08_IO\\print.txt"));
        // 設置System.out的打印流向指向ps
        System.setOut(ps);
        // 後續打印在print.txt文件中
        System.out.println("你好1");
        System.out.println("你好2");
        System.out.println("你好3");
        ps.close();
      }
相關文章
相關標籤/搜索