File與IO基礎

卡斯特梅的雨傘發表於2021-12-02

IO流的作用:持久化到磁碟

File類的使用

File類基本概念

  • 檔案和資料夾都是用File類來表示。
  • File類是記憶體層面的物件,記憶體中建立出來的File物件不一定有一個真實存在的檔案或資料夾,但是磁碟中真實的檔案或資料夾必須建立一個對應的File物件才能操作。
  • File檔案可進行增刪改查,但不能訪問檔案本身的內容,需要使用輸入/輸出流來進行訪問。
  • File物件一般作為流物件的構造器引數傳入。如FileInputStream fileInputStream = new FileInputStream(new File("hello.tex"));

File物件的建立

注意:資料夾和檔案如果在磁碟中都沒有的話,要分開分別建立資料夾和檔案。

構造器

檔案建立.png

分隔符

分隔符.png

示例小結
public class FileTest {

    /**
     * 檔案建立的字串引數意義
     * @throws FileNotFoundException
     */
    @Test
    public void test1() throws FileNotFoundException {
        //windows環境下用\\來作為資料夾分隔符,之所以用\\是因為\是轉義符的意思,所有\\表示不轉義
        File fileA1 = new File("F:\\newwork\\roy-practice-metaverse\\hello-io.txt");
        //windows環境下也可以像linux環境一樣用/表示
        File fileA2 = new File("F:/newwork/roy-practice-metaverse/hello-io.txt");
        //Java提供了File.pathSeparator來拼接路徑分隔符,用於讀取不同的系統的路徑分隔符
        File fileA3 = new File("F:" + File.pathSeparator + "newwork" + File.pathSeparator + "roy-practice-metaverse" + File.pathSeparator + "hello-io.txt");
        //相當於當前module模組下的hello-io.txt,如果是main方法則是當前工程下的hello-io.txt
        File fileR = new File("hello-io.txt");
    }

    /**
     * 檔案建立的3種構造器
     * @throws FileNotFoundException
     */
    @Test
    public void test2() throws FileNotFoundException {
        //構造器1:File(String pathname)
        File file1 = new File("F:/newwork/roy-practice-metaverse/hello-io.txt");
        //構造器2:File(String parent, String child)
        //child可以是檔案或資料夾名稱
        File file2 = new File("F:/newwork","roy-practice-metaverse");
        //構造器3:File(File parent, String child)
        //child可以是檔案或資料夾名稱
        File file3 = new File(file2,"hello-io.txt");
    }
}

File類的常用方法

查詢方法

注意:File類中的檢視方法操作不會因為檔案或資料夾不存在而報錯,因為還都是記憶體層面的操作,沒有涉及磁碟檔案。

file命令.png

示例小結
    /**
     * File類的常用方法
     * @throws FileNotFoundException
     */
    @Test
    public void test3() throws FileNotFoundException {
        File file1 = new File("hi-io.txt");//檔案存在的情況
        System.out.println(file1.getAbsolutePath());
        System.out.println(file1.getPath());
        System.out.println(file1.getName());
        System.out.println(file1.getParent());
        System.out.println(file1.length());//預設值0
        System.out.println(file1.lastModified());//預設值0
        System.out.println("------------------------------------");
        File file2 = new File("F:/newwork/roy-practice-metaverse/hello-io.txt");
        System.out.println(file2.getAbsolutePath());
        //這個path的返回值就是我們建立file物件時傳入的引數,引數是絕對路徑就返回絕對路徑,否則就是相對路徑名
        System.out.println(file2.getPath());
        System.out.println(file2.getName());
        System.out.println(file2.getParent());
        System.out.println(file2.length());
        System.out.println(file2.lastModified());
    }
    /**
     * F:\newwork\roy-practice-metaverse\hi-io.txt
     * hi-io.txt
     * hi-io.txt
     * null
     * 0
     * 0
     * ------------------------------------
     * F:\newwork\roy-practice-metaverse\hello-io.txt
     * F:\newwork\roy-practice-metaverse\hello-io.txt
     * hello-io.txt
     * F:\newwork\roy-practice-metaverse
     * 0
     * 0
     */

    /**
     * File類的常用方法-資料夾方法
     * @throws FileNotFoundException
     */
    @Test
    public void test4() throws FileNotFoundException {
        //資料夾遍歷時該資料夾必須存在,否則NPE
        File file1 = new File("F:/newwork/roy-practice-metaverse");
        String[] list = file1.list();
        for (String name : list) {
            System.out.println(name);
        }
        File[] files = file1.listFiles();
        for (File file : files) {
            System.out.println(file);
        }
        //移動檔案或者重新命名,要保證操作成功,需要 from在磁碟存在,而to不能存在這兩個條件
        File from = new File("hello-io.txt");
        File to = new File("new-io.txt");
        boolean success = from.renameTo(to);
        System.out.println(success);
    }
    /**
     * .idea
     * hello-io.txt
     * pom.xml
     * src
     * target
     * F:\newwork\roy-practice-metaverse\.idea
     * F:\newwork\roy-practice-metaverse\hello-io.txt
     * F:\newwork\roy-practice-metaverse\pom.xml
     * F:\newwork\roy-practice-metaverse\src
     * F:\newwork\roy-practice-metaverse\target
     * true
     *
     *
     * Process finished with exit code 0
     */

File類的判斷方法

file判斷命令.png

File類的建立刪除方法

file建立刪除命令.png

示例小結
    /**
     * File類的判斷功能
     *
     * @throws FileNotFoundException
     */
    @Test
    public void test5() throws FileNotFoundException {
        File existFile = new File("new-io.txt");
        System.out.println(existFile.isDirectory());
        System.out.println(existFile.isFile());
        System.out.println(existFile.exists());
        System.out.println(existFile.canRead());
        System.out.println(existFile.canWrite());
        System.out.println(existFile.isHidden());
        System.out.println("------------------------");
        File notExistFile = new File("notExist-new-io.txt");
        //當檔案不存在的時候,結果是既不是資料夾也不是檔案,所有如果有需要可以先判斷是否存在
        System.out.println(notExistFile.isDirectory());
        System.out.println(notExistFile.isFile());
        System.out.println(notExistFile.exists());
        System.out.println(notExistFile.canRead());
        System.out.println(notExistFile.canWrite());
        System.out.println(notExistFile.isHidden());
    }
    /**
     * false
     * true
     * true
     * true
     * true
     * false
     * ------------------------
     * false
     * false
     * false
     * false
     * false
     * false
     */

    /**
     * File類的建立刪除檔案/資料夾
     *
     * @throws FileNotFoundException
     */
    @Test
    public void test6() throws IOException {
        //建立刪除檔案
        File file = new File("create-file.txt");
        if (!file.exists()) {
            boolean flag = file.createNewFile();
            if (flag) {
                System.out.println("建立檔案成功");
            }
        } else {
            boolean delete = file.delete();
            if (delete) {
                System.out.println("刪除檔案成功");
            }
        }
        //建立刪除資料夾
        File file2 = new File("create-dir");
        if (!file2.exists()) {
            boolean flag = file2.mkdir();
            if (flag) {
                System.out.println("建立資料夾成功");
            }
        } else {
            boolean delete = file2.delete();
            if (delete) {
                System.out.println("刪除資料夾成功");
            }
        }
        //建立刪除多級資料夾
        File file3 = new File("create-dir1/sub-dir1");
        if (!file3.exists()) {
            //mkdirs可以遞迴建立資料夾,mkdir只能建立一級資料夾,如果有多級則建立不成功
            boolean flag = file3.mkdirs();
            if (flag) {
                System.out.println("建立資料夾成功");
            }
        } else {
            boolean delete = file3.delete();
            if (delete) {
                System.out.println("刪除資料夾成功");
            }
        }
        //刪除多級資料夾,只能刪除葉子節點的檔案或資料夾,不能級聯刪除
          //要想刪除成功,被刪除的資料夾下不能有子資料夾或者檔案,如果是檔案則檔案要存在。
//        File file4 = new File("create-dir1");
//        System.out.println(file4.delete());//false
    }

IO流原理及流的分類

基本概念

  • I/O是指Input/Output(輸入流/輸出流)的意思,用來處理裝置之間的資料傳輸。

  • 輸入輸出流的劃分是個相對的概念,我們在操作選擇輸入輸出流是站在程式或者說記憶體的角度上看流屬於輸入還是輸出。

  • 我們應該站在記憶體的角度上去看流,讀到記憶體中要找輸入流,從記憶體中讀到磁碟或者其他網路端叫輸出流。

IO流模板四步走

  1. File類物件建立
  2. 輸入輸出流的建立
  3. 讀取/寫入資料操作
  4. 關閉流資源

IO流的分類

  • 資料單位
  • 流向
  • 角色

流的分類.png
流的分類2.png

IO流體系

io流體系.png

節點流和處理流

基本概念

節點流.png

角色劃分.png

字元流和位元組流.png

流的選擇

  • 對於文字檔案(.txt .java .c ...),使用字元流處理。
  • 對於非文字檔案(.doc .ppt .jpg .avi ...),(只能)使用位元組流處理

注意:文字檔案操作讀取輸入流後展示(即在記憶體層面去讀時)時要使用字元流操作,避免會有亂碼的情況。當然如果只是讀取資料並寫入到檔案中,則都可以用。而非文字檔案即使沒有在記憶體層面去讀,直接寫入硬碟,也不能用字元流處理,否則檔案會開啟失敗。

示例小結

字元流

public class FileReaderWriterTest {

    /**
     * 通過字元輸入流讀取檔案資料列印到控制檯
     */
    @Test
    public void test1() {
        //注意,一開始寫程式碼時可以先丟擲異常,等後面整理時再針對性try-catch處理
        //1、例項化要操作的檔案物件
        FileReader fr = null;
        try {
            //如果讀取的檔案不存在會報檔案未找到異常
            File file = new File("new-io.txt");
            //2、建立具體的流
            fr = new FileReader(file);
            //3、資料的讀入/寫出
            //從輸入流中讀取資料的下一個位元組,沒有返回-1
            int data;
            while ((data = fr.read()) != -1) {
                //強轉為字元
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //在關閉流時都要先判斷字元輸入流是否不為空
            if (fr != null) {
                //關閉流
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 通過字元輸入流讀取檔案資料列印到控制檯-加緩充
     */
    @Test
    public void test2() {
        //注意,一開始寫程式碼時可以先丟擲異常,等後面整理時再針對性try-catch處理
        //1、例項化要操作的檔案物件
        FileReader fr = null;
        try {
            //如果讀取的檔案不存在會報檔案未找到異常
            File file = new File("new-io.txt");
            //2、建立具體的流
            fr = new FileReader(file);
            //3、資料的讀入/寫出
            //從輸入流中讀取資料的下一個位元組,沒有返回-1
            int len;
            char[] cbuff = new char[1024];
            //一次讀取多個可以提供效率,len是每次讀取到cbuff陣列中的個數,沒有則返回-1
            while ((len = fr.read(cbuff)) != -1) {
                //注意長度是len而不是陣列cbuff的長度
                //接收方式1:
                System.out.print(new String(cbuff, 0, len));
                //接收方式2:
                /*for (int i = 0; i < len; i++) {
                    System.out.print(cbuff[i]);
                }*/
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //在關閉流時都要先判斷字元輸入流是否不為空
            if (fr != null) {
                //關閉流
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 從記憶體中通過字元輸出流寫檔案到硬碟中
     * 輸出操作的檔案可以不存在,不存在時會自動建立此檔案。
     * 建立字元輸出流時會有個append引數,預設是false,表示如果檔案存在則覆蓋
     * 如果設定append引數=true,表示如果檔案存在則會追加資料在檔案裡的末尾。
     */
    @Test
    public void test3() {
        FileWriter fw = null;
        try {
            //1、例項化要操作的檔案物件
            File file = new File("out-io.txt");
            //2、建立具體的流
            //FileOutputStream(File file, boolean append) append=false表示覆蓋,true表示在檔案末尾追加
            fw = new FileWriter(file);
            //3、資料的寫出
            //直接寫字串,\n表示換行符
            fw.write("everybody wants to rule the world!\n");
            //也可以寫字元陣列
            fw.write("不是哦,我只是個良民,活著就好!".toCharArray());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fw != null) {
                //4、關閉流資源
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 讀取硬碟中的檔案到新檔案中(複製)-字元流版
     */
    @Test
    public void test5() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1、例項化要操作的讀入/寫出檔案物件
            File srcFile = new File("new-io.txt");
            File destFile = new File("new-io-copy.txt");
            //2、建立輸入流和輸出流
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);
            //3、資料的讀入和寫出操作
            int len;
            char[] buff = new char[1024];
            while ((len = fr.read(buff)) != -1) {
                //注意要讀多少個寫出多少長度len
                fw.write(buff, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、關閉流資源
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

位元組流

public class FileInOutPutStreamTest {

    /**
     * 讀取硬碟中的檔案到新檔案中(複製)-位元組流版
     */
    @Test
    public void test() {
        copyFile("是大臣.jpg", "是大臣-copy.jpg");
    }

    public void copyFile(String srcPath, String destPath) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1、例項化要操作的讀入/寫出檔案物件
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
//            File srcFile = new File("new-io.txt");
//            File destFile = new File("new-io-copy.txt");
            //2、建立輸入流和輸出流
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            //3、資料的讀入和寫出操作
            int len;
            byte[] buff = new byte[1024];
            while ((len = fis.read(buff)) != -1) {
                //注意要讀多少個寫出多少長度len
                fos.write(buff, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、關閉流資源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

緩衝流——處理流的一種

處理流就是套接在已有流的基礎上的流。

作用:提高流的讀寫速度。

原因:內部提供了一個預設8K的緩衝區。緩衝區大小我們可以在構造器中指定大小。

圖片加密.png

示例小結

public class BufferedTest {

    /**
     * 使用快取流進行復制
     */
    @Test
    public void test1() {
        //971M耗時1444ms 測試後如果指定的buff緩衝區越大理論上越好,因為順序寫速度更快
        long start = System.currentTimeMillis();
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1、例項化要操作的讀入/寫出檔案物件
            File srcFile = new File("E:/mv/[電影天堂www.dygod.com]倩女幽魂3:道道道BD國語中字.rmvb");
            File destFile = new File("nxq.rmvb");
            //2.1、建立輸入流和輸出流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.1、建立快取流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos, 8192);//1444ms
//            bos = new BufferedOutputStream(fos, 8192 / 2);//1952ms
//            bos = new BufferedOutputStream(fos, 8192 * 2);//1202ms
//            bos = new BufferedOutputStream(fos, 8192 * 8);//1101ms
            //3、資料的讀入和寫出操作
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
                //強制重新整理緩衝區,會把緩衝區的資料重新整理到磁碟中
//                bos.flush();
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、關閉流資源 關閉原則,先開啟的後關閉,關閉外層包裝流時內層流也會關閉,因此就不需要另外關閉
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


    /**
     * 使用快取字元流進行文字複製
     */
    @Test
    public void test2() {
        //971M耗時1444ms 測試後如果指定的buff緩衝區越大理論上越好,因為順序寫速度更快
        long start = System.currentTimeMillis();
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //1、例項化要操作的讀入/寫出檔案物件
            //2.1、建立輸入流和輸出流
            //2.1、建立快取流
            br = new BufferedReader(new FileReader(new File("new-io.txt")));
            bw = new BufferedWriter(new FileWriter(new File("new-io-copy3.txt")));
            //3、資料的讀入和寫出操作
            //字元流讀寫的是字元,所以用字元容器
            //方式一:使用char陣列
           /* char[] buffer = new char[1024];
            int len;
            while ((len = br.read(buffer)) != -1) {
                bw.write(buffer, 0, len);
                //強制重新整理緩衝區,會把緩衝區的資料重新整理到磁碟中
//                bw.flush();
            }*/
            //方式二:使用String
            String data;
            while ((data = br.readLine()) != null) {
//                bw.write(data+"\n");
                //如果沒有換號符,則會寫在一起
                bw.write(data);
                bw.newLine();
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、關閉流資源
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

轉換流

基本概念

  • 轉換流屬於字元流,看流的歸屬看字尾。

  • InputStreamReader將位元組流轉換成字元流,並解碼(位元組、位元組陣列--->字元、字元陣列)。

  • OutputStreamWriter將字元流轉換成位元組流,並編碼(字元、字元陣列--->位元組、位元組陣列)。

  • 編碼解碼過程需要字符集進行處理。

轉換流.png

轉換流2.png

字符集

ASCII:因為只要表示字母這些所有一個位元組就夠了。

GBK,UTF-8都相容了ASCII碼錶,所有在把原本是UTF-8的文字解碼GBK時對於英文的部分也不會有什麼問題,但中文部分就會亂碼。

GBK,GBK2312是通過首字元是0表示一個位元組程式碼一個字元;首字元是1表示需要讀取2個位元組表示一個字元來規範編碼的,即用最高位是1或0來表示兩個位元組或一個位元組。

字符集.png
字符集3.png
字符集4.png

應用

編碼和解碼。

image.png

示例小結

public class TransferIOTest {

    /**
     * 轉換流讀檔案列印
     */
    @Test
    public void test1() {
        //1、建立位元組流物件
        InputStreamReader isr = null;
        try {
            FileInputStream fis = new FileInputStream("new-io.txt");
            //沒有指定解碼的字符集的話就使用預設的字符集
//        InputStreamReader isr = new InputStreamReader(fis);
            //2、建立字元轉換流
            isr = new InputStreamReader(fis, "UTF-8");
            //3、解碼讀取
            char[] buff = new char[1024];
            int len;
            while ((len = isr.read(buff)) != -1) {
                //因為我們使用了檔案原本的UTF-8字符集編碼格式,所有在列印時就不會出現解碼出現亂碼的問題。
                System.out.println(new String(buff, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isr != null) {
                //4、關閉流資源
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * hello,my io
     * 我要被強轉了
     * hello,my io
     * 我要被強轉了
     * hello,my io
     * 我要被強轉了我要被強轉了我要被強轉了我要被強轉了我要被強轉了
     */

    /**
     * 轉換流讀UTF-8檔案儲存成GBK檔案
     */
    @Test
    public void test2() {
        //1、建立位元組流物件
        InputStreamReader isr = null;
        OutputStreamWriter osr = null;
        try {
            FileInputStream fis = new FileInputStream("new-io.txt");
            FileOutputStream fos = new FileOutputStream("new-io-gbk.txt");
            //沒有指定解碼的字符集的話就使用預設的字符集
//        InputStreamReader isr = new InputStreamReader(fis);
            //2、建立字元轉換流
            //寫程式碼時必須要指定字符集格式
            isr = new InputStreamReader(fis, "UTF-8");
            osr = new OutputStreamWriter(fos, "GBK");

            //3、解碼讀取
            char[] buff = new char[1024];
            int len;
            while ((len = isr.read(buff)) != -1) {
                osr.write(buff,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isr != null) {
                //4、關閉流資源
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (osr != null) {
                //4、關閉流資源
                try {
                    osr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

其他流

標準輸入、輸出流

輸入輸出流.png

列印流

列印流.png

資料流

基本資料流.png

示例小結

public class OtherTest {

    /**
     * 利用標準輸入輸出流從鍵盤中讀取輸入,轉換成大寫列印到控制檯上
     * 注意idea不支援使用@Test測試方法讀取鍵盤的輸入,所以要改用main方法
     *
     * @param args
     */
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(System.in));
            String data = "";
            while (true) {
                System.out.println("請輸入:");
                data = br.readLine();
                if ("exit".equalsIgnoreCase(data)) {
                    System.out.println("再見!");
                    break;
                }
                System.out.println(data.toUpperCase());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 請輸入:
     * hhhh
     * HHHH
     * 請輸入:
     * exit
     * 再見!
     */

    /**
     * 列印流
     */
    @Test
    public void test1() {
        PrintStream ps = null;
        try {
            //建立列印輸出流,並設定自動重新整理,預設是false,自動重新整理當遇到換行符或者\n時會重新整理輸出緩衝區
            ps = new PrintStream(new FileOutputStream(new File("test-print.txt")),true);
            //把標準輸出流(列印到控制檯改成我們檔案),我們的log日誌記錄到文字中就是用的列印流PrintStream/PrintWriter
            System.setOut(ps);
            String str = "我就是來測試列印流的\n現在開始測試:abcedsakjfslkdjfdfskgjdfkg\nsdfsdfsdf\n結束";
            System.out.println(str);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

    /**
     * 資料流:讀取和寫出8種基本資料型別和字串
     */
    @Test
    public void test2() throws Exception {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
        dos.writeUTF("期望薪資");
        dos.writeInt(36000);
        dos.writeBoolean(true);
        dos.flush();
        dos.close();

        DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
        //注意讀取資料的時候要按照寫入的順序讀,否則會報錯
        System.out.println(dis.readUTF());
        System.out.println(dis.readInt());
        System.out.println(dis.readBoolean());
        dis.close();
    }
}

物件流

物件可序列化需滿足3個條件

  • 物件對應的類需要實現Serializable介面。
  • 需定義一個全域性常量serialVersionUID。
  • 物件內的所有屬性也必須是可序列化的,比如自定義的Address類,基本資料型別都是可序列號的。

序列化**:把記憶體中的物件轉換為二進位制流。這樣便可以將二進位制流持久化到磁碟或者傳輸到另一個網路節點上。

反序列化:將二進位制流還原成Java物件。

serialVersionUID的作用

  • 標識類的版本以便於版本相容。
  • 如果沒有定義serialVersionUID的話Java會根據類的內部細節自動生成,如果類的例項變數發生了修改,自動生成的serialVersionUID就會發生改變。
  • Java序列化機制是通過serialVersionUID來驗證版本的一致性,如果版本不一致則在反序列化時會報版本不一致異常。

物件流.png
物件流2.png

例項小結

ublic class ObjectInputOutputStreamTest {

    /**
     * 物件序列化
     *
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
        oos.writeObject(new Person("張三", 30, new Address("紫禁城1號")));
        oos.writeObject(new String("我是艾米"));
        oos.flush();
        oos.close();
    }

    /**
     * 物件反序列化
     *
     * @throws Exception
     */
    @Test
    public void test2() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
        //需按照寫入的物件順序讀取,否則會報錯
        Person person = (Person) ois.readObject();
        String str = (String) ois.readObject();
        System.out.println(person);
        System.out.println(str);
        ois.close();
    }
    /**
     * Person(name=張三, age=30, address=Address(location=紫禁城1號))
     * 我是艾米
     */
}

/**
 * 物件可序列化需滿足3個條件:
 * 物件對應的類需要實現Serializable介面
 * 需定義一個全域性常量serialVersionUID
 * 物件內的所有屬性也必須是可序列化的,比如自定義的Address類,基本資料型別都是可序列號的。
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Serializable {
    private static final long serialVersionUID = -6849344724054667710L;

    private String name;
    private int age;
    private Address address;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address implements Serializable {
    private static final long serialVersionUID = -684934470754667710L;
    private String location;
}

隨記存取檔案流——斷點續傳

image.png

image.png
image.png

image.png

例項小結

public class RandomAccessFileTest {

    /**
     * 隨機存取檔案-讀檔案寫到新的檔案中
     */
    @Test
    public void test1() {
        RandomAccessFile read = null;
        RandomAccessFile write = null;
        try {
            //只讀
            read = new RandomAccessFile(new File("new-io.txt"), "r");
            //可讀可寫
            write = new RandomAccessFile(new File("test.txt"), "rw");

            byte[] buff = new byte[1024];
            int len;
            while ((len = read.read(buff)) != -1) {
                write.write(buff, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (read != null) {
                try {
                    read.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (write != null) {
                try {
                    write.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用RandomAccessFile實現資料的插入效果
     */
    @Test
    public void test2() throws Exception {
        //可讀可寫
        RandomAccessFile raf = new RandomAccessFile(new File("test.txt"), "rw");
        //將指標調到角標為3的位置,0開始。
        raf.seek(3);
        //儲存角標3後的資料,然後再實現插入,最後再把角標3後位置的資料繼續追加進來
        //用位元組陣列輸出流來儲存資料避免亂碼
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int len;
        while ((len = raf.read(buff)) != -1) {
            baos.write(buff, 0, len);
        }
        //調回要插入位置的指標
        raf.seek(3);
        raf.write("哈哈哈我插入進來了".getBytes());
        raf.write(baos.toString().getBytes());
        raf.close();
        baos.close();
    }
}

NIO.2中Path、Paths、Files類的使用

image.png
image.png
image.png
image.png
image.png

第三方common-io實現IO讀寫

依賴

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

示例

public class CommonsIOTest {

    @Test
    public void test() throws IOException {
        File srcFile = new File("是大臣.jpg");
        File destFile = new File("是大臣-copy3.jpg");
        FileUtils.copyFile(srcFile, destFile);
    }
}

參考

Java入門視訊教程

577-617

相關文章