Java—IO 流

🔍文件


文件, 计算机存储数据的一种方式

创建文件


下面列举了创建文件的 3 种方式, 包括

  1. File(String pathname), 指定路径名创建
  2. File(File parent, String child), 指定父抽象路径 + 子路径名创建
  3. File(String parent, String child), 指定父路径名 + 子路径名创建
public class FileCreate {

    public static void main(String[] args) {
        // create1();
        // create2();
        // create3();
    }

    // 创建方式 1, File(String pathname)
    public static void create1() {
        String pathName = "e:/test1.txt";
        File file = new File(pathName);
        try {
            boolean ret = file.createNewFile();
            if(ret) {
                System.out.println("文件创建成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 创建方式 2, File(File parent, String child)
    public static void create2() {
        String parentPath = "e:/";
        File parent = new File(parentPath);
        String child = "test2.txt";
        try {
            File file = new File(parent, child);
            boolean ret = file.createNewFile();
            if(ret) {
                System.out.println("文件创建成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 创建方式3, File(String parent, String child)
    public static void create3() {
        String parent = "e:/";
        String child = "test3.txt";
        try {
            File file = new File(parent, child);
            boolean ret = file.createNewFile();
            if(ret) {
                System.out.println("文件创建成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

获取文件相关信息


具体文章参考获取文件相关信息

  • file.getName(), 获取文件名
  • file.getAbsolutePath(), 获取文件绝对路径
  • file.getParent(), 获取文件父级目录
  • file.length(), 获取文件大小(单位: 字节)
  • file.exists(), 判断文件是否存在
  • file.isFile(), 判断是否为一个文件
  • file.isDirectory(), 判断是否为一个目录

目录相关操作


具体文章参考目录相关操作

  • file.exists(), 判断目录是否存在
  • file.delete(), 删除目录
  • file.mkdir(), 创建单级目录
  • file.mkdirs(), 创建多级目录

注意🍂

  • 执行删除目录命令时, 如果目录中存在文件, 则无法直接删除目录
  • 创建多级目录也可以应用于创建单级目录的情况

🔍IO 流


I, Input 的缩写, 表示输入
O, Output 的缩写, 表示输出

在这里插入图片描述

输入流, 数据从文件输入到程序(文件 → 程序)
输出流, 数据从程序输出到文件(程序 → 文件)

举个栗子🌰

你可以将程序看作是一个人, 将文件看作是水

在这里插入图片描述

将人喝水(水被输入到人的肚子中)的过程理解为输入流
人在喝水过程中被呛到了(水被重新洒到了杯子)的过程理解为输出流

理解流与文件


流与文件之间的关系类似于快递小哥与商品之间的关系

在这里插入图片描述

  • 将用户理解为程序
  • 将快递小哥送快递的过程理解为输入流 / 输出流
  • 将快递驿站理解为文件, 驿站中的快递(物品)理解为文件中的数据

快递小哥将快递(文件中的数据)送至用户家(程序) → 输入流
快递小哥将用户需要寄送的快递送至快递驿站 → 输出流

流负责数据的输入与输出

流的分类


  • 依据操作的对象划分为: (1) 字节流 (2) 字符流
  • 依据数据的流向划分为: (1) 输入流 (2) 输出流
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

在这里插入图片描述

FileInputStream


使用 FileInputStream 中的 read() 方法, 一次只能读取一个字节的内容
使用 FileInputStream 中的 read(byte[] b) 方法, 一次可以读取指定字节大小的内容

public class ReadFile {

    public static void main(String[] args) {
        // readFile1();
        // readFile2();
    }
	
	// 一次读取一个字节
    public static void readFile1() {
        String filePath = "e:/hello.txt";
        int readSize = 0;
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(filePath);
            // 从文件中一次读取一个字节的数据. 返回 -1, 表示读到文件末尾
            while((readSize = inputStream.read()) != -1) {
                System.out.print((char) readSize);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
	
	// 一次读取多个字节
    public static void readFile2() {
        String filePath = "e:/hello.txt";
        int readSize = 0;
        // 从文件中一次读取 8 个字节的数据. 返回 -1, 表示读到文件末尾
        byte[] buffer = new byte[8];
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(filePath);
            while((readSize = inputStream.read(buffer)) != -1) {
                // 注意不能写成 System.out.print(new String(buffer, 0, buffer.length));
                // 这是因为后续的 byte[] buffer 中的值由于第一次读取后的内容已经被填充
                System.out.print(new String(buffer, 0, readSize));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

FileOutputStream


使用 FileOutputStream 向指定的文件中写入内容时. 如果该文件不存在, 则会自动进行创建
使用 FileOutputStream 中的 write() 方法, 一次只能写入一个字节的内容
使用 FileOutputStream 中的 write(byte[] b) 方法, 一次可以写入指定字节大小的内容

FileOutputStream outputStream = new FileOutputStream(filePath) 再次执行程序时, 新写入的内容覆盖原有的内容
FileOutputStream outputStream = new FileOutputStream(filePath, true) 再次执行程序时, 新写入的内容会追加到原有内容的末尾

再次执行程序时, 指的是流被关闭后再次使用

public class WriteFile {

    public static void main(String[] args) {
        writeFile1();
        // writeFile2();
    }

    // 一次写入一个字节
    public static void writeFile1() {
        OutputStream outputStream = null;
        String filePath = "e:/test.txt";
        try {
            // 使用 FileOutputStream 向指定的文件中写入内容时
            // 如果该文件不存在, 则会自动进行创建
            outputStream = new FileOutputStream(filePath);
            // 当再次执行程序时, 新写入的内容覆盖原有的内容
            // outputStream = new FileOutputStream(filePath); 
            // 当再次执行程序时, 新写入的内容会追加到原有内容的末尾  
            // outputStream = new FileOutputStream(filePath, true); 
            outputStream.write('h');
            outputStream.write('e');
            outputStream.write('l');
            outputStream.write('l');
            outputStream.write('o');
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 一次写入多个字节
    public static void writeFile2() {
        OutputStream outputStream = null;
        String filePath = "e:/test.txt";
        try {
            // 使用 FileOutputStream 向指定的文件中写入内容时
            // 如果该文件不存在, 则会自动进行创建
            outputStream = new FileOutputStream(filePath);
            String str = "hello world";
            outputStream.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

文件拷贝


在这里插入图片描述

文件拷贝分为 2 步, 包括

  1. 读取源文件中的数据输入到程序(输入流)
  2. 从程序中输出数据到目标文件(输出流)

写入目标文件中的数据长度应该是读取到的长度, 不能是字节数组本身的长度
outputStream.write(buffer, 0, readSize)

注意🍂

拷贝文件时应每次读取部分数据时就写入目标文件, 不要一下子全部读取再写入目标文件
这是因为如果文件太大, 可能会导致执行过程崩溃

public class FileCopy {

    public static void main(String[] args) {
        copyFile();
    }

    // 文件拷贝
    // 1. 读取源文件的数据
    // 2. 将源文件的数据拷贝到目标文件路径
    public static void copyFile() {
        // srcFilePath 源文件路径(最好不要有中文)
        String srcFilePath = "D:/Csdn截图上传/Redis_01.png";
        // destFilePath 目标文件路径(最好不要有中文)
        String destFilePath = "E:/Redis2.png";
        InputStream inputStream = null;
        OutputStream outputStream = null;
        int readSize = 0;
        // 每次读取 1024 字节的数据
        byte[] buffer = new byte[1024];
        try {
            inputStream = new FileInputStream(srcFilePath);
            // 使用 FileOutputStream 向指定的文件中写入内容时
            // 如果该文件不存在, 则会自动进行创建
            outputStream = new FileOutputStream(destFilePath);
            while((readSize = inputStream.read(buffer)) != -1) {
                // 将读取到的内容写入到目标文件
                // outputStream.write(buffer);
                outputStream.write(buffer, 0, readSize);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 资源释放
            try {
                if(inputStream != null) {
                    inputStream.close();
                }
                if(outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

FileReader


使用 FileReader 中的 read() 方法, 一次只能读取一个字符的内容
使用 FileReader 中的 read(char cbuf[]) 方法, 一次可以读取指定字符大小的内容

public class ReadFile {

    public static void main(String[] args) {
        // readFile1();
        // readFile2();
    }

    // 一次读取一个字符
    public static void readFile1() {
        Reader reader = null;
        String filePath = "e:/test.txt";
        int readSize = 0;
        try {
            reader = new FileReader(filePath);
            // 从文件中一次读取一个字符的数据. 返回 -1, 表示读到文件末尾
            while((readSize = reader.read()) != -1) {
                System.out.print((char) readSize);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 一次读取多个字符
    public static void readFile2() {
        Reader reader = null;
        String filePath = "e:/test.txt";
        int readSize = 0;
        char[] buffer = new char[1024];
        try {
            reader = new FileReader(filePath);
            // 从文件中一次读取多个字符的数据. 返回 -1, 表示读到文件末尾
            while((readSize = reader.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, readSize));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

FileWriter


使用 FileWriter 向指定的文件中写入内容时. 如果该文件不存在, 则会自动进行创建
使用 FileWriter 中的 write() 方法, 一次只能写入一个字符的内容
使用 FileWriter 中的 write(char[] cbuf)write(String str) 方法, 一次可以写入指定字符大小的内容

FileWriter writer= new FileWriter(filePath) 再次执行程序时, 新写入的内容覆盖原有的内容
FileWriter writer= new FileWriter(filePath, true) 再次执行程序时, 新写入的内容会追加到原有内容的末尾

再次执行程序时, 指的是流被关闭后再次使用

FileWriter 使用之后, 必须要关闭(close)或刷新(flush). 否则数据写入不到指定的文件中

public class WriteFile {

    public static void main(String[] args) {
        // writeFile1();
        // writeFile2();
    }

    public static void writeFile1() {
        Writer writer = null;
        String filePath = "e:/note.txt";
        try {
            writer = new FileWriter(filePath);
            writer.write('我');
            writer.write('亦');
            writer.write('无');
            writer.write('他');
            writer.write(',');
            writer.write('为');
            writer.write('手');
            writer.write('属');
            writer.write('尔');
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 表示使用过该字符流
                if(writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void writeFile2() {
        Writer writer = null;
        String filePath = "e:/note.txt";
        try {
            writer = new FileWriter(filePath);
            String str = "我亦无他, 为手熟尔";
            writer.write(str);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 表示使用过该字符流
                if(writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

节点流与处理流


节点流, 针对指定的数据源读写数据. 例如 FileReader, FileWriter 针对的数据源就是文件

处理流, 也叫包装流. 是连接在已存在的流(节点流或处理流)之上, 为程序提供更为强大的读写功能. 例如 BufferedReader, BufferedWriter

翻译一下就是节点流只能做指定的工作. 而处理流不仅能做指定的工作, 还能在指定的基础之上做额外的工作

类型


分类字节输入流字节输出流字符输入流字符输出流流类型
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter节点流
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter节点流
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter节点流
访问字符串StringReaderStringWriter节点流
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter处理流
转换流InputStreamReaderOutputStreamWriter处理流
对象流ObjectInputStreamObjectOutputStream处理流
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter处理流
打印流PrintStreamPrintWriter处理流
推回输入流PushbackInputStreamPushbackReader处理流
特殊流DataInputStreamDataOutputStream处理流

BufferedReader


public class BFReadFile {

    public static void main(String[] args) throws IOException {
        readFile();
    }

    public static void readFile() throws IOException {
        String filePath = "e:/test.txt";
        BufferedReader reader = new BufferedReader(new FileReader(filePath));;
        String line = "";
        while((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
    }

}

BufferedWriter


public class BFWriter {

    public static void main(String[] args) throws IOException {
        writeFile();
    }

    public static void writeFile() throws IOException {
        String filePath = "e:/write.txt";
        // 表示以追加方式写入
        // BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true));
        // 表示以覆盖方式写入
        BufferedWriter writer = new BufferedWriter(new FileWriter(filePath));
        writer.write("hello world");
        // writer.write('\n');
        writer.newLine(); // 插入一个和系统相关的换行符
        writer.write("hello world");
        // writer.write('\n');
        writer.newLine(); // 插入一个和系统相关的换行符
        writer.write("hello world");
        // writer.write('\n');
        writer.newLine(); // 插入一个和系统相关的换行符
        writer.close();
    }

}

BufferedInputStream + BufferedOutputStream


利用 BufferedInputStream + BufferedOutputStream 实现文件拷贝

public class BufferedCopy {

    public static void main(String[] args) throws IOException {
        copyFile();
    }

    // 拷贝文件
    // 1. 读取源文件数据到程序(输入流)
    // 2. 将原文件数据从程序输入到目标文件(输出流)

    public static void copyFile() throws IOException {
    	// 源文件路径
        String srcFilePath = "d:/活法.pdf";
        // 目标文件路径
        String descFilePath = "e:/稻盛和夫_活法.pdf";
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
        BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(descFilePath));
        int readSize = 0;
        byte[] buf = new byte[1024];
        // 1. 读取源文件数据到程序
        while((readSize = inputStream.read(buf)) != -1) {
            // 2. 将原文件数据从程序输入到目标文件(输出流)
            outputStream.write(buf, 0, readSize);
            // 刷新缓冲区
            outputStream.flush();
        }
        inputStream.close();
        outputStream.close();
    }

}

对象处理流


对象处理流包括 ObjectInputStreamObjectOutputStream

通常利用 ObjectInputStreamObjectOutputStream 进行序列化与反序列化

对于序列化与反序列化的解释🍂

当我们保存数据时, 通常不会保存数据的类型

例如在 .txt 文件中保存写入 10 并保存, 此时并没有保存数据的类型. 此时我们无法确定 10 这个数据是整数还是字符串类型

在比如, 当我们保存一个浮点数 10.5 时, 虽然进行了保存, 但是无法确定这个浮点数的类型是 float 还是 double 类型

序列化的过程就是保存数据的类型 + 数据的值

反序列化的过程就是将保存数据的类型 + 数据的值

举个栗子🌰

// 示例代码
public class Student {
	private int id;
	private String name
}

定义一个学生类, 属性包括 id, name

如果只保存一个 id 的值 + name 的值. 例如 id = 10, name = “嘟嘟”. 此时我们并没有办法判断这些属性描述的具体对象. 可以是学生, 老师, 也可能是一只宠物

序列化的过程就是保存了数据的类型 + 数据的值, 也就是说将值所描述的对象一同进行保存

而反序列化的过程就是在恢复数据时, 恢复保存了的数据 + 数据的值

实现序列化的过程需要该类实现下列的任意一个接口

  • Serializable
  • Externalizable

通常选择实现 Serializable 接口, 因为这只是一个标记接口, 里面并不包含抽象方法

在这里插入图片描述

ObjectOutputStream


public class ObjOutput {

    public static void main(String[] args) throws IOException {
        writeFile();
    }

    public static void writeFile() throws IOException {
        String filePath = "e:/t1.txt";
        ObjectOutputStream outputStream = 
        	new ObjectOutputStream(new FileOutputStream(filePath));
        outputStream.writeInt(100);
        outputStream.writeChar('a');
        outputStream.writeUTF("hello world");
        outputStream.writeObject(new Student(1, "Tom"));
        outputStream.close();
        System.out.println("序列化完成");
    }

}

Student 类🍂

public class Student implements Serializable {

    private int id;
    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

ObjectInputStream


public class ObjInput {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        readFile();
    }

    public static void readFile() throws IOException, ClassNotFoundException {
        String filePath = "e:/t1.txt";
        ObjectInputStream inputStream = 
        	new ObjectInputStream(new FileInputStream(filePath));
        System.out.println(inputStream.readInt());
        System.out.println(inputStream.readChar());
        System.out.println(inputStream.readUTF());
        System.out.println(inputStream.readObject());
        inputStream.close();
        System.out.println("反序列化完成");
    }
    
}

对象处理流注意事项


  1. 读写顺序需要保持一致
  2. 序列化或反序列化的对象, 需要实现 SerializableExternalizable 接口
  3. 序列化的类中建议添加 SerialVersionUID, 以便提高版本兼容性
  4. 序列化对象时, 默认将对象中的所有属性都进行序列化(被 statictransient 修饰的成员不会序列化)
  5. 序列化对象时, 要求对象中的属性也实现序列化的接口
  6. 序列化具备可继承性. 即某个类实现了序列化, 那么它的子类也默认实现了序列化

标准输入输出流


System.in 标准输入 → 默认对应设备为键盘

  • System.in 编译类型为 InputStream
  • System.in 运行类型为 PrintStream

System.out 标准输出 → 默认对应设备为显示器

  • System.out 编译类型为 PrintStream
  • System.out 运行类型为 PrintStream

转换流


转换流包括 InputStreamReaderOutputStreamReader

转换流通常用于解决乱码问题. 这是因为InputStreamReaderOutputStreamReader 的构造方法中都可以指定字符编码

在这里插入图片描述

在这里插入图片描述

InputStreamReaderReader 的子类, 可以将 InputStream(字节流) 包装为 Reader(字符流)
OutputStreamReaderWriter 的子类, 可以将 OutputStream(字节流) 包装为 Writer(字符流)

InputStreamReader


public class InputReadFile {

    public static void main(String[] args) throws IOException {
        readFile();
    }

    public static void readFile() throws IOException {
        String filePath = "e:/copyFile.pdf";
        FileInputStream inputStream = new FileInputStream(filePath);
        // 将 FileInputStream 转换为 InputStreamReader
        // 指定编码格式为 UTF-8
        InputStreamReader reader = new InputStreamReader(inputStream, "gbk");
        // 将 InputStreamReader 转换为 BufferedReader
        BufferedReader bfReader = new BufferedReader(reader);
        while(bfReader.readLine() != null) {
            System.out.println(bfReader.readLine());
        }
        bfReader.close();
    }

}

OutputStreamWriter

public class OutputWriteFile {

    public static void main(String[] args) throws IOException {
        writeFile();
    }

    public static void writeFile() throws IOException {
        String filePath = "e:/test.txt";
        FileOutputStream outputStream = new FileOutputStream(filePath);
        // 设置编码格式
        // OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
        OutputStreamWriter writer = new OutputStreamWriter(outputStream, "gbk");
        BufferedWriter bfWriter = new BufferedWriter(writer);
        bfWriter.write("hello, world");
        bfWriter.newLine();
        bfWriter.write("做到才能得到");
        bfWriter.close();
    }

}

PrintStream


public class ExPrintStream {

    public static void main(String[] args) throws IOException {
        // print1();
        // print2();
    }

    // 未设置打印位置
    // 默认将打印结果输出到显示器
    public static void print1() {
        PrintStream stream = System.out;
        stream.println("hello world");
        stream.println("做到才能得到");
        stream.close();
    }

    // 设置打印位置
    public static void print2() throws IOException {
        String filePath = "e:/test.txt";
        System.setOut(new PrintStream(filePath));
        System.out.println("输出结果到指定位置");
        System.out.close();
    }

}

PrintWriter


public class ExPrintWriter {

    public static void main(String[] args) throws IOException {
        // print1();
        print2();
    }

    // 为指定输出位置
    // 默认将打印结果输出到显示器
    public static void print1() {
        PrintWriter writer = new PrintWriter(System.out);
        writer.println("hello world");
        writer.println("hello myFriend");
        writer.close();
    }

    // 指定输出位置
    public static void print2() throws IOException {
        String filePath = "e:/test.txt";
        PrintWriter writer = new PrintWriter(new FileWriter(filePath));
        writer.println("hello world");
        writer.println("hello myFriend");
        writer.close();
    }

}

🌸🌸🌸完结撒花🌸🌸🌸

在这里插入图片描述

  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值