一些關於IO流的知識點

yu_lu發表於2024-06-26

IO操作

Flie類

  1. 介紹:

    File物件表示路徑,可以是檔案、也可以是資料夾。

    這個路徑可以是存在的,也可以是不存在的

    絕對路徑是帶磁碟機代號的。相對路徑是不帶磁碟機代號的,預設到當前專案下去找

  2. File物件代表磁碟中實際存在的檔案和目錄。透過以下構造方法建立一個File物件:

    • 透過給定的父抽象路徑名和子路徑名字串建立一個新的File例項。
    File(File parent, String child);
    File(String parent,String child);
    
    • 把字串表示的路徑
    File(String pathname) ;
    
  3. 建立File物件成功後,可以使用以下列表中的方法操作檔案。

    • public String getName():返回由此抽象路徑名錶示的檔案或目錄的名稱。
    • public String getPath():將此抽象路徑名轉換為一個路徑名字串。
    • public boolean canRead():測試應用程式是否可以讀取此抽象路徑名錶示的檔案。
    • public boolean canWrite():測試應用程式是否可以修改此抽象路徑名錶示的檔案
    • public boolean exists():測試此抽象路徑名錶示的檔案或目錄是否存在
    • public boolean isDirectory():測試此抽象路徑名錶示的檔案是否是一個目錄。
    • public boolean isFile():測試此抽象路徑名錶示的檔案是否是一個標準檔案
    • public long lastModified():返回此抽象路徑名錶示的檔案最後一次被修改的時間。
    • public boolean mkdir():建立此抽象路徑名指定的目錄。
    • public boolean renameTo(File dest): 重新命名此抽象路徑名錶示的檔案
    • public boolean setReadOnly():標記此抽象路徑名指定的檔案或目錄,以便只可對其進行讀操作。
  4. createNewFile 建立一個新的空的檔案

    1. 如果當前路徑表示的檔案是不存在的,則建立成功,方法返回true,如果當前路徑表示的檔案是存在的,則建立失敗,方法返回false
    2. 如果父級路徑表示的檔案是存在的,那麼方法會有異常IOException
    3. createNewFile方 法建立的一定是檔案,如果路徑中不包含字尾名,則建立一個沒有字尾的檔案。
  5. mkdirs 建立多級資料夾

    1. windows當中路徑是唯一的,如果當前路徑已經存在,則建立失敗,返回false
    2. midir方法只能建立單級資料夾,無法建立多級資料夾。
    3. midirs既可以建立單級資料夾,也可以建立多級資料夾。
  6. delete方法

    1. 如果刪除的是檔案,則直接刪除,不走回收站
    2. 如果刪除的是空資料夾,則直接刪除,不走回收站。
    3. 如果刪除的是有內容的檔案,則刪除失敗。
  7. listFiles()方法

    • 當呼叫者File表示的路徑不存在時,返回null
    • 當呼叫者File表示的路徑時檔案時,返回null
    • 當呼叫者File表示的路徑時一個空檔案時,返回一個長度為0的陣列
    • 當呼叫者File表示的路徑時一個有內容的資料夾時,將裡面所有檔案和資料夾的路徑放在File陣列中放回
    • 當呼叫者File表示的路徑是一個有隱藏檔案的資料夾時,將裡面所有檔案和資料夾的路徑放在File陣列中返回,包含隱藏檔案
    • 當呼叫者File表示的路徑時需要許可權才能訪問的資料夾時,返回null

IO流

  1. IO的初步瞭解

    1. 什麼是io流

      儲存和讀取資料的解決方案

      I:input O : output

    2. IO流的作用?

      用於讀寫資料(本地檔案,網路)

    3. IO流按照流向可以分類哪兩種流?

      輸出流:程式-> 檔案

      輸入流:檔案-> 程式

    4. IO流按照操作檔案的型別可以分類哪兩種流?

      位元組流:可以操作所有型別的檔案

      字元流:只能操作純文字檔案

    5. 什麼是純文字檔案?

      用windows系統自帶的記事本開啟並且能夠讀懂的檔案

      txt檔案,md檔案,xml檔案,lrc檔案等

      純文字檔案可以使用字元流去讀取操作,而非純文字檔案如word,excel檔案不能使用字元流,需要使用位元組流。

  2. 位元組輸出流的細節

    1. 建立位元組輸出流物件
      • 引數是字串表示的路徑或者是File物件都是可以的
      • 如果檔案不存在會建立一個新的檔案,但是要保證父級路徑是存在的
      • 如果檔案已經存在,則會清空檔案
    2. 寫資料:
      • write方法的引數是整數,但是實際上寫到本地檔案中的是整數在ASCII上對應的字元
    3. 釋放資源:
      • 每次使用完流之後都要釋放資源,解除資源的佔用
  3. 位元組輸入流的細節:

    1. 建立位元組輸入流物件
      • 如果檔案不存在,就直接報錯
    2. 寫資料
      • 一次讀一個位元組,讀出來的是資料在ASCII上對應的數字’
      • 讀到檔案末尾了,read方法返回-1
    3. 釋放資源
      • 每次使用完流之後都要釋放資源,解除資源佔用
  4. copy大檔案時

    public class ByteStreamDemo3 {
        public static void main(String[] args) throws IOException {
            FileInputStream fileInputStream = new FileInputStream("D:\\BaiduNetdiskDownload\\影片.mp4");
            FileOutputStream fileOutputStream = new FileOutputStream("data/copy.mp4");
            int len ;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fileInputStream.read(bytes))!=-1){
                fileOutputStream.write(bytes,0,len);
            }
            fileOutputStream.close();
            fileInputStream.close();
        }
    }
    
  5. 如何不產生亂碼?

    1. 不要用位元組流讀取文字檔案
    2. 編碼解碼時使用同一個碼錶,同一個編碼方式
  6. 字元流讀取

    1. 空參讀取
      • 預設也是一個位元組一個位元組的讀取的,如果遇到中文就會一次讀取多個,在讀取之後,方法的底層還會進擊解碼並轉成十進位制
    2. 有參讀取
      • 讀取資料,解碼,強轉三步合併了,把強轉之後的字元放到陣列當中。相當於空參的read+強轉型別轉換。
  7. 位元組流和字元流的使用場景

    1. 位元組流:
      • 複製任意型別的檔案
    2. 字元流:
      • 讀取純文字檔案中的資料
      • 往純文字檔案中寫出資料
  8. 實列從一個資料夾複製到另一個資料夾

    public class test1 {
        public static void main(String[] args) throws IOException {
            File src = new File("D:\\蘭智數加\\aaa\\src");
            File dest = new File("D:\\蘭智數加\\aaa\\dest");
            copydir(src,dest);
        }
    
        private static void copydir(File src, File dest) throws IOException {
            dest.mkdirs();
            // 1.進入資料來源
            File[] files = src.listFiles();
            // 2.遍歷陣列
            for (File file : files) {
                //3.判斷是否為檔案,然後複製
                if(file.isFile()){
                    FileInputStream fileInputStream = new FileInputStream(file);
                    FileOutputStream fileOutputStream = new FileOutputStream(new File(dest, file.getName()));
                    byte[] bytes = new byte[1024];
                    int len;
                    while ((len=fileInputStream.read(bytes))!=-1){
                        fileOutputStream.write(bytes,0,len);
                    }
                    fileOutputStream.close();
                    fileInputStream.close();
                }else {
                    //4.判斷是否為資料夾,然後遞迴
                    copydir(file,new File(dest,file.getName()));
                }
            }
        }
    }
    
  9. 緩衝流

    1. 緩衝流有幾種?
      • 位元組緩衝輸入流:BufferedInputStream
      • 位元組緩衝輸出流:BufferedOutputStream
      • 字元緩衝輸入流:BufferedReader
      • 字元緩衝輸出流:BufferedWriter
    2. 緩衝流為什麼能提高效能
      • 緩衝流自帶長度為8192的緩衝區
      • 可以顯著提高位元組流的讀寫效能
      • 對於字元流提升不明顯,對於字元緩衝流而言關鍵點是兩個特有的方法
    3. 字元緩衝流兩個特有的方法是什麼?
      • 字元緩衝輸入流 readLine()
      • 字元緩衝輸出流newLine()

    下面是一個緩衝流的簡單應用

    import java.io.*;
    
    public class BufferedStreamDemo1 {
        /*TODO
            節約的時間是在讀和寫時與硬碟打交道的時間
         */
        public static void main(String[] args) throws IOException {
            // 建立緩衝流讀取資料
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("data/a.txt"));
            // 建立緩衝流寫出資料
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("data/copy.txt"));
            int readLine;
            while ((readLine = bis.read()) != -1){
                bos.write(readLine);
            }
            bos.close();
            bis.close();
        }
    }
    
  10. 轉換流

    在 Java 中,轉換流主要用於在位元組流和字元流之間進行轉換。常用的轉換流類有 InputStreamReaderOutputStreamWriter。這些類提供了將位元組流轉換為字元流,或者將字元流轉換為位元組流的功能。這對於處理不同編碼格式的資料特別有用。

    // 這是一個簡單轉換流應用
    import java.io.*;
    
    public class ConvertStreamDemo1 {
        public static void main(String[] args) throws IOException {
            // 建立位元組輸入流讀取資料
            FileInputStream fileInputStream = new FileInputStream("data/GBKfileText.txt");
            // 建立轉換流 讀取GBK檔案的內容
            InputStreamReader isr = new InputStreamReader(fileInputStream, "GBK");
            // 建立緩衝流
            BufferedReader br = new BufferedReader(isr);
            // 建立位元組輸出流寫出資料
            FileOutputStream fileOutputStream = new FileOutputStream("data/GBKfile.txt");
            // 建立轉換流寫出GBK檔案
            OutputStreamWriter osw = new OutputStreamWriter(fileOutputStream, "GBK");
            // 建立緩衝流
            BufferedWriter bw = new BufferedWriter(osw);
            // 讀取資料並寫出到新的地址
            String line;
            while ((line = br.readLine()) != null){
                bw.write(line);
                bw.newLine();
            }
            bw.close();
            br.close();
        }
    }
    
  11. 序列化流

    序列化流在 Java 中用於將物件的狀態轉換為位元組流,以便可以將物件的狀態儲存到檔案中、透過網路傳輸或儲存在資料庫中。反之,反序列化流則用於將位元組流恢復為物件。

    序列化是將物件的狀態轉換為位元組流的過程,反序列化是將位元組流恢復為物件的過程。在 Java 中,序列化是透過實現 Serializable 介面來實現的。

    • ObjectOutputStream:用於將物件寫入輸出流(序列化)。
    • ObjectInputStream:用於從輸入流讀取物件(反序列化)。

    下面是一個簡單的應用:

    #  建立一個標準的Student類
        
    import java.io.Serializable;
    
    /**
     * Serializable介面裡面沒有抽象方法,是標記型介面
     * 一旦實現這個介面,那麼就表示當前的Student類可以被序列化
     * 理解:
     *      可以是一個物品的合格證
     */
    public class Student implements Serializable {
        private String name;
        private int age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public Student(){}
    
        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 "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
    
    # 建立一個序列化流把檔案寫道本地
        
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    
    public class ObjectStreamDemo1 {
        public static void main(String[] args) throws IOException {
            /*
                需求:
                    利用序列化流/物件操作輸出流,把一個物件寫本地檔案中
                構造方法:
                    public ObjectOutputStream(OutputStream out)把基本流變成高階流
                成員方法:
                    public final void writeObject(Object obj) 把物件序列化(寫出)到檔案中去
             */
            // 建立物件
            Student student = new Student("EzReal", 23);
            // 建立序列化流的物件/物件操作輸出流
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data/a.txt"));
            // 寫出資料
            oos.writeObject(student);
            // 釋放資源
            oos.close();
    
        }
    }
    
    # 建立一個反序列化流讀取檔案
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    
    public class ObjectStreamDemo2 {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            // 建立反序列化流的物件
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data/a.txt"));
            // 讀取資料
            Object o = ois.readObject();
            // 列印物件
            System.out.println(o);
            // 釋放資源
            ois.close();
    
        }
    }
    

    序列化流/反序列化流的細節彙總

    1. 使用序列化流將物件寫到檔案時,需要讓Javabean類實現Serializable介面。否則,會出現notSerializableException異常
    2. 序列化流寫到檔案中的資料是不能被修改的,一旦修改了就無法再次讀取回來了
    3. 序列化物件後,修改了JavaBean類,再次反序列化,會不會有問題?
      • 會出問題,會丟擲InvalidClassException異常
      • 解決方案:給javabean類新增SerialVersionUID(序列號、版本號)
    4. 如果一個物件的某個成員變數的值不想被序列化,又該怎麼實現呢?
      • 解決方案:給該成員變數加transient關鍵字修飾,該關鍵字標記的成員變數不參與序列化過程

    使用序列化流一次讀取多個物件

    // 序列化流
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    
    public class ObjectStreamTestOutput {
        public static void main(String[] args) throws IOException {
            Student stu1 = new Student("zhangsan", 21, "nanjin");
            Student stu2 = new Student("lisi", 22, "shanghai");
            Student stu3 = new Student("wangwu", 23, "shenzhen");
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data/a.txt"));
            oos.writeObject(stu1);
            oos.writeObject(stu2);
            oos.writeObject(stu3);
    
            oos.close();
        }
    }
    
    // 反序列化流
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.util.ArrayList;
    
    public class ObjectStreamTestInput {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data/a.txt"));
            ArrayList<Student> stuList = new ArrayList<>();
            try {
                while (true){
                    Student stu = (Student)ois.readObject();
                    stuList.add(stu);
                }
            }catch (Exception e){
                System.out.println("檔案已經讀取到末尾,開始列印資料:");
            }
            System.out.println(stuList);
            ois.close();
        }
    }
    

相關文章