JavaIO流的使用和修饰器模式(直击心灵版)
系列文章目录
JavaIO流的使用和修饰器模式
文章目录
- 系列文章目录
- 前言
- 一、字节流:
- 1.FileInputStream(读取文件)
- 2.FileOutputStream(写入文件)
- 二、字符流:
- 1..基础字符流:
- 2.处理流:
- 3.对象处理流:
- 4.转换流:
- 三、修饰器模式
- 总结
前言
前面我们讲解了Java文件和IO流的基础部分。把流简单的分了一下类,但是我们还不知道具体是如何是使用的,下面我们将详细的讲解一下这些个流各自的职责是什么,简言之就是各自的使用方式。然后我还想给大家强戴一下IO流当中的修饰器模式,因为这个实际上通过封装真的太牛逼了。
我先给大家按字节流和字符流的分类方式来进行讲述:
一、字节流:
用于处理二进制数据(如图片、视频、任何文件),核心类为 InputStream 和 OutputStream
(1)FileInputStream(读取文件)
每次调用 read() 方法从磁盘读取1字节,频繁IO操作性能差。
适用场景:小文件读取或需要逐字节处理的场景。
try (InputStream in = new FileInputStream("test.jpg")) {int byteData;while ((byteData = in.read()) != -1) { // 每次读取1字节// 处理字节(例如加密、校验)System.out.print((char)byteData + " ");}
} catch (IOException e) {e.printStackTrace();
}
我们要注意,这样单个字节读取,如果文件当中有汉字就不行了。
所以进阶版可以用int read(byte[] b)方法来读取,这个方法底层是从该输入流中读取最多b.length字节数据到字节数组,如果读取正常,返回实际读取字节数, -1表示的是读取完毕了。但记得最后还要转换为字符串 new String(buf,0,readlen).
(2) FileOutputStream(写入文件)
注意点:若文件不存在会自动创建,若存在默认覆盖(通过构造参数可设置为追加模式)。
// 第二个参数 true 表示追加写入
try (OutputStream out = new FileOutputStream("log.txt", true)) {String logEntry = "Error occurred at " + new Date() + "\n";out.write(logEntry.getBytes(StandardCharsets.UTF_8)); // 显式指定编码
} catch (IOException e) {e.printStackTrace();
}
这里还有一处细节要注意,就是这样创建,写入内容会覆盖原来的内容,但如果是这样创建的 new FileOutputStream(filepath,true),这样再写入内容就会追加到文件后面。
二、字符流
1.基础字符流:
(1)FileReader 读文件
这里循环读取使用read()是单个字符读取,使用read(buf)返回的是实际取到的字符数.
(2)FileWriter 写文件
这里面注意一定要关闭流,或者Flush才能真正的把数据写入到文件
2.处理流:
BufferedReader 和 BufferedWriter:
readLine() 可逐行读取文本。
// 读取CSV文件并解析
try (BufferedReader br = new BufferedReader(new FileReader("data.csv"))) {String line;while ((line = br.readLine()) != null) {String[] columns = line.split(",");// 处理每一列数据}
}// 写入带换行的文本
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {bw.write("Line 1");bw.newLine(); // 跨平台换行(Windows为\r\n,Linux为\n)bw.write("Line 2");
}
像BufferedReader类中,有属性Reader,即可以封装一个节点流 (该节点流可以是任意的,只要是Reader的子类就行,这个我们下面讲修饰器模式再讲)。
details:
1.BufferedReader 和 BufferedWriter都是按照字符操作的。
2.不要去操作二进制文件(如声音,视频等)可能会造成文件损坏。
总结:
| 场景 | 正确流类型 | 原因 |
|---|---|---|
| 图片、视频、EXE文件 | 字节流 | 直接处理原始字节,避免编解码干扰 |
| 文本文件(.txt) | 字符流 | 正确处理字符编码(如UTF-8、GBK) |
| 混合数据(如PDF) | 字节流 | PDF包含文本和二进制结构,需精确控制字节 |
| 网络传输数据 | 字节流 | 网络协议基于字节,而非字符 |
所以说字符流是“文本专用工具”,操作二进制文件就像用剪刀拧螺丝——不仅费力,还可能搞砸!
3.对象处理流:
能够将基本数据类型或者对象进行序列化和反序列化的操作。

这里我们需要注意的是如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,而为了让某个类是可序列化的,该类必须实现如下两个接口之一:
Serializable 和 Externalizable 我们常用的是Serializable接口,因为它不用再重写方法了。
class User implements Serializable {private static final long serialVersionUID = 1L; // 版本号private String name;private transient String password; // transient字段不会被序列化
}// 序列化对象到文件
User user = new User("Alice", "secret");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"))) {oos.writeObject(user);
}// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"))) {User restoredUser = (User) ois.readObject();System.out.println(restoredUser.getName()); // 输出 "Alice"System.out.println(restoredUser.getPassword()); // 输出 null(transient字段)
}
注意读取(反序列化)的顺序需要和保存数据(序列化)的顺序一致,否则会出现异常。
还有最容易忽略的一点就是序列化对象时,要求里面的属性的类型也需要实现序列化接口。
序列化对象时,默认将里面所有属性都会进序列化,除了static或者transient修饰的成员。
4.转换流:
乱码的本质是 字符编码不匹配:
- 写入时:文本按编码A(如UTF-8)转换为字节。
- 读取时:字节按编码B(如GBK)解码为字符。
- 结果:编码A和编码B的映射关系不同,导致字符显示错误。
转换流的作用
| 类名 | 功能 | 核心价值 |
|---|---|---|
InputStreamReader | 将字节流(InputStream)按指定编码转换为字符流 | 解决读取时的编码问题 |
OutputStreamWriter | 将字符流按指定编码转换为字节流(OutputStream) | 解决写入时的编码问题 |
try (Reader reader = new InputStreamReader(new FileInputStream("utf8_file.txt"), StandardCharsets.UTF_8)) {// 正确读取中文字符int data;while ((data = reader.read()) != -1) {System.out.print((char) data);}
}try (Reader reader = new InputStreamReader(new FileInputStream("utf8_file.txt"), StandardCharsets.UTF_8)) {// 正确读取中文字符int data;while ((data = reader.read()) != -1) {System.out.print((char) data);}
}
综上可知,学习IO流我们必须要知道什么时候使用什么流。
三、修饰器模式:
其实我在学习的过程中也很疑惑这个修饰器模式到底有什么用,不就是像套娃一样一层套着一层吗,但是当我们真正理解了才发现Java设计者有多牛逼。
像以BufferedInputStream举例:
BufferedInputStream 的缓冲机制
- 内部缓冲区:
BufferedInputStream维护一个字节数组(默认大小 8KB),用于临时存储从底层流读取的数据。 - 读取逻辑:
- 当用户调用
read()时,BufferedInputStream会优先从缓冲区读取数据。 - 如果缓冲区为空,它会一次性从底层
InputStream(如FileInputStream)读取一批数据(填满缓冲区)。 - 后续的
read()直接从缓冲区返回数据,直到缓冲区耗尽,再重复步骤 2。
- 当用户调用
- 数据来源:
BufferedInputStream本身不连接任何数据源(如文件、网络等),它只是一个“功能增强包装器”。 - 依赖关系:缓冲流需要底层流提供原始数据,而
FileInputStream是唯一能直接读取文件的节点流。
装饰器模式(Decorator Pattern)的核心思想是 动态地为对象添加功能,同时保持接口的一致性。
举一个咖啡加料的例子:
假设你经营一家咖啡店,需要灵活组合咖啡和配料(如牛奶、糖),但不想为每种组合创建子类(如 MilkSugarCoffee、SugarCoffee 等)。装饰器模式可以完美解决这个问题
1.定义基础组件:
// 基础接口:咖啡
public interface Coffee {double getCost();String getDescription();
}// 具体组件:基础咖啡
public class SimpleCoffee implements Coffee {@Overridepublic double getCost() { return 2.0; }@Overridepublic String getDescription() { return "基础咖啡"; }
}
2. 定义装饰器基类:
// 装饰器基类:实现 Coffee 接口,并持有一个 Coffee 对象
public abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}// 委托给被装饰的 Coffee 对象@Overridepublic double getCost() { return decoratedCoffee.getCost(); }@Overridepublic String getDescription() { return decoratedCoffee.getDescription(); }
}
3. 具体修饰器:牛奶或糖:
// 牛奶装饰器
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() { return super.getCost() + 0.5; }@Overridepublic String getDescription() { return super.getDescription() + "+牛奶"; }
}// 糖装饰器
public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() { return super.getCost() + 0.2; }@Overridepublic String getDescription() { return super.getDescription() + "+糖"; }
}
4.使用修饰器的动态组合:
public class Main {public static void main(String[] args) {// 基础咖啡Coffee coffee = new SimpleCoffee();System.out.println(cost: " + coffee.getCost() + ", desc: " + coffee.getDescription());// 加牛奶coffee = new MilkDecorator(coffee);System.out.println(cost: " + coffee.getCost() + ", desc: " + coffee.getDescription());// 再加糖coffee = new SugarDecorator(coffee);System.out.println(cost: " + coffee.getCost() + ", desc: " + coffee.getDescription());}
}
而在IO流中:
- 组件接口:
InputStream(所有输入流的基类)。 - 具体组件:
FileInputStream(直接操作文件的节点流)。 - 装饰器基类:
FilterInputStream(实现InputStream,并持有InputStream对象)。 - 具体装饰器:
BufferedInputStream(扩展FilterInputStream,添加缓冲功能)。
// 节点流:直接读取文件
InputStream fileStream = new FileInputStream("data.txt");// 装饰器:添加缓冲功能
InputStream bufferedStream = new BufferedInputStream(fileStream);// 可以继续装饰:例如添加解密功能(假设有 DecryptInputStream)
InputStream decryptedStream = new DecryptInputStream(bufferedStream);
当调用 bufferedStream.read() 时:
- 检查缓冲区:如果有数据,直接返回。
- 缓冲区为空:调用底层
fileStream.read(byte[])批量读取数据到缓冲区。 - 返回数据:从缓冲区返回一个字节。
其实吧,处理流(如 BufferedInputStream)需要传入 InputStream 对象的核心目的,正是为了在自己的成员方法中调用底层流的 read 方法,并在其基础上添加额外功能(如缓冲、编码转换等)。这是装饰器模式的精髓所在。
总结
以上就是今天要讲的内容,本文仅简单的讲述了IO流分类后的使用和例子,然后讲了一下修饰器模式,接下来我会一直持续更新,谢谢大家。
相关文章:
JavaIO流的使用和修饰器模式(直击心灵版)
系列文章目录 JavaIO流的使用和修饰器模式 文章目录 系列文章目录前言一、字节流: 1.FileInputStream(读取文件)2.FileOutputStream(写入文件) 二、字符流: 1..基础字符流:2.处理流:3.对象处理流:4.转换流: 三、修饰器…...
爬虫入门re+bs4
目录 前言 1. 导入必要的库 2. 定义获取网页HTML内容的函数 get_html 3. 定义获取数据的函数 get_data 4. 定义获取文章正文内容的函数 content_text 5. 定义获取单条课程数据的函数 get_one_course_data 6. 定义保存数据的函数 save_data 7. 定义文件名合法化处理函数 sanitiz…...
【WebGL】texImage2D函数
参数 从像素数据加载纹理 gl.texImage2D(target, level, internalformat, width, height, border, format, type, source);从图像元素加载纹理 gl.texImage2D(target, level, internalformat, format, type, image);target gl.TEXTURE_2D(2D 纹理) T…...
北斗设备启动流程与时长解析
北斗卫星导航系统作为我国自主研发的全球卫星导航系统,广泛应用于交通、通信、农业等多个领域。今天,我们就来详细探讨一下北斗设备的启动流程以及不同启动方式下的时长。 一、北斗设备的启动流程 北斗设备的启动流程可以分为以下几个关键步骤…...
MySQL身份验证的auth_socket插件
在Ubuntu 20.04 LTS上,MySQL 8.0默认使用auth_socket插件进行身份验证,可能存在意想不到的情况。 一、auth_socket插件 在使用sudo mysql或通过sudo切换用户后执行任何MySQL命令时,不需要输入密码或错误密码都可以正常登入mysql数据库&…...
openstack安装部署
在OpenStack的安装和部署中,你需要按照一定的步骤来完成整个环境的搭建。OpenStack是一个开源的云计算平台,它提供了基础设施即服务(IaaS)的能力,包括计算、存储和网络等资源的管理。下面是一些基本的步骤来安装和部署…...
【日志库】—— log4cpp 部署套路
部署: 1、安装log4cpp,执行如下指令进行编译安装 log4cpp的官网是: http://log4cpp.sourceforge.net/ wget https://nchc.dl.sourceforge.net/project/log4cpp/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-1.1.3.tar.gz tar xzvf log4cpp…...
使用Gitee Go流水线部署个人项目到服务器指南
使用Gitee Go流水线部署个人项目到服务器指南 前言!!! 本文解决的问题: 你有一台ECS服务器,你在上面部署了一个Java服务也就是一个jar,你觉着你每次手动本地打包,上传,在通过命令去…...
BlockChain.java
BlockChain 区块链,举个栗子 注意啦,列子里面的hashcode相等,但是字符串是不一样的哦,之前有记录这个问题 String.hashCode()-CSDN博客...
SystemVerilog 数据类型
1、内建数据类型 verilog有两种基本的数据类型:变量和线网,他们各自都可以有四种取值:0 1 z x; RTL代码使用 变量 来存放组合和时序值;变量可以是单bit或者是多bit的无符号数 reg [7:0] m, 32bit的有符号…...
【技术简析】触觉智能RK3506 Linux星闪网关开发板:重新定义工业物联新标杆
在工业智能化与物联网深度融合的今天,深圳触觉智能推出首款搭载瑞芯微RK3506芯片的Linux星闪网关开发板,为大家技术解析。 RK3506-国产芯的硬核实力 作为瑞芯微2024年第四季度推出的入门级工业芯片平台,RK3506以三核Cortex-A7(1.…...
YOLO-UniOW: 高效通用开放世界目标检测模型【附论文与源码】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
面向对象(进阶)(‘封装‘,‘多态‘,‘对象属性‘,‘类属性‘,‘类方法‘,‘对象方法‘及其应用场景)
‘封装’,多态’及其应用场景 一, 封装及其应用场景 封装的定义 属于面向对象的三大特征之一, 就是隐藏对象的属性和现实细节, 仅对外提供公共的访问方式.(我们学的 函数, 类, 都是封装的体现). 封装的格式 __属性名 __属性名()封装的好处和弊端 好处 提高代码的安全性.…...
django如何配置使用asgi
以下是 Django 配置使用 ASGI 的完整指南: 一、配置前提 Django 版本:确保使用 Django 3.0(原生支持 ASGI)必要依赖:pip install daphne channels二、基础配置步骤 1. 创建/修改 ASGI 入口文件 在 Django 项目根目录…...
MongoDB 只能存储能够序列化的数据(比如字符串、数字等),而 Python 的 UUID 对象并不是直接可以存入数据库的格式。
1. UUID 对象是什么? UUID 是 “Universally Unique Identifier”(通用唯一识别码)的缩写,是一种 128 位的数字,用于在全局范围内生成一个唯一的标识符。它常用于数据库中的记录标识、分布式系统中的对象标识等场景。…...
分页优化之——游标分页
游标分页(Cursor-based Pagination) 是一种高效的分页方式,特别适用于大数据集和无限滚动的场景。与传统的基于页码的分页(如 page1&size10)不同,游标分页通过一个唯一的游标(通常是时间戳或…...
算法模型从入门到起飞系列——广度优先遍历(BFS)
文章目录 前言一、广度优先遍历(BFS)简介1.1 广度优先遍历(BFS)的特点1.2 广度优先遍历(BFS)的要素 二、广度优先遍历(BFS)& 深度优先遍历(DFS)2.1 广度优…...
AsyncHttpClient使用说明书
[[toc]] AsyncHttpClient(AHC)是一个高性能、异步的 HTTP 客户端库,广泛用于 Java 和 Scala 应用中,特别适合处理高并发、非阻塞的 HTTP 请求。它基于 Netty 或 Java 原生的异步 HTTP 客户端实现,支持 HTTP/1.1 和 HTTP/2 协议,适用于微服务、API 调用、爬虫等场景。 1.…...
FRP在远程办公中的实战应用
远程办公场景中,FRP可穿透企业防火墙,安全访问内网资源。以下是典型用例: SSH远程连接 配置示例: 客户端配置SSH映射,将本地22端口映射至公网服务器的6000端口,用户通过ssh -p 6000 user公网IP即可连接内网…...
git 设置保存密码 git保存密码
目录 长久保存密码 长久保存密码 git push和git pull都能使用。 git config --global credential.helper store 然后执行一次 git pull,Git 会提示输入用户名和密码, 输入后保存路径: ~/.git-credentials , Windows系统&…...
<项目> 主从Reactor模型的高并发服务器
目录 Reactor 概念 分类 单Reactor单线程 单Reactor多线程 多Reactor多线程 项目介绍 项目规划 模块关系 实现 TimerWheel -- 时间轮定时器 定时器系统调用 时间轮设计 通用类型Any Buffer Socket Channel Poller EventLoop(核心) eventfd 设计思路 …...
注意力机制,本质上是在做什么?
本文以自注意机制为例,输入一个4*4的矩阵 如下: input_datatorch.tensor([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16] ],dtypetorch.float) 得到Q和K的转置如下。 此时,计算QK^T ,得到如下结果 第一行第一个位置就是第一条样本和第…...
使用Python在Word中创建、读取和删除列表 - 详解
目录 工具与设置 Python在Word中创建列表 使用默认样式创建有序(编号)列表 使用默认样式创建无序(项目符号)列表 创建多级列表 使用自定义样式创建列表 Python读取Word中的列表 Python从Word中删除列表 在Word中ÿ…...
右键添加:新建HTML模板文件
使用注册表给Windows右键添加:新建HTML文档模板的功能_注册表右键新建-CSDN博客 新建文件有了,但是没有引用模板文件,是空文件。 默认改成 htmlfile 模板成功...
Windows10配置OpenJDK11
下载 # 华为OpenJDK镜像源 https://mirrors.huaweicloud.com/openjdk/11.0.2/解压 # 解压后至于C:\Dev\Env\Java\jdk-11.0.2目录下 https://mirrors.huaweicloud.com/openjdk/11.0.2/openjdk-11.0.2_windows-x64_bin.zip编译安装 # 以管理员身份运行 CMD命令提示符 并进入JD…...
浅谈Go垃圾回收机制-面试笔记
Go 语言的垃圾回收机制(Garbage Collection,GC)是其内存管理的重要组成部分,以下是相关介绍: 1、基本原理 标记 - 清除算法:Go 语言的垃圾回收主要基于三色标记 - 并发清除算法。首先,从根对象…...
统一开放世界与开放词汇检测:YOLO-UniOW无需增量学习的高效通用开放世界目标检测框架
目录 一、摘要 二、引言 三、相关工作 开放词汇对象检测 开放世界目标检测 参数高效学习 四、高效通用的开放世界目标检测 问题定义 高效的自适应决策学习 开放世界通配符学习 五、Coovally AI模型训练与应用平台 六、实验 数据集 评价指标 实施细节 定量结果 …...
如何给商品一键换色?图生生AI,告别繁琐修图
在电商竞争日益激烈的今天,商品图片的视觉效果直接影响着消费者的购买决策。而商品颜色的展示,更是重中之重!传统的图片换色方式,往往需要耗费设计师大量的时间和精力,从抠图到调色,再到细节调整࿰…...
AIGC-名人语录账号运营创作智能体完整指令(DeepSeek,豆包,千问,Kimi,GPT)
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列AIGC(GPT、DeepSeek、豆包、千问、Kimi)...
基于MySQL的创建Java实体Bean和TypeScript实体Bean
基于MySQL的创建Java实体Bean和TypeScript实体Bean 基于MySQL的创建Java实体Bean和TypeScript实体Bean select ORDINAL_POSITION as a, -- Data -- Schema(description "货物管理表") -- TableName("lpg_cargo") -- public class CargoEntity implements…...
