Java
Python前端运维数据库
Java
Java
  • 新特性
    • Record
    • Optional
  • 面向对象
    • 面向对象基础
    • 构造方法
    • 继承与多态
    • 接口
    • 修饰符
    • 代码块
    • 接口(Interface)
    • 枚举类
  • IO流
    • IO
      • 字节流
      • 字符流
      • 缓冲流
      • 转换流
      • 操作ZIP
      • File 对象
    • NIO
      • Channel和Buffer
      • 异步文件通道AsynchronousFileChannel
      • Selector
      • Path/Files/Pipe
  • 反射
  • 内存分配
  • 集合
    • 简介
    • List
    • Set
    • Map
    • EnumMap
  • 日期与时间
    • Date和Calendar
    • Java8 新时间 ✨
      • LocalDateTime
      • ZonedDateTime
      • Duration
    • 时间格式化
      • SimpleDateFromat
      • DateTimeFormatter ✨
    • Instant
    • 实践
  • 网络编程
    • IP 地址
    • 网络模型
    • TCP 编程
    • UDP 编程
    • HTTP 编程
  • 加密和安全
  • 并发编程
    • 多线程
    • 线程与进程的区别
    • 线程组和线程优先级
    • 线程池
    • 线程锁
  • 异步任务
    • Future
    • CompletableFuture
      • 开启异步任务
      • 串行任务方法
      • 并行任务方法
      • 任务结束方法
      • 异常处理方法
      • 查看状态方法
      • 设置任务结果方法
  • 执行系统命令
  • Stream 流
    • Stream 流的创建
    • Stream 流串行与并行
    • Stream 流中间操作
    • Stream 流终端操作
  • Lambda 表达式
    • Lambda 表达式简介
    • Lambda 表达式语法
    • 方法引用
  • String
  • StringBuffer
由 GitBook 提供支持
在本页
  • InputStream
  • 基本使用
  • 异常处理
  • 缓冲
  • 中文字符乱码
  • ByteArrayInputStream
  • Outputstream
  • 基本使用
  • 使用IO流进行文件拷贝

这有帮助吗?

  1. IO流
  2. IO

字节流

InputStream

InputStream是 Java 标准库提供的最基本的输入流,它位于java.io包中,这个包提供了所有同步阻塞式 IO 的功能。

InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类,这个抽象定义了一个最重要的方法就是int read() 。这个方法会读取输入流的下一个字节,并返回字节表示的int值(0~255)。如果读到末尾,返回-1表示不能再读取了。

基本使用

FileInputStream是InputStream的一个子类,它可以从文件中读取数据。

复制

FileInputStream fileInputStream = new FileInputStream("test.txt");

// 读取文件中的内容,返回值是数据的下一个字节,如果到达文件末尾则为-1 。
int v1 = fileInputStream.read(); // 104
int v2 = fileInputStream.read(); // 101
int v3 = fileInputStream.read(); // 108
int v4 = fileInputStream.read(); // 108
int v5 = fileInputStream.read(); // 111

// 关闭资源
fileInputStream.close();

上面的程序还存在一些问题,即在读取文件数据之前,我们须提前得到文件中的字符数量,如果字符比较多的情况下,就需要调用很多次read方法。此时我们可以通过循环来去读文件中的字符。

复制

FileInputStream fileInputStream = new FileInputStream("test.txt");

int temp;
while ((temp = fileInputStream.read()) != -1) {
   System.out.println((char)temp);
}

// 关闭流资源
fileInputStream.close()

异常处理

在读取或写入 IO 流的过程中,可能会发生错误,这些错误由 Java 虚拟机自动封装成IOException异常并抛出。因此,所有IO操作都必须正确处理IOException异常。

复制

FileInputStream fileInputStream = null;
try {
    fileInputStream = new FileInputStream("test.txt");
    int temp;
    while ((temp = fileInputStream.read()) != -1) {
        System.out.println((char) temp);
    }
} catch (IOException e) {
    System.out.println("文件读取出错");
} finally {
    if (fileInputStream != null) {
        fileInputStream.close();
    }
}

使用try...finally来处理上述代码会比较复杂,更好的写法是利用 Java7 引入的try(resource)语法,只需要编写try语句,让编译器自动来关闭资源。

复制

try (FileInputStream fileInputStream = new FileInputStream("test.txt")) {
    int temp;
    while ((temp = fileInputStream.read()) != -1) {
        System.out.println((char)temp);
    }
} catch (IOException e) {
    System.out.println("文件读取出错");
}

实际上,编译器并不会特别地为InputStream加上自动关闭。编译器只看try(resource = ...)中的对象是否实现了java.lang.AutoCloseable接口,如果实现了,就自动加上finally语句并调用close()方法。InputStream和OutputStream都实现了这个接口,因此,都可以用在try(resource)中。

缓冲

在读取流资源的时候,一次一个字节的读取效率不高。很多流支持一次性读取多个字节到缓冲区,对于文件和网络流来说,利用缓冲区一次性读取多个字节效率往往要高很多。

复制

try (FileInputStream fileInputStream = new FileInputStream("test.txt")) {
    byte[] buffer = new byte[2];
    int temp;
    StringBuilder stringBuilder = new StringBuilder();
    while ((temp = fileInputStream.read(buffer)) != -1) {
        System.out.println("读取" + temp + "个字节");
        stringBuilder.append(new String(buffer, 0, temp));
    }
    System.out.println(stringBuilder);
} catch (IOException e) {
    System.out.println("文件读取出错");
}

中文字符乱码

当文本文件中出现中文字符时,会出现中文乱码问题,这是因为在utf-8字符编码下,一个中文字符占3个字节,所以按照字节读取时,容易把一个完整的文字劈开,所以会出现中文乱码问题。

无论是在 IO 的读取还是写入中,通过字节流来进行读写操作都会导致中文乱码的问题,要想解决问题,需要使用字符流Reader和Writer。

ByteArrayInputStream

用FileInputStream可以从文件获取输入流,这是InputStream最常用的一个实现类。此外ByteArrayInputStream可以在内存中模拟一个InputStream:

ByteArrayInputStream 实际上是把一个byte[]数组在内存中变成一个InputStream,虽然实际应用不多,但是在测试的时候,可以用它来构造一个InputStream。

因为在使用FileInputStream时,就需要在本地磁盘中放一个真实的文本文件,而在测试的时候,往往没有这个文件,就可以用ByteArrayInputStream来模拟一个InputStream。

复制

StringBuilder stringBuilder = new StringBuilder();
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream("hello word".getBytes())) {
    byte[] buffer = new byte[5];
    int temp;
    while ((temp = byteArrayInputStream.read(buffer)) != -1) {
        stringBuilder.append(new String(buffer, 0, temp));
    }
    System.out.println(stringBuilder);
} catch (IOException e) {
    System.out.println("文件读取出错");
}

Outputstream

基本使用

和InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。OutputStream最重要的一个方法就是void write(int b)。

复制

public abstract void write(int b) throws IOException;

这个方法会写入一个字节到输出流,虽然传入的是int参数,但是只会写入一个字节,即int最低8位表示字节的部分(相当于b & 0xff)。

复制

try (FileOutputStream fileOutputStream = new FileOutputStream("test_01.txt")) {
    byte[] bytes = "hello word".getBytes(StandardCharsets.UTF_8);
    fileOutputStream.write(bytes);
    // 刷新缓冲区,保证数据写入文件
    fileOutputStream.flush();
}

OutputStream提供了close和flush方法。

flush方法

为什么需要使用flush方法?因为向磁盘、网络写入数据的时候,处于效率考虑,操作系统并不是输出一个字节就立即写入文件或发送网络,而是把输出的字节先放到内存的一个缓冲区(本质上是byte[])数组,等到缓冲区写满了,再一次性写入文件或网络。对于很多IO设备来说,一次写一个字节和1000个字节,花费的时间是一样的,所以OutputStream有个flush()方法,能强制把缓冲区内容输出。

通常情况下,我们不需要调用这个flush方法,因为缓冲区写满了OutputStream会自动调用它,并且在调用close方法关闭之前,也会自动调用flush方法。

手动调用flush的案例

例如你正在开发一个应用程序,它需要记录重要事件和信息,以便后续分析和监控,这个应用程序需要在实时性方面非常敏感,因为事件的记录需要尽快写入日志文件,以便管理员能够及时查看并采取必要的措施。

在这种情况下,你会使用OutputStream(通常是FileOutputStream)来写入日志文件。为了确保日志信息尽快写入磁盘,你会手动调用flush()方法,而不是等待缓冲区填满或等待自动刷新。这确保了日志事件的即时记录,即使应用程序在某个时间点崩溃,也可以确保已记录的事件不会丢失。

使用IO流进行文件拷贝

使用流进行文件拷贝使用缓冲进行文件拷贝

复制

try (FileInputStream fileInputStream = new FileInputStream("test.txt");
     FileOutputStream fileOutputStream = new FileOutputStream("test_01.txt")) {
    byte[] bytes = new byte[1024];
    int temp;
    while ((temp = fileInputStream.read(bytes)) != -1) {
        // write(数据, 起始索引, 写入数据的长度)
        fileOutputStream.write(bytes, 0, temp);
    }
}

上一页IO下一页字符流

最后更新于5个月前

这有帮助吗?

通过可以得知,文件中存储的字符串是hello。

ASCII对照表