Java—IO 流

Java---IO 流

🔍文件

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

创建文件

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

  1. File(String pathname), 指定路径名创建
  2. File(File parent, String child), 指定父抽象路径 + 子路径名创建
  3. File(String parent, String child), 指定父路径名 + 子路径名创建
java 复制代码
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) 输出流
抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

FileInputStream

使用 FileInputStream 中的 read() 方法, 一次只能读取一个字节的内容

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

java 复制代码
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) 再次执行程序时, 新写入的内容会追加到原有内容的末尾

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

java 复制代码
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)

注意🍂

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

java 复制代码
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[]) 方法, 一次可以读取指定字符大小的内容

java 复制代码
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). 否则数据写入不到指定的文件中

java 复制代码
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

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

类型

分类 字节输入流 字节输出流 字符输入流 字符输出流 流类型
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter 节点流
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 节点流
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter 节点流
访问字符串 StringReader StringWriter 节点流
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 处理流
转换流 InputStreamReader OutputStreamWriter 处理流
对象流 ObjectInputStream ObjectOutputStream 处理流
抽象基类 FilterInputStream FilterOutputStream FilterReader FilterWriter 处理流
打印流 PrintStream PrintWriter 处理流
推回输入流 PushbackInputStream PushbackReader 处理流
特殊流 DataInputStream DataOutputStream 处理流

BufferedReader

java 复制代码
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

java 复制代码
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 实现文件拷贝

java 复制代码
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 类型

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

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

举个栗子🌰

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

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

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

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

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

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

  • Serializable
  • Externalizable

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

ObjectOutputStream

java 复制代码
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 类🍂

java 复制代码
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

java 复制代码
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

java 复制代码
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

java 复制代码
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

java 复制代码
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

java 复制代码
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();
    }

}

🌸🌸🌸完结撒花🌸🌸🌸

相关推荐
进阶的架构师几秒前
2024年Java面试题及答案整理(1000+面试题附答案解析)
java·开发语言
前端拾光者4 分钟前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
The_Ticker5 分钟前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
程序猿阿伟6 分钟前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链
傻啦嘿哟24 分钟前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel
大数据编程之光28 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
初九之潜龙勿用28 分钟前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
爪哇学长41 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
Dola_Pan1 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法
ExiFengs1 小时前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring