阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

InputStream

272次阅读
没有评论

共计 4363 个字符,预计需要花费 11 分钟才能阅读完成。

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

要特别注意的一点是,InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read(),签名如下:

public abstract int read() throws IOException;

这个方法会读取输入流的下一个字节,并返回字节表示的 int 值(0~255)。如果已读到末尾,返回 -1 表示不能继续读取了。

FileInputStreamInputStream 的一个子类。顾名思义,FileInputStream就是从文件流中读取数据。下面的代码演示了如何完整地读取一个 FileInputStream 的所有字节:

public void readFile() throws IOException {// 创建一个 FileInputStream 对象:
    InputStream input = new FileInputStream("src/readme.txt");
    for (;;) {int n = input.read(); // 反复调用 read()方法,直到返回 -1
        if (n == -1) {break;
        }
        System.out.println(n); // 打印 byte 的值
    }
    input.close(); // 关闭流
}

在计算机中,类似文件、网络端口这些资源,都是由操作系统统一管理的。应用程序在运行的过程中,如果打开了一个文件进行读写,完成后要及时地关闭,以便让操作系统把资源释放掉,否则,应用程序占用的资源会越来越多,不但白白占用内存,还会影响其他应用程序的运行。

InputStreamOutputStream 都是通过 close() 方法来关闭流。关闭流就会释放对应的底层资源。

我们还要注意到在读取或写入 IO 流的过程中,可能会发生错误,例如,文件不存在导致无法读取,没有写权限导致写入失败,等等,这些底层错误由 Java 虚拟机自动封装成 IOException 异常并抛出。因此,所有与 IO 操作相关的代码都必须正确处理IOException

仔细观察上面的代码,会发现一个潜在的问题:如果读取过程中发生了 IO 错误,InputStream就没法正确地关闭,资源也就没法及时释放。

因此,我们需要用 try ... finally 来保证 InputStream 在无论是否发生 IO 错误的时候都能够正确地关闭:

public void readFile() throws IOException {InputStream input = null;
    try {input = new FileInputStream("src/readme.txt");
        int n;
        while ((n = input.read()) != -1) {// 利用 while 同时读取并判断
            System.out.println(n);
        }
    } finally {if (input != null) {input.close(); }
    }
}

try ... finally 来编写上述代码会感觉比较复杂,更好的写法是利用 Java 7 引入的新的 try(resource) 的语法,只需要编写 try 语句,让编译器自动为我们关闭资源。推荐的写法如下:

public void readFile() throws IOException {try (InputStream input = new FileInputStream("src/readme.txt")) {int n;
        while ((n = input.read()) != -1) {System.out.println(n);
        }
    } // 编译器在此自动为我们写入 finally 并调用 close()
}

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

缓冲

在读取流的时候,一次读取一个字节并不是最高效的方法。很多流支持一次性读取多个字节到缓冲区,对于文件和网络流来说,利用缓冲区一次性读取多个字节效率往往要高很多。InputStream提供了两个重载方法来支持读取多个字节:

  • int read(byte[] b):读取若干字节并填充到 byte[] 数组,返回读取的字节数
  • int read(byte[] b, int off, int len):指定 byte[] 数组的偏移量和最大填充数

利用上述方法一次读取多个字节时,需要先定义一个 byte[] 数组作为缓冲区,read()方法会尽可能多地读取字节到缓冲区,
但不会超过缓冲区的大小。read()方法的返回值不再是字节的 int 值,而是返回实际读取了多少个字节。如果返回-1,表示没有更多的数据了。

利用缓冲区一次读取多个字节的代码如下:

public void readFile() throws IOException {try (InputStream input = new FileInputStream("src/readme.txt")) {// 定义 1000 个字节大小的缓冲区:
        byte[] buffer = new byte[1000];
        int n;
        while ((n = input.read(buffer)) != -1) {// 读取到缓冲区
            System.out.println("read" + n + "bytes.");
        }
    }
}

阻塞

在调用 InputStreamread()方法读取数据时,我们说 read() 方法是阻塞(Blocking)的。它的意思是,对于下面的代码:

int n;
n = input.read(); // 必须等待 read()方法返回才能执行下一行代码
int m = n;

执行到第二行代码时,必须等 read() 方法返回后才能继续。因为读取 IO 流相比执行普通代码,速度会慢很多,因此,无法确定 read() 方法调用到底要花费多长时间。

InputStream 实现类

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

import java.io.*;

public class Main {public static void main(String[] args) throws IOException {byte[] data = { 72, 101, 108, 108, 111, 33 };
        try (InputStream input = new ByteArrayInputStream(data)) {int n;
            while ((n = input.read()) != -1) {System.out.println((char)n);
            }
        }
    }
}

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

举个例子:我们想从文件中读取所有字节,并转换成 char 然后拼成一个字符串,可以这么写:

public class Main {public static void main(String[] args) throws IOException {
        String s;
        try (InputStream input = new FileInputStream("C:\\test\\README.txt")) {int n;
            StringBuilder sb = new StringBuilder();
            while ((n = input.read()) != -1) {sb.append((char) n);
            }
            s = sb.toString();}
        System.out.println(s);
    }
}

要测试上面的程序,就真的需要在本地硬盘上放一个真实的文本文件。如果我们把代码稍微改造一下,提取一个 readAsString() 的方法:

public class Main {public static void main(String[] args) throws IOException {
        String s;
        try (InputStream input = new FileInputStream("C:\\test\\README.txt")) {s = readAsString(input);
        }
        System.out.println(s);
    }

    public static String readAsString(InputStream input) throws IOException {int n;
        StringBuilder sb = new StringBuilder();
        while ((n = input.read()) != -1) {sb.append((char) n);
        }
        return sb.toString();}
}

对这个 String readAsString(InputStream input) 方法进行测试就相当简单,因为不一定要传入一个真的FileInputStream

import java.io.*;

public class Main {public static void main(String[] args) throws IOException {byte[] data = { 72, 101, 108, 108, 111, 33 };
        try (InputStream input = new ByteArrayInputStream(data)) {String s = readAsString(input);
            System.out.println(s);
        }
    }

    public static String readAsString(InputStream input) throws IOException {int n;
        StringBuilder sb = new StringBuilder();
        while ((n = input.read()) != -1) {sb.append((char) n);
        }
        return sb.toString();}
}

这就是面向抽象编程原则的应用:接受 InputStream 抽象类型,而不是具体的 FileInputStream 类型,从而使得代码可以处理 InputStream 的任意实现类。

小结

Java 标准库的 java.io.InputStream 定义了所有输入流的超类:

  • FileInputStream实现了文件流输入;
  • ByteArrayInputStream在内存中模拟一个字节流输入。

总是使用 try(resource) 来保证 InputStream 正确关闭。

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计4363字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
8000663
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...
免费领取huggingface的2核16G云服务器,超简单教程

免费领取huggingface的2核16G云服务器,超简单教程

免费领取 huggingface 的 2 核 16G 云服务器,超简单教程 前言 HuggingFace.co...
星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛 NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手! 作为动漫爱好者,你是否还在为...
小白也能看懂:什么是云服务器?腾讯云 vs 阿里云对比

小白也能看懂:什么是云服务器?腾讯云 vs 阿里云对比

小白也能看懂:什么是云服务器?腾讯云 vs 阿里云对比 星哥玩云,带你从小白到上云高手。今天咱们就来聊聊——什...
星哥带你玩飞牛 NAS-10:备份微信聊天记录、数据到你的NAS中!

星哥带你玩飞牛 NAS-10:备份微信聊天记录、数据到你的NAS中!

星哥带你玩飞牛 NAS-10:备份微信聊天记录、数据到你的 NAS 中! 大家对「数据安全感」的需求越来越高 ...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
把小米云笔记搬回家:飞牛 NAS 一键部署,小米云笔记自动同步到本地

把小米云笔记搬回家:飞牛 NAS 一键部署,小米云笔记自动同步到本地

把小米云笔记搬回家:飞牛 NAS 一键部署,小米云笔记自动同步到本地 大家好,我是星哥,今天教大家在飞牛 NA...
安装并使用谷歌AI编程工具Antigravity(亲测有效)

安装并使用谷歌AI编程工具Antigravity(亲测有效)

  安装并使用谷歌 AI 编程工具 Antigravity(亲测有效) 引言 Antigravity...
自己手撸一个AI智能体—跟创业大佬对话

自己手撸一个AI智能体—跟创业大佬对话

自己手撸一个 AI 智能体 — 跟创业大佬对话 前言 智能体(Agent)已经成为创业者和技术人绕...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...