Java输入输出流(IO)

Java输入输出流(IO)

概论

流是一组有顺序的,有起点和终点字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输成为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。输入流和输出的读取和写入流程如图所示。

请添加图片描述

分类

1.字符流和字节流

字符流:以字符为最小数据单位的流,是基于字节流 + 编码表(UTF-8/GBK)封装的流。

字节流:以字节(8 位二进制)为最小数据单位进行数据读写的流,是 Java IO 中**最基础、最通用的流。

2.输入流和输出流

输入流:数据从「外部设备」流向「Java 程序(内存)」**的流,核心作用是**读取数据。

输出流:数据从「Java 程序(内存)」流向「外部设备」**的流,核心作用是**写入数据。

3.节点流和处理流

节点流:直接连接数据源 / 数据目的地的流,是真正负责底层数据读写的基础流(无任何包装)。

处理流:包装节点流,不直接连接数据源,仅对已有流进行功能增强的流(基于装饰者模式实现)。

IO 流的体系结构

请添加图片描述

Java.io 包中的最重要的部分是由6个类和1个接口组成。6个类是指 File、RandomAccessFile、InputStream、OutputStream、Writer、Reader,1个接口指的是 Serializable。掌握了这些 I/O 的核心操作,那么对于Java 中的 I/O 体系也就有了一个初步的认识了。

总体上看,Java I/O 主要包括如下3个部分:

流式部分:I/O 的主体部分。
非流式部分:主要包含一些辅助流式部分的类,如 File 类、RandomAccessFile 类和 FileDescriptor 类等。
其他类:主要是文件读取部分的与安全相关的类(如 SerializablePermission 类),以及与本地操作系统相关的文件系统的类,如(FileSystem 类、Win32FileSystem 类和 WinNTFileSystem 类)。

这里,将 Java I/O 中主要的类简单介绍如下:

File类:文件和目录操作,用于表示文件或目录,并提供文件操作,如创建、删除、重命名等。

InputStream类:抽象类 (输入流),基于字节输入操作的抽象类,是所有字节输入流的父类,定义了所有输入流都具有的共同特征。

OutputStream类:抽象类 (输出流),基于字节输出操作的抽象类,是所有字节输出流的父类,定义了所有输出流都具有的共同特征。

Reader类:抽象类 (输入流),所有字符输入流的超类,处理字符的输入操作。

Writer类:抽象类 (输出流),所有字符输出流的超类,处理字符的输出操作。

RandomAccessFile类:随机访问文件,支持文件的随机访问,可以从文件的任意位置读写数据。

缓冲流和缓冲区(是处理流里的分支)

流和缓冲区都是用来描述数据的。计算机中,数据往往会被抽象成流,然后传输。

比如读取一个文件,数据会被抽象成文件流;播放一个视频,视频被抽象成视频流。

在传输层协议当中,应用往往先把数据放入缓冲区,然后再将缓冲区提供给发送数据的程序。发送数据的程序,从缓冲区读取出数据,然后进行发送。

中心思想:减少对硬盘的读写次数,提高读写效率。

通俗解释:送快递,如果到了一个快递,我就给人送去,然后又到了一个快递,又给别人送去,这样效率就非常低下。于是我把快递囤了一定数量,然后再用快递车一起去送,那么效率将高得多。

问题:

那么缓冲流有什么好处么?为什么有了InputStream还需要BufferedInputStream呢?

BufferedInputStream、BufferedOutputStream是FilterInputStream、FilterOutputStream的子类,作为装饰器类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。

我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!

代码实现

File类(文件创建,获取,删除)

在文件夹中创建1.txt文件和b目录

请添加图片描述

书写类DemoA
import java.io.File;

// 描述文件类
public class DemoA {

    // 1. 获取到文件或者目录
    private static void a() {
        // 代表的是一个文件,1.txt 存在
        File file1 = new File("/D:/java代码/workspace/io/1.txt"); //刚才创建文件的地方
        // 代表的是一个文件,2.txt 不存在
        File file2 = new File("/D:/java代码/workspace/io/2.txt");
        // 代表的是一个目录(文件夹),a目录
        File file3 = new File("/D:/java代码/workspace/io/a");
        // 代表的是一个目录(文件夹),b目录,存在
        File file4 = new File("/D:/java代码/workspace/io/b");

        System.out.println(file1);
        System.out.println(file2);
        System.out.println(file3);
        System.out.println(file4);
    }
    
    public static void main(String[] args) {
        a();
    }
}
文件和目录的常见方法1️⃣
// 文件和目录的常见方法1️⃣
private static void b() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    // 1.获取到名称
    String name = file.getName();
    // 2.获取到绝对路径
    String absolutePath = file.getAbsolutePath();
    // 3.判断是否是目录、是否是文件
    boolean isFile = file.isFile();
    boolean isDirectory = file.isDirectory();
    // 4.判断是否存在
    boolean exists = file.exists();
    // 5.获取到父目录
    String parentPath = file.getParent();
    System.out.println(name);
    System.out.println(absolutePath);
    System.out.println(isFile);
    System.out.println(isDirectory);
    System.out.println(exists);
    System.out.println(parentPath);
}
文件和目录的常见方法2️⃣
// 文件和目录的常见方法2️⃣
private static void c() {
    File file = new File("/D:/java代码/workspace");
    if (file.isDirectory()) {
        // 获取到子内容
        File[] childFiles = file.listFiles();
        for (File childFile : childFiles) {
            System.out.println(childFile.getName());
        }
    }
}
文件和目录的常见方法3️⃣
// 文件和目录的常见方法3️⃣
@SneakyThrows
private static void d() {
    File file1 = new File("/D:/java代码/workspace/io/2.txt");
    if (!file1.exists()) {
        // 创建文件
        boolean isSuccess = file1.createNewFile();
        System.out.println(isSuccess);
    }
    File file2 = new File("/D:/java代码/workspace/io/c/a");
    if (!file2.exists()) {
        // 创建目录
        // boolean isSuccess = file2.mkdir();//不带s不能创建子目录的不存在的父目录
        boolean isSuccess = file2.mkdirs();//带s的即使没有c,也能帮你把a的父目录c创建出来
        System.out.println(isSuccess);
    }
    // 删除文件
    file1.deleteOnExit();
    // 删除目录
    file2.deleteOnExit();
}
遍历某个盘符
  // 遍历某个盘符
    private static void e(File file, int index) {
        for (int i = 0; i < index; i++) {
            System.out.print(" ");
        }
        System.out.println(file.getName());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File childFile : files) {
                int m = index + 1;
                e(childFile, m);
            }
        }
    }


    public static void main(String[] args) {
        File file = new File("/D:/java代码/workspace");
        int index = 0;
        e(file, index);
    }
效果

请添加图片描述

OutputStream类(写)

文件写入(覆盖)
import lombok.SneakyThrows;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Scanner;

// 文件的写入(字节流形式)
public class DemoB {

    @SneakyThrows
    private static void a() {
        // 1.产生一个文件
        File file = new File("/D:/java代码/workspace/io/1.txt");
        boolean exists = file.exists();
        if (!exists) {
            System.out.println("该文件不存在");
            return;
        }
        // 2.写入到这个文件中(覆盖)
        // 2.1 产生写入的对象(字节流对象)
        OutputStream out = new FileOutputStream(file);
        // 2.2 写入一个字
        String s = "张三来了呀";
        byte[] bytes = s.getBytes();
        out.write(bytes);
        // 2.3 关闭流通道
        //out.close();
    }
    public static void main(String[] args) {
        a();
    }
}
文件写入(追加)
@SneakyThrows
private static void b() {
    // 1.产生一个文件
    File file = new File("/D:/java代码/workspace/io/1.txt");
    boolean exists = file.exists();
    if (!exists) {
        System.out.println("该文件不存在");
        return;
    }
    // 2.写入到这个文件中(追加)
    // 2.1 产生写入的对象(字节流对象) 追加操作
    OutputStream out = new FileOutputStream(file, true);
    // 2.2 写入一个字
    String s = "张三来了呀";
    byte[] bytes = s.getBytes();
    out.write(bytes);
    // 2.3 关闭流通道
    out.close();
}
控制台键盘写入
@SneakyThrows
private static void c() {
    File file = new File("/D:/java代码/workspace/io/2.txt");
    boolean exists = file.exists();
    if (!exists) {
        System.out.println("该文件不存在");
        file.createNewFile();
    }
    OutputStream out = new FileOutputStream(file);
    Scanner scan = new Scanner(System.in);
    String line;
    do {
        System.out.print("请输入:");
        line = scan.nextLine();
        if ("bye".equals(line)) {
            break;
        }
        byte[] bytes = (line + "\n").getBytes();
        out.write(bytes);
    } while (true);
    out.close();
}

InputStream类

一次性读取一个字节
import java.io.*;
import java.util.Arrays;

// 文件的读取(字节流形式)
public class DemoC {

    // 一次读取一个字节
    @SneakyThrows
    private static void a() {
        File file = new File("/D:/java代码/workspace/io/2.txt");
        if (!file.exists()) {
            System.out.println("文件不存在");
            return;
        }
        if (file.isDirectory()) {
            System.out.println("读取的必须是文件");
            return;
        }
        InputStream in = new FileInputStream(file);
        // 读取了一个字
        int read = in.read();
        System.out.println(read);
        in.close();
    }
     public static void main(String[] args) {
        a();
    }
}
根据循环将内容读取完毕
@SneakyThrows
private static void b() {
    File file = new File("/D:/java代码/workspace/io/2.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (file.isDirectory()) {
        System.out.println("读取的必须是文件");
        return;
    }
    InputStream in = new FileInputStream(file);
    while (true) {
        // 读取了一个字
        int read = in.read();
        if (read == -1) {
            break;
        }
        System.out.print((char) read);
    }
    in.close();
}
一次读取多个字节
@SneakyThrows
private static void c() {
    File file = new File("/D:/java代码/workspace/io/2.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (file.isDirectory()) {
        System.out.println("读取的必须是文件");
        return;
    }
    InputStream in = new FileInputStream(file);
    // 一次读取了十个字节
    byte[] buffer = new byte[19];
    // len:实际读取的长度
    int len = in.read(buffer);
    if (len == -1) {
        System.out.println("没有内容");
    }
    System.out.println(Arrays.toString(buffer));
    String str = new String(buffer);
    System.out.println(str);
    in.close();
}

循环的方式

@SneakyThrows
private static void d() {
    File file = new File("/D:/java代码/workspace/io/2.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (file.isDirectory()) {
        System.out.println("读取的必须是文件");
        return;
    }
    InputStream in = new FileInputStream(file);
    // 一次读取了十个字节
    byte[] buffer = new byte[10];
    // len:实际读取的长度
    while (true) {
        int len = in.read(buffer);
        if (len == -1) {
            System.out.println("没有内容");
            break;
        }
        String str = new String(buffer);
        System.out.print(str);
    }
    in.close();
}
将5.png复制到另一个目录中
@SneakyThrows
private static void e() {
    File srcFile = new File("/D:/java代码/workspace/io/a/5.png");
    File destFile = new File("/D:/java代码/workspace/io/b/5.png");
    // 读取文件
    InputStream in = new FileInputStream(srcFile);
    // 写入文件
    OutputStream out = new FileOutputStream(destFile);
    byte[] buffer = new byte[1024];
    while (true) {
        int len = in.read(buffer);
        if (len == -1) {
            break;
        }
        out.write(buffer, 0, len);
    }
    in.close();
    out.close();
}

Reader类

读取一个字符
@SneakyThrows
private static void a() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (!file.isFile()) {
        System.out.println("不是一个文件");
        return;
    }
    // 产生一个字符读取流
    Reader reader = new FileReader(file);
    // 读取一个字符
    int i = reader.read();
    if (i == -1) {
        System.out.println("文件读取完毕");
        return;
    }
    char ch = (char) i;
    System.out.println(ch);
    reader.close();

}

循环方式

@SneakyThrows
private static void b() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (!file.isFile()) {
        System.out.println("不是一个文件");
        return;
    }
    // 产生一个字符读取流
    Reader reader = new FileReader(file);
    while (true) {
        // 读取一个字符
        int i = reader.read();
        if (i == -1) {
            System.out.println("文件读取完毕");
            break;
        }
        char ch = (char) i;
        System.out.print(ch);
    }
    reader.close();

}
读取多个字符
@SneakyThrows
private static void c() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (!file.isFile()) {
        System.out.println("不是一个文件");
        return;
    }
    Reader reader = new FileReader(file);
    char[] chars = new char[5];
    int len = reader.read(chars);
    System.out.println(chars);
    reader.close();
}

循环方式

@SneakyThrows
private static void d() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    if (!file.exists()) {
        System.out.println("文件不存在");
        return;
    }
    if (!file.isFile()) {
        System.out.println("不是一个文件");
        return;
    }
    Reader reader = new FileReader(file);
    char[] chars = new char[5];
    while (true) {
        int len = reader.read(chars);
        if(len == -1) {
            break;
        }
        String str = new String(chars, 0, len);
        System.out.print(str);
    }
    reader.close();
}

Writer类

写入到了字符缓冲区
@SneakyThrows
private static void a() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    if (file.isDirectory()) {
        System.out.println("不是一个文件");
        return;
    }
    Writer writer = new FileWriter(file);
    // 写入是写入到了字符缓冲区(临时的内存区域)
    writer.write("你好");
    // 将字符缓冲区中的数据写入到硬盘中
    writer.flush();
    writer.close();
}
批量换行写入
@SneakyThrows
private static void b() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    if (file.isDirectory()) {
        System.out.println("不是一个文件");
        return;
    }
    Writer writer = new FileWriter(file);
    Scanner scan = new Scanner(System.in);
    while (true) {
        System.out.print("Input:");
        String input = scan.next();
        if ("再见".equals(input)) {
            break;
        }
        writer.write(input + "\n");
    }
    writer.close();
}

RandomAccessFile类

Java 提供的 RandomAccessFile 类,允许从文件的任何位置进行数据的读写。它不属于流,是 Object 类的子类,但它融合了 InputStream 类和 OutStream 类的功能,既能提供 read()方法和 write()方法,还能提供更高级的直接读写各种基本数据类型数据的读写方法,如 readInt()方法和 writeInt()方法等。

public class RandomAccessFileTest{
    public static void main(String[] args) throws Exception {
        File file = new File("D://guanwei//guanwei.txt");
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        // 从2号位置开始定位
        raf.seek(2);
        // 插入7个字母‘abcdefg’ 会覆盖其后的7个内容
        raf.write("abcdefg".getBytes());
        // 获取当前指针的位置
        long pointer = raf.getFilePointer();
        System.out.println("当前指针位置是:" + pointer);
        // 读取,设置从0开始读取
        raf.seek(0);
        byte[] bs = new byte[100];
        int len;
        while ((len = raf.read(bs)) != -1) {
            System.out.println(new String(bs, 0, len));
        }
    }
 
}

缓冲流

字节流的写入缓冲
@SneakyThrows
private static void a() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    // 创建了一个字节写入流
    OutputStream in = new FileOutputStream(file);
    // 创建缓冲流
    BufferedOutputStream bis = new BufferedOutputStream(in);
    bis.write("hello".getBytes());
    bis.flush();
    bis.close();
}
字节流的读取缓冲
@SneakyThrows
private static void b1() {
    File file = new File("/D:/java代码/workspace/io/2025.07.24.17.23.mp4");
    InputStream in = new FileInputStream(file);
    long start = System.currentTimeMillis();
    byte[] buffer = new byte[1024];
    while (true) {
        int len = in.read(buffer);
        if (len == -1) {
            break;
        }
    }
    long end = System.currentTimeMillis();
    System.out.println("总共花费了:" + (end - start) + "毫秒");
    in.close();
}
字符流的写入缓冲
@SneakyThrows
private static void c() {
    File file = new File("/D:/java代码/workspace/io/1.txt");
    Writer writer = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(writer);
    bw.write("你好,我是张三,我是使用缓冲流写入的数据");
    bw.flush();
}
字符流的读取缓冲
@SneakyThrows
private static void d1() {
    File file = new File("/D:/java代码/workspace/io/2.txt");
    Reader reader = new FileReader(file);
    char[] buffer = new char[10];
    long start = System.currentTimeMillis();
    while (true) {
        int len = reader.read(buffer);
        if (len == -1) {
            break;
        }
        System.out.print(new String(buffer, 0, len));
    }
    System.out.println();
    long end = System.currentTimeMillis();
    System.out.println("总共花费了:" + (end - start) + "毫秒");
    reader.close();
}

字节流转字符流

import lombok.SneakyThrows;

import java.io.*;

public class DemoG {
    @SneakyThrows
    public static void main(String[] args) {
        File file = new File("/D:/java代码/workspace/io/1.txt");
        InputStream in = new FileInputStream(file);
        // 通过InputStreamReader将字节流---->字符流
        Reader reader = new InputStreamReader(in);
        BufferedReader bufferedReader = new BufferedReader(reader);
        String line = bufferedReader.readLine();
        System.out.println(line);
        bufferedReader.close();
        reader.close();
        in.close();

        OutputStream out = new FileOutputStream(file);
        // 通过OutputStreamWriter将字节流---->字符流
        Writer writer = new OutputStreamWriter(out);
        writer.write("asjkdhkdsjhiwheswegqwiud你啊后啊我😋3 的说法 ");
        writer.flush();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值