Java IO 的基本劃分和代碼示例

代碼演示需要提前在項目根目錄下創建一個 file.txt ,並在其中輸入一些信息
不習慣這樣看代碼的話,我把代碼上傳到了 github 上:https://github.com/Mr1wangjiabin/java-io

IO流的劃分

按數據來源或者操作對象劃分

在這裏插入圖片描述

按數據傳輸方式或者說是運輸方式劃分

在這裏插入圖片描述

文本、文件、文本文件

  • 文本:Java 的文本是 16 位無符號整數,是字符的 unicode 編碼
  • 文件:文件時 byte byte byte ... 的數據序列
  • 文本文件:是文本按照某種編碼方式序列化爲 byte 的存儲結果

編碼

  • GBK
  • UTF-8
  • UTF-16be
public class EncodeDemo {
    public static void main(String[] args) throws Exception {
        /**
         * 文本文件,可以是任意編碼的字節序列
         * 如果我們在中文機器上直接創建文本文件,那麼該文件只認識ANSI編碼
         *
         * 字節序列只能進行同種編碼間的傳輸,如果編碼方式不一致,會出現亂碼
         */
        //UTF-8 輸出e6 b5 8b e8 af 95 41 42 43
        //一個漢字 3 個字節,字母 1 個字節
        String s = "測試ABC";
        byte[] bytes1 = s.getBytes();//轉換成字節是項目默認的編碼 UTF-8
        System.out.println("UTF-8編碼:");
        for (byte b : bytes1) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");//將字節轉換爲 16進制 int型輸出
        }
        System.out.println();

        //GBK輸出b2 e2 ca d4 41 42 43 
        //一個漢字 2 個字節,字母 1 個字節
        System.out.println("GBK編碼:");
        byte[] bytes2 = s.getBytes("gbk");
        for (byte b : bytes2) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        System.out.println();

        //UTF-16be 輸出6d 4b 8b d5 0 41 0 42 0 43 
        //一個漢字 2 個字節,字母 2 個字節
        System.out.println("UTF-16be編碼:");
        byte[] bytes3 = s.getBytes("utf-16be");
        for (byte b : bytes3) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        //關閉流
        raf.close
    }
}

File

  • java.io.file 用來表示文件或者目錄
  • File 類用於表示文件(目錄)的信息(名稱、大小等)。不能用於文件內容的訪問

File 類的基本API

/**
 * File 的構造函數:
 *  File(filepath) filepath爲文件全目錄
 *  File(filepath,filepath1,filepath2...) filepath1爲filepath的子目錄
 * File.separator 文件分隔符
 * exists 驗證file是否存在
 * mkdir 創建一個文件夾/目錄
 * mkdir 創建多級目錄
 * delete 刪除文件
 * isDirectory 判斷file 是否是文件夾
 * isFile 判斷file 是否是文件
 * getAbsolutePath 獲取全路徑
 * getName 獲取文件名稱
 * createNewFile 創建一個新文件
 */
public class FileDemo {
    public static void main(String[] args) throws IOException {
        File file = new File("E:\\Project\\io\\test");
        File file1 = new File("E:" + File.separator
                + "Project" + File.separator
                + "io"+ File.separator
                + "test.text");

        if(!file.exists()){
            file.mkdir();
        }else {
            file.delete();
        }
        System.out.println(file.isDirectory());//判斷file 是否是文件夾
        System.out.println(file.isFile());//判斷file 是否是文件
        System.out.println(file.getAbsolutePath());//獲取全路徑
        System.out.println(file.getName());//獲取文件名稱

        if (!file1.exists()){
            file1.createNewFile();
        }else {
            file1.delete();
        }

    }
}

File 的 list 與 listFile

import java.io.File;

/**
 * list 返回目錄/文件下的所有文件名
 * listFiles 返回文件下的所有文件
 *
 * 練習,完成目錄下所有子目錄的查詢
 */
public class FileUtils {
    public static void main(String[] args) throws IllegalAccessException {
        File file = new File("E:/Project/io");
        FileUtils.listFile(file);

    }

    public static void listFile(File file) throws IllegalAccessException {
        //首先判斷文件是否存在以及文件是文件夾
        if(!file.exists()){
            throw new IllegalAccessException(file + " 不存在");
        }
        if (!file.isDirectory()){
            throw new IllegalAccessException(file + " 不是一個文件夾");
        }

        File[] files = file.listFiles();
        if (files.length > 0 && files != null){
            for (File file1 : files) {
                if (file1.isDirectory()){
                    FileUtils.listFile(file1);
                }else {
                    System.out.println(file);
                }
            }
        }
  }
}

RandomAccessFile

  • 即可以讀取文件內容,也可以向文件中寫入內容。但是和其他輸入/輸入流不同的是,程序可以直接跳到文件的任意位置來讀寫數據。
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;

/**
 ** java 文件模型
 *      在硬盤上是 byte byte 存儲的,是數據的集合
 *  打開文件
 *      有兩種模式:"rw"(讀寫) "r"(只讀)
 *      RandomAccessFile raf = new RandomAccessFile("rw")
 *      文件指針,打開文件時,文件指針在開頭 pointer = 0
 * 寫方法
 *      raf.writer() -->只寫一個字節(後8位)同時指針指向下一個位置,準備再次寫入
 * 讀方法
 *      int b = raf.read() --> 讀一個字節
 * 文件讀寫完成一定要關閉
 *
 * 其他API
 * getFilePointer 獲取指針當前所在位置
 *
 *
 */
public class RandomAccessFileDmo {
    public static void main(String[] args) throws IOException {
        //創建文件
        File file = new File("demo");
        if(!file.exists()){
            file.mkdir();
        }
        File file1 = new File(file, "raf.dat");
        if(!file1.exists()){
            file1.createNewFile();
        }

        //對文件的讀寫操作
        RandomAccessFile raf = new RandomAccessFile(file1, "rw");
        System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置

        //寫入一個字節
        //默認編碼UTF-8,一個漢字 3 個字節,一個字母 1 個字節
        raf.write('A');
        System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置

        int i = 0x7fffffff;
        //用write方法每次只能寫一個字節,如果要把i寫進去就得寫4次
        raf.write((i >>> 24) & 0xff);//右移 24 位,並清空其餘的0值
        raf.write((i >>> 16) & 0xff);
        raf.write((i >>> 8) & 0xff);
        raf.write((i) & 0xff);
        System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置

        //可以直接寫一個 int 型變量,底層實現和上面的方法一樣
        raf.writeInt(i);

        //寫一個漢字
        String s = "中";
        byte[] bytes = s.getBytes();
        raf.write(bytes);
        System.out.println("指針當前所在位置爲:" + raf.getFilePointer());//獲取指針當前所在位置

        //讀文件,必須把指針移動到頭部
        raf.seek(0);
        //把文件中的內容都讀取到字節數組中
        byte[] bytes1 = new byte[(int) raf.length()];
        raf.read(bytes1);

        System.out.println(Arrays.toString(bytes1));//將數組輸出
        //將字節數組轉換爲字符串輸出
        String s1 = new String(bytes1);
        System.out.println(s1);
    }
}

字節流

輸入輸出流

FileInputStream
  • 實現了在文件上讀取數據
package byteStream;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * 讀取指定文件內容,按照16進制輸出到控制檯。並且每輸出10個byte換行
 * 批量讀取
 */
public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        printHex("file.txt");
    }

    public static void printHex(String filename) throws IOException{
        FileInputStream fis = new FileInputStream(filename);
        byte[] bytes = new byte[8 * 1024];
        int b = 0;
        int j = 1;
        while ((b = fis.read(bytes,0,bytes.length)) != -1){
            for (int i = 0; i < b; i++) {
                System.out.print(Integer.toHexString(bytes[i] & 0xff) + "  ");
                if(((j++)%10)==0){
                    System.out.println();
                }
            }

        }

        fis.close();
    }

}

FileOutputStream
package byteStream;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 從文件中讀取數據
 */
public class FileOutPutStreamTest {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("out.dat");
        byte[] bytes = "你好".getBytes();
        fos.write(bytes);
        fos.close();

        FileInputStreamTest.printHex("out.dat");
    }
}

練習:實現文件的複製
package byteStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * 完成文件的複製
 */
public class FileCopyTest {

    public static void main(String[] args) throws Exception {
        fileCopy(new File("file.txt"),new File("fileout.txt"));
    }

    /**
     * 文件複製
     * 問什麼傳入參數不用文件名?
     *      需要判斷源文件是否存在
     * @param srcFile
     * @param destFile
     */
    public static void fileCopy(File srcFile,File destFile) throws Exception{
        if(!srcFile.exists()){
            throw new IllegalAccessException("文件" + srcFile + "不存在");
        }
        if(!srcFile.isFile()){
            throw new IllegalAccessException("文件" + srcFile + "不是文件");
        }

        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);

        byte[] bytes = new byte[8 * 1024];
        int b = 0;

        if((b = fis.read(bytes,0,bytes.length)) != -1){
            fos.write(bytes,0,b);
            fos.flush();
        }

        fis.close();
        fos.close();
    }
}

緩衝流 BufferedInputStream && BufferedOutputStream 進行文件複製

  • 提供了緩衝區操作,提高的IO的性能
package byteStream;

import java.io.*;

public class BufferedTest {
    public static void main(String[] args) throws Exception{
        copyFile(new File("E:\\Project\\io\\src\\main\\java\\file\\FileDemo.java"),
        new File("fileOut2.txt"));

    }

    public static void copyFile(File srcFile, File destFile) throws Exception{
        if(!srcFile.exists()){
            throw new IllegalAccessException("文件" + srcFile + "不存在");
        }
        if(!srcFile.isFile()){
            throw new IllegalAccessException("文件" + srcFile + "不是文件");
        }

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        int c;
        while ((c = bis.read())!= -1){
            bos.write(c);
            bos.flush();
        }

        bis.close();
        bos.close();

    }
}

字符流

  • 有了字節流爲什麼還需要字符流?
    字符流是由Java虛擬機將字節轉換得到的,問題就出在這個過程還算是非常耗時,並且,如果我們不知道編碼類型就很容易出現亂碼問題。所以,I/O 流就乾脆提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。
  • 字符的處理
    一次處理一個字符,底層仍然是最基本的字節序列

InputStreamReader && OutputStreamWriter 實現文件複製

  • InputStreamReader 按照編碼將 byte 流解析爲 char
  • OutputStreamWriter 默認項目的編碼,操作的時候要寫文件本身的編碼
package charStream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/**
 *  InputStreamReader && OutputStreamWriter 實現文件複製
 */
public class FileCopyTest {
    public static void main(String[] args) throws Exception{
        InputStreamReader isr = new InputStreamReader(new FileInputStream("file.txt"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("fileOut3.txt"));

        char[] chars = new char[8 * 1024];
        int c;

        while ((c = isr.read(chars,0,chars.length)) != -1){
            osw.write(chars,0,c);
            osw.flush();
        }

        isr.close();
        osw.close();
    }
}


FileReader && FileWriter 實現文件複製

package charStream;

import java.io.FileReader;
import java.io.FileWriter;

/**
 *  InputStreamReader && OutputStreamWriter 實現文件複製
 */
public class FileCopyByFileTest {
    public static void main(String[] args) throws Exception{
        FileReader fr = new FileReader("file.txt");
        FileWriter fw = new FileWriter("fileOut4.txt");

        char[] chars = new char[8 * 1024];
        int d;

        while ((d = fr.read(chars,0,chars.length)) != -1){
            fw.write(chars,0,d);
        }

        fr.close();
        fw.close();
    }
}

BufferedReader && BufferedWriter/PrintWriter 實現文件複製

  • PrintWriter 用起來比 BufferedWriter 方便不少,因此我不再寫 BufferedWriter相關代碼,感興趣的自己查吧
package charStream;

import java.io.*;

public class FileCopyByBuffer {
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("file.txt")));
        PrintWriter pw = new PrintWriter(
                new FileOutputStream("fileOut5.txt"),true);//在文件參數爲 OutputStream時,可以設置自動清空緩存

        String line;
        while ((line = br.readLine()) != null){
            pw.println(line);
        }

        br.close();
        pw.close();

    }
}

序列化與反序列化

  • 將對象轉換爲 byte 存儲稱爲序列化,反之爲反序列化
  • 序列化流 ObjectOutputStream 方法 writeObject
  • 反序列化流 ObjectInputStream 方法 readObject
  • 只有實現了 Serializable 接口的類才能序列化
  • 類中 transient 修飾的變量不會被 JVM 自動序列化,但可以手動,用以提高性能(這部分不懂,就簡單的提一下)
package SerializableTest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableTest {

    public static void main(String[] args) throws Exception{
        //對象序列化
        ObjectSerializable();
        //對象反序列化
        ObjectInSerializable();
    }

    /**
     * 對象序列化
     * @throws Exception
     */
    public static void ObjectSerializable() throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.dat"));
        Student student = new Student(1L, "小王");
        oos.writeObject(student);
        oos.flush();
        oos.close();
    }

    public static void ObjectInSerializable() throws  Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.dat"));
        Student student = (Student) ois.readObject();
        System.out.println(student);
        ois.close();
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章