1. 程式人生 > 實用技巧 >JavaSE學習筆記 - I/O流詳解

JavaSE學習筆記 - I/O流詳解

I/O 概述

  I/O流:對硬碟檔案進行讀寫,輸入流就是將硬碟檔案的內容讀入到記憶體中使用,輸出流就是將記憶體中的資料讀入到硬碟檔案中,以便於長期儲存。檔案是由字元或者位元組構成的,所以可以進行寫入和讀取。所有的檔案資料都是以二進位制數字的形式儲存,都是一個一個的位元組,在傳輸的時候也是以二進位制的形式進行資料的傳輸。

位元組流繼承結構圖

字元流繼承機構圖

位元組流

OutputStream 與 FileOutputStream(檔案流)

  OutputStream:所有位元組流輸出流的父類,將制定的位元組資料寫入硬碟檔案中。FileOutputStream:檔案專屬的流。
  按照位元組或者字元的方式讀取或寫入檔案。

檔案的寫入

public class Main {
    public static void main(String[] args) throws IOException {
        OutputStream os = new FileOutputStream("1.txt");
        //寫入單個位元組
        os.write(97);//a
        //寫入多個位元組,如果第一個位元組是正數預設查詢ASCII碼錶,如果是負數,兩個位元組組成一箇中文,預設查詢GBK碼錶
        byte[] bytes = {65, 66, 67};
        os.write(bytes);//ABC
        byte[] bytes1 = {-65, -66, -67, 68, 69};
        os.write(bytes1);//烤紻E
        byte[] bytes2 = "啦啦啦".getBytes();
        System.out.println(Arrays.toString(bytes2));
        //UTF-8三個位元組一個漢字,GBK兩位元組一個漢字,故亂碼
        os.write(bytes2);//鍟﹀暒鍟?
        os.close();
    }
}

檔案的續寫與換行

public class Main {
    public static void main(String[] args) throws IOException {
        OutputStream os = new FileOutputStream("1.txt", true);
        os.write("啦啦啦".getBytes());
        for (int i = 0; i < 5; i++) {
            os.write("啦啦啦".getBytes());
            os.write("\r\n".getBytes());
        }
        os.close();
    }
}

InputStream 與 FileInputStream(檔案流)

  InputStream:所有位元組輸入流的父類,可以將檔案中的資料載入到記憶體中。FileInputStream:檔案專屬的流。

讀取單個位元組

public class Main {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("C:\\Users\\17388\\Desktop\\1.txt");//abced
//        int len = is.read();
//        System.out.println(len);//97
//        len = is.read();
//        System.out.println(len);//98
//        len = is.read();
//        System.out.println(len);//99
//        len = is.read();
//        System.out.println(len);//100
//        len = is.read();
//        System.out.println(len);//101
//        len = is.read();
//        System.out.println(len);//-1 到達檔案末尾

        int len = 0;
        while((len = is.read()) != -1) {
            System.out.println((char) len);
        }//abcde
        is.close();
    }
}

讀取多個位元組

public class Main {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("1.txt");//abcde
        //一次讀取三個位元組
//        byte[] bytes = new byte[3];
//        int len = is.read(bytes);
//        System.out.println(len);
//        System.out.println(new String(bytes));//abc
//        len = is.read(bytes);
//        System.out.println(len);
//        System.out.println(new String(bytes, 0, len));//de
//        len = is.read(bytes);
//        System.out.println(len);
//        System.out.println(new String(bytes, 0, len));//StringIndexOutOfBoundsException

        byte[] bytes = new byte[1024];
        int len = 0;
        while((len = is.read(bytes)) != -1) {
            System.out.println(len);
            System.out.println(new String(bytes, 0, len));//abcde
        }
        is.close();
    }
}

讀取中文

public class Main {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("1.txt");//啦啦啦
        //utf-8是三個位元組一個漢字,gbk是兩個位元組一個漢字,讀取時可能會出現問題
        //所以會亂碼,後面可以使用字元流來解決
        //或者每次讀取三個位元組
        byte[] bytes = new byte[3];
        int len = 0;
        while((len = is.read(bytes)) != -1) {
            System.out.println(len);
            System.out.println(new String(bytes, 0, len));//�
        }
        is.close();
    }
}

檔案複製

public class Main {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("C:\\Users\\17388\\Desktop\\1.txt");//啦啦啦
        OutputStream os = new FileOutputStream("C:\\Users\\17388\\Desktop\\2.txt");
        byte[] bytes = new byte[3];
        int len = 0;
        while((len = is.read(bytes)) != -1) {
            os.write(new String(bytes, 0, len).getBytes());
        }
        is.close();
        os.close();
    }
}

字元流

Writer 與 FileWriter(檔案流)

  Writer:所有字元輸出流的父類,將指定的字元輸出到硬碟檔案中。FileWriter:檔案專屬字元輸出流。
檔案的寫入

public class Main {
    public static void main(String[] args) throws IOException {
        Writer fw = new FileWriter("1.txt");
        //寫入單個字元
        fw.write(97);
        fw.write(97);
        //寫入多個字元
        char[] chars = {'a', 'b', 'c'};
        fw.write(chars);
        fw.write("啦啦啦");
        //fw.flush();//將記憶體緩衝區的資料重新整理到檔案中
        fw.close();//不寫flush自動將緩衝區內容重新整理到檔案中
    }
}

檔案的續寫與換行

public class Main {
    public static void main(String[] args) throws IOException {
        Writer fw = new FileWriter("1.txt", true);
        //寫入單個字元
        fw.write(97);
        fw.write(97);
        fw.write("\r\n");
        //寫入多個字元
        char[] chars = {'a', 'b', 'c'};
        fw.write(chars);
        fw.write("\r\n");
        fw.write(chars, 0, 1);
        fw.write("\r\n");
        fw.write("啦啦啦");
        //fw.flush();//將記憶體緩衝區的資料重新整理到檔案中
        fw.close();//不寫flush自動將緩衝區內容重新整理到檔案中
    }
}

Reader 與 FileReader(檔案流)

  Reader:所有字元輸入流的父類,將指定的字元輸出到硬碟檔案中。FileReader:檔案專屬字元輸入流。

讀取一個字元

public class Main {
    public static void main(String[] args) throws IOException {
        Reader fr = new FileReader("1.txt");
        int len = 0;
        while((len = fr.read()) != -1) {
            System.out.print((char) len);
        }
        fr.close();
    }
}

讀取多個字元

public class Main {
    public static void main(String[] args) throws IOException {
        Reader fr = new FileReader("1.txt");
        int len = 0;
        char[] chars = new char[1024];
        while((len = fr.read(chars)) != -1) {
            System.out.print(new String(chars, 0, len));
        }
        fr.close();
    }
}

緩衝流

  緩衝流能夠高效的進行讀寫操作,是對檔案流的增強,在建立流物件的時候自動建立一個預設大小的緩衝區陣列,在讀取資料的時候,緩衝流會將資料一次性讀取到緩衝區中,然後記憶體在讀取資料的時候直接從緩衝區中取資料;在進行寫入資料的時候先將資料放入緩衝區中,然後在一次性的將資料寫入硬碟檔案中。這樣就減少了系統的I/O次數,從而提高讀寫的效率。

BufferedOutputStream 與 BufferedInputStream


位元組緩衝輸出流

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("1.txt"));
        bos.write("寫入緩衝區".getBytes());
        bos.close();//自動將緩衝區資料重新整理到檔案中
    }
}

位元組緩衝輸入流

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt"));
        byte[] bytes = new byte[2]; //中文亂碼,編碼問題
        //可以讀取超過檔案的長度不會造成中文亂碼
        int len = 0;
        while((len = bis.read(bytes)) != -1) {
            System.out.println(new String(bytes, 0, len));
        }
//        int len = 0;
//        while((len = bis.read()) != -1) {
//            System.out.println(len);//無法讀中文
//        }
        bis.close();
    }
}

BufferedWriter 與 BufferedReader



字元緩衝輸出流

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt"));
        bw.write("啦啦啦");
        bw.newLine();//寫入換行符,Linux,Window都自動加
        bw.write("啦啦啦");
        //都寫入到了緩衝區中
        bw.close();//自動重新整理到檔案中
    }
}

字元緩衝輸入流

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\17388\\Desktop\\1.txt"));
        String line;
        while((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

從螢幕讀入字串
  首先我們將位元組流轉換為字元流,這樣就可以以字元為單位進行傳輸,在使用 BufferedReader 進行包裝,就可以使用 readLine 來讀取一行字串。將標準輸入讀入到緩衝區中,然後我們就可以從緩衝區中獲取到相應的資料。

public class Main {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(System.in));
            String s = null;
            while ((s = br.readLine()) != null) {
                System.out.println(s);
                if ("exit".equals(s)) break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

轉換流

  將位元組流轉換成字元流

OutputStreamWriter 與 InputStreamReader



位元組輸入流轉字元輸入流

public class Main {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("1.txt"), "GBK");
        osw.write("我是XXX");
        osw.close();
    }
}

位元組輸出流轉字元輸出流

public class Main {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\17388\\Desktop\\1.txt"), "GBK");
        int len = 0;
        while((len = isr.read()) != -1) {
            System.out.println((char) len);
        }
        isr.close();
    }
}

列印流

  可以重定向輸出,可以將資料直接輸出到檔案

PrintStream 與 PrintWriter

位元組列印流

public class Main {
    public static void main(String[] args) throws IOException {
        PrintStream ps = new PrintStream("C:\\Users\\17388\\Desktop\\1.txt");
//        ps.write(97);//a
//        ps.println(97);//97
//        ps.println(true);//true

        //重定向輸出
        System.setOut(ps);
        System.out.println("輸出到檔案");

        ps.close();
    }
}

字元列印流

public class Main {
    public static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter("C:\\Users\\17388\\Desktop\\1.txt");
        pw.write(97);//a
        pw.println(97);//97
        pw.println(true);//true
        pw.close();
    }
}

序列化流

  序列化:將物件以流的形式儲存在檔案中
  反序列化:將檔案中儲存的物件,以流的形式讀取出來
  首先我們需要自定義一個類,然後實現 \(Serializable\) 序列化介面,這樣類物件才能夠被序列化。
  如果成員變數被 transient 或者 static 關鍵字修飾,那麼這個成員變數將不能被序列化,如果沒有類沒有繼承序列化介面,那麼在進行序列化的時候會報序列化異常,如果我們在類物件中添加了一個唯一的 serialVersionUID,那麼如果我們在新增成員變數之後我們依然可以反序列化出這個物件,因為新增一個唯一標識以後表明類沒有改變,如果沒有這個唯一的 serialVersionUID,那麼就表明這個類不是原來的那個 Person 類,從而物件也不能用原來的檔案進行反序列化。

ObjectOutputStream 與 ObjectInputStream

序列化

//序列化單個物件
public class Main {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\17388\\Desktop\\1.txt"));
        //無法查閱檔案,亂碼
        oos.writeObject(new Person("sss", 18));
        oos.writeObject(new Person("yyy", 17));
        oos.writeObject(new Person("lll", 16));
        oos.close();
    }
}

//序列化多個物件
public class Main {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\17388\\Desktop\\1.txt"));
        Person[] persons = {new Person("sss", 18), new Person("yyy", 17), new Person("lll", 16)};
        oos.writeObject(persons);
        oos.close();
    }
}

反序列化

//反序列化單個物件
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\17388\\Desktop\\1.txt"));
        Object object = ois.readObject();
        ois.close();
        System.out.println(object);
    }
}

//反序列化多個物件
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\17388\\Desktop\\1.txt"));
        Person[] persons = (Person[]) ois.readObject();
        ois.close();
        for (Person person : persons) {
            System.out.println(person);
        }
    }
}