Java Stream 流对象(实用技巧)

news2024/11/26 12:37:49

目录

一、InputStream & OutputStream

1.1、InputStream 和 OutputStream 一般使用

1.2、特殊使用

1.2.1、如何表示文件读取完毕?(DataInputStream)

1.2.2、字符读取/文本数据读取(Scanner)

1.2.3、文件的随机读写(RandomAccessFile)


一、InputStream & OutputStream


1.1、InputStream 和 OutputStream 一般使用

InputStream 有以下几个方法:

  1. int read():读取一个字节的数据,返回 -1 代表已经完全读完了.
  2. int read(byte[] b):最多读取 b.length 字节的数据到 b 中,返回实际读到的数 量;-1 代表以及读完了(这就像是你去端了个盆,去食堂让阿姨给打饭,那么阿姨肯定是按照她这饭的多少,能给你打满,就尽量给你打满),这也是实际比较常用的方法.
  3. int read(byte[] b, int off, int len):最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了.
  4. void close():关闭字节流(一般会把 InputStream 写在 try() 中,就不用手动释放了~).

inputStream 只是一个抽象类,要使用还是需要具体的实现类,比如 当客户端和服务器 accept 后,获取流对象具体实现类...... 但是我们最常用的还是文件的读取,也就是 FileInputStream.

OutputStream 有以下几个方法:

  1. void write(int b):将指定的字节写入此输出流.
  2. void write(byte[] b):将 b 这个字符数组中的数据全部写入 os 中.
  3. int write(byte[] b, int off, int len):将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
  4. void close():关闭字节流
  5. void flush():我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置, 调用 flush(刷新)操作,将数据刷到设备中。

OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中, 所以使用 FileOutputStream

Ps:FileOutputStream 有一个构造器是 new FileOutputStream(String path, boolean append),第一个参数是文件路径,第二个参数是是否以追加到末尾的形式写入,这里如果要在文件末尾追加数据,就需要填写 true 即可~

1.2、特殊使用

1.2.1、如何表示文件读取完毕?(DataInputStream)

使用 read() 方法,返回一个 int 值,这个值如果是 -1,表示文件已经全部读取完毕~

但是实际的项目中,还常常使用一种顺水推舟方式表示文件读取完毕~ 如果我们约定数据的格式,是一个 int (表示 payload 的长度 )+ payload,后面也是一样格式的数据,那么这个时候,我们就需要通过 DataInputStream (这个流对象专门用来读取数字和字节流,必须搭配 DataOutputStream 使用)中的 readInt 方法来读取 这个 int,这个方法特殊就在于读取到文件末尾以后,继续读取就会抛出 EOFException 这个异常(以往我们读取到文件末尾都是返回 -1,或者是 null。),因此这里我们就可以 通过 catch 来捕获这个异常,表示读取完成~

Ps:值得注意的是,DataInputStream / DataOutputStream 可以方便进行数字的读写(readInt、writeInt),原生的 InputStream / OutputStream 没有提供数字读写方法,需要我们自己转化.

    public LinkedList<Message> loadAllMessageFromQueue(MSGQueue queue) throws IOException {
        //1.检查文件是否存在
        if(!checkQueueFileExists(queue.getName())) {
            throw new IOException("[MessageFileManager] 获取文件中所有有效消息时,发现队列文件不存在!queueName=" + queue.getName());
        }
        //2.获取队列中所有有效的消息
        synchronized (queue) {
            LinkedList<Message> messages = new LinkedList<>();
            try (InputStream inputStream = new FileInputStream(getQueueDataFilePath(queue.getName()))) {
                try (DataInputStream dataInputStream = new DataInputStream(inputStream)) {
                    int index = 0;
                    while(true) {
                        int messageSize = dataInputStream.readInt();
                        byte[] payload = new byte[messageSize];
                        int n = dataInputStream.read(payload);
                        if(n != messageSize) {
                            throw new IOException("[MessageFileManager] 读取消息格式出错!expectedSize=" + messageSize +
                                    ", actualSize=" + n);
                        }
                        //记录 offset
                        Message message = (Message) BinaryTool.fromBytes(payload);
                        if(message.getIsValid() == 0x0) {
                            index += (4 + messageSize);
                            continue;
                        }
                        message.setOffsetBeg(index + 4);
                        message.setOffsetEnd(index + 4 + messageSize);
                        messages.add(message);
                        index += (4 + messageSize);
                    }
                }
            } catch (EOFException e) {
                System.out.println("[MessageFileManager] 队列文件中有消息获取完成!queueName=" + queue.getName());
            }
            return messages;
        }
    }

1.2.2、字符读取/文本数据读取(Scanner)

对字符类型直接使用 InputStream 进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该工作,就是 Scanner 类。

Scanner 一般搭配 PrintWrite ,进行文本格式数据的读写,大大省去了 InputStream/OutputStream  还需要将 字节数据 和 文本数据 之间使用 UTF-8 解码转换的操作.

例如一:

// 需要先在项目目录下准备好一个 hello.txt 的文件,里面填充 "你好中国" 的内容

public class Main {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
           try (Scanner scanner = new Scanner(is, "UTF-8")) {
               while (scanner.hasNext()) {
                   String s = scanner.next();
                   System.out.print(s);
               }
           }
       }
   }
}

例如二: 

    public void writeStat(String queueName, Stat stat) {
        try (OutputStream outputStream = new FileOutputStream(getQueueStatFilePath(queueName))) {
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.write(stat.totalCount + "\t" + stat.validCount);
            printWriter.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public Stat readStat(String queueName) {
        Stat stat = new Stat();
        try (InputStream inputStream = new FileInputStream(getQueueStatFilePath(queueName))) {
            Scanner scanner = new Scanner(inputStream);
            stat.totalCount = scanner.nextInt();
            stat.validCount = scanner.nextInt();
            return stat;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

1.2.3、文件的随机读写(RandomAccessFile)

之前使用 DataInputStream / DataOutputStream 都是接收 FileInputStream/FileOutputStream 进行文件的顺序读写(要么是从头读到尾,要么是在尾部追加写入......),RandomAccessFile 类就特别在可以任意指定位置进行 读/写 操作!

这里涉及到光标的概念,实际上就是你写文件的时候,你写到哪个位置,哪个位置就会有一个光标一闪一闪~

在 RandomAccessFile 中,可以使用 seek() 方法指定光标的位置(单位是字节),例如你要对一个文件中的某一段内存进行逻辑删除(没有实际删除,只是先读出来标记为无效,然后在写回文件,回收站就差不多是这个逻辑).

    public void deleteMessage(MSGQueue queue, Message message) throws IOException {
        //1.检查队列相关文件是否存在
        if(!checkQueueFileExists(queue.getName())) {
            throw new IOException("[FileDataCenter] 删除消息时,发现队列相关文件不存在!queueName=" + queue.getName());
        }
        synchronized (message) {
            //2.将要删除的消息文件读出来
            try (RandomAccessFile randomAccessFile = new RandomAccessFile(getQueueDataFilePath(queue.getName()), "rw")) {
                randomAccessFile.seek(message.getOffsetBeg() - 4);
                int payloadSize = randomAccessFile.readInt();
                byte[] payload = new byte[payloadSize];
                int n = randomAccessFile.read(payload);
                if(n != payloadSize) {
                    throw new IOException("[FileDataCenter] 读取文件格式出错!path=" + getQueueDataFilePath(queue.getName()));
                }
                //3.将待删除的消息标记为无效(isValid = 0x0)
                Message toDeleteMessage = (Message) BinaryTool.fromBytes(payload);
                toDeleteMessage.setIsValid((byte) 0x0);
                //4.将消息写入文件
                randomAccessFile.seek(message.getOffsetBeg());
                randomAccessFile.write(BinaryTool.toBytes(toDeleteMessage));
            }
            //5.更新统计文件
            Stat stat = readStat(queue.getName());
            stat.validCount -= 1;
            writeStat(queue.getName(), stat);
        }
    }

Ps:

RandomAccessFile 的有两种构造器(实际上是一种),RandomAccessFile(String name, String mode)等价于RandomAccessFile(new File(name), String mode)

mode 这个参数表示 访问模式~
➢ "r":以只读方式打开指定文件。如果试图对该RandomAccessFile执行写入方法,都将抛出IOException异常。
➢ "rw":以读、写方式打开指定文件。如果该文件尚不存在,则尝试创建该文件。
➢ "rws":以读、写方式打开指定文件。相对于"rw"模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
➢ "rwd":以读、写方式打开指定文件。相对于"rw"模式,还要求对文件内容的每个更新都同步写入到底层存储设备。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/980078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UG\NX二次开发 选择基准平面 UF_UI_select_with_single_dialog

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 简介: 使用UF_UI_select_with_single_dialog函数,选择基准平面。 效果: 代码: #include "me.hpp"//过滤 基准平面 UF_datum_plane_type int InitProcDa…

虹科产品|HK-TrueNAS开放式存储平台被评为数字公益产品

一、HK-TrueNAS 被评为数字公益产品 数字公共产品联盟&#xff08;Digital Public Goods Alliance&#xff09;是一项多方利益相关者倡议&#xff0c;旨在促进数字公益&#xff08;DPG&#xff09;的发现、开发、使用和投资。数字公共产品的定义是所有人都能免费获取的资源或服…

Linux下的系统编程——进程间的通信(九)

一、进程间通信常用方式 IPC方式&#xff1a; Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&am…

螺母加工工艺流程

螺母是具有内螺纹并与螺栓配合使用的紧固件,具有内螺纹并与螺杆配合使用用以传递运动或动力的机械零件&#xff0c;是自动化行业中的重要传动零部件之一。大家知道螺母的加工工艺吗&#xff1f; 一般来说&#xff0c;螺母的加工工艺流程包括原材料采购、初加工、调质、精加工、…

vue基础知识六:v-show和v-if有什么区别?使用场景分别是什么?

一、v-show与v-if的共同点 我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含v-else)&#xff0c;都能控制元素在页面是否显示 在用法上也是相同的 <Model v-show"isShow" /> <Model v-if"isShow" />当表达式为true的时候&#…

网管实战⑼:配置华为S5720交换机

配置好汇聚交换机后&#xff0c;需要根据单位情况配置具体的接入交换机。 自从2019年12月底配置好交换机后&#xff0c;基本上都没有怎么操作交换机了。那时候使用的是H3C交换机&#xff0c;主要是H3C S7706、H3C S5120、H3C S5130、H3C S5500、H3C S3600等型号的交换机&#x…

微信公众号从0到1开发

之前做项目有就接触微信公众号的接入&#xff0c;但没有将过程记录成笔记&#xff0c;这几天在做的项目也是需要集成微信公众号&#xff0c;正好将在做的过程记录成笔记 文章目录 0、准备工作一、公众号平台1、参数解释2、获取域名 二、后端配置 一、第一阶段-公众号接入1、接…

第4节-PhotoShop基础课程-Ps格式

文章目录 前言1.像素认识2. 图层认识1.图层有上下前后遮挡关系2.橡皮檫可以擦掉选择图层的像素3.新建图层4.新建删除图层 3. 分辨率的理解4. 图片格式A 前言 本章主要介绍PS常用格式 1.像素认识 下面每个格子就是像素 2. 图层认识 1.图层有上下前后遮挡关系 2.橡皮檫可以擦…

elk安装篇之 Kibana安装

Kibana是一个开源的分析与可视化平台&#xff0c;设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。是es的可视化客户端之一。 一&#xff1a;下载 https://www.elastic.co/cn/kibana 我的es是elasticsearch-7.10.2版本&#x…

MQ解决重复消费问题

1. 消息重复消费概述 重复消费一直是行业内重视的问题&#xff0c;在当下的互联网时代&#xff0c;追求的是高效&#xff0c;安全&#xff0c;准确的数据交互。对于大型项目来讲&#xff0c;数据量数以亿计&#xff0c;那么这些数据如何确保安全准确&#xff0c;同时又不失效率…

用AI数字人视频带货新玩法教程

本期是赤辰第26期AI项目教程&#xff0c;底部准备了9月粉丝福利&#xff0c;可以免费领取。 今天给大家分享的AI项目是用AI数字人图文带货账号案例&#xff0c;这个账号是我2周前刷到的&#xff0c;今早闲着无事又刷到了这个账号数据已经飞起来了&#xff0c;第一条视频是8月1…

webhook--详解(gitee 推送)

一、简介 webhook 是一种基于 HTTP 的回调函数&#xff0c;可在 2 个应用编程接口&#xff08;API&#xff09;之间实现轻量级的事件驱动通信。是一种新型的前后端交互方式&#xff0c;一种对客户端-服务器模式的逆转&#xff0c;在传统方法中&#xff0c;客户端从服务器请求数…

提货卡礼品卡免登录提货程序开发

提货卡礼品卡免登录多活动H5小程序开发 适用于公司福利礼品卡提货&#xff0c;礼品公司提货卡。 功能&#xff1a; 支持多平台&#xff1a;基于Uniapp开发&#xff0c;可编译H5、微信小程序。 商品库模式&#xff1a;提货活动创建可以设置从商品库选择本活动可选的商品&am…

RKNPU2通用API和零拷贝API

RKNPU2通用API 通用API接口按照异构编程规范&#xff0c;需要将数据拷贝到NPU运行时的内存空间。 通用API部署流程 初始化上下文&#xff0c;需要先创建上下文对象和读取模型文件 rknn_context ctx; model load_model(model_path, &model_len); ret rknn_init(&ctx…

集合的进阶学习

集合体系结构 Collection 单列集合 包含List Set List 包含ArrayList LinkedList Set包含HashSet TreeSet HashSet包含LinkedHashSet List系列集合&#xff1a;添加的元素是有序的、可重复、有索引 Set系列集合&#xff1a;添加的元素是无序的、不重复、无索引 Collectio…

华为云云服务器评测|在Docker环境下部署Mysql数据库

华为云云服务器评测&#xff5c;在Docker环境下部署Mysql数据库 一、前言1.1 云耀云服务器L实例简介1.2 Mysql数据库简介 二、本次实践介绍2.1 本次实践简介2.2 本次环境规划 三、购买云耀云服务器L实例3.1 登录华为云3.2 购买云耀云服务器L实例3.3 查看云耀云服务器L实例状态3…

Windows wsl2安装Ubuntu

wsl&#xff08;Windows Subsystem for Linux&#xff09;即适用于Windows的Linux子系统&#xff0c;是一个实现在Windows 10 / 11上运行原生Linux的技术。 wsl2 为其迭代版本&#xff0c;可以更好的在Windows上运行Linux子系统。 这里以 Windows 11 安装Ubuntu作为示例。 开启…

浅识java多线程

目录 一 进程和线程定义 二 创建线程的种类 &#xff08;1&#xff09;继承java.lang.Thread &#xff08;2&#xff09;实现java.lang.Runnable接口 三 多线程 &#xff08;1&#xff09;继承java.lang.Thread多线程 &#xff08;2&#xff09;实现java.lang.Runnable…

vmware fusion12共享文件夹到虚拟机window10

文章目录 一、window10虚拟机安装VMware Tools二、MAC配置共享文件夹三、使用 一、window10虚拟机安装VMware Tools vmware fusion—虚拟机----安装VMware Tools–一路下一步 确认安装 双击进行安装 一路下一步&#xff0c;傻瓜式安装 二、MAC配置共享文件夹 设置—系…

2023年高教社杯全国大学生数学建模竞赛参赛事项注意

MathClub数模资源&#xff0c;含专属思路 资源链接&#xff1a;点击这里获取众多数模资料、思路精讲、论文模板latex和word、学习书籍等 2023高教社杯数学建模国赛–赛前准备 一年一度的数学建模国赛要来啦&#xff01;&#xff01;&#xff01;小编仔细阅读了比赛官方网站上…