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.转换流:

乱码的本质是 ​字符编码不匹配

  1. 写入时:文本按编码A(如UTF-8)转换为字节。
  2. 读取时:字节按编码B(如GBK)解码为字符。
  3. 结果:编码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),用于临时存储从底层流读取的数据。
  • 读取逻辑
    1. 当用户调用 read() 时,BufferedInputStream 会优先从缓冲区读取数据
    2. 如果缓冲区为空,它会一次性从底层 InputStream(如 FileInputStream)读取一批数据(填满缓冲区)。
    3. 后续的 read() 直接从缓冲区返回数据,直到缓冲区耗尽,再重复步骤 2。

   

  • 数据来源BufferedInputStream 本身不连接任何数据源(如文件、网络等),它只是一个“功能增强包装器”。
  • 依赖关系:缓冲流需要底层流提供原始数据,而 FileInputStream 是唯一能直接读取文件的节点流。

   装饰器模式(Decorator Pattern)的核心思想是 ​动态地为对象添加功能,同时保持接口的一致性。

  举一个咖啡加料的例子:

     假设你经营一家咖啡店,需要灵活组合咖啡和配料(如牛奶、糖),但不想为每种组合创建子类(如 MilkSugarCoffeeSugarCoffee 等)。装饰器模式可以完美解决这个问题

     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() 时:

  1. 检查缓冲区:如果有数据,直接返回。
  2. 缓冲区为空:调用底层 fileStream.read(byte[]) 批量读取数据到缓冲区。
  3. 返回数据:从缓冲区返回一个字节。

其实吧,处理流(如 BufferedInputStream)需要传入 InputStream 对象的核心目的,正是为了在自己的成员方法中调用底层流的 read 方法,并在其基础上添加额外功能(如缓冲、编码转换等)。这是装饰器模式的精髓所在。


总结

以上就是今天要讲的内容,本文仅简单的讲述了IO流分类后的使用和例子,然后讲了一下修饰器模式,接下来我会一直持续更新,谢谢大家。

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

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

相关文章

【设计模式】SOLID 设计原则概述

SOLID 是面向对象设计中的五大原则,不管什么面向对象的语言, 这个准则都很重要,如果你没听说过,赶紧先学一下。它可以提高代码的可维护性、可扩展性和可读性,使代码更加健壮、易于测试和扩展。SOLID 代表以下五个设计原…

可发1区的超级创新思路:基于注意力机制的DSD-CNN时间序列预测模型(功率预测、交通流量预测、故障检测)

首先声明,该模型为原创!原创!原创! 一、应用场景 该模型主要用于时间序列数据预测问题,包含功率预测、电池寿命预测、电机故障检测等等 二、模型整体介绍(本文以光伏功率预测为例) DSD-CNN(Depthwise-Spacewise Separable CNN)结合通道注意力机制,通过以下创新提升…

wsl2配置xv6全解(包括22.04Jammy)

文章目录 获取xv6源代码Ubuntu20.04 Version安装指令成功测试参考MIT2021年官方文档 24.04 Version安装指令成功测试参考MIT2024年官方文档 Ubuntu 22.04没有官方文档? 配置大体流程1. 卸载原本qemu(如果之前安装了)2. clone qemu官方源代码&…

招聘面试季--一文顿悟,Java中字节流和字符流的区别及使用场景上的差异

‌一、核心区别‌ ‌特性‌‌字节流‌‌字符流‌‌数据单位‌以字节(8-bit)为单位处理数据(如0xA1)以字符(16-bit Unicode)为单位处理数据(如A, 你)‌基类‌InputStream / OutputSt…

车载以太网网络测试-16【传输层-UDP】

目录 1 摘要2 车载以太网传输层概述3 车载以太网UDP协议3.1 车载以太网UDP协议的作用3.2 UDP报文帧结构3.3 UDP协议的通信过程3.3.1 通信过程3.3.2 实例示例3.3.3 代码示例 4 总结 1 摘要 车载以太网的第五层是传输层,它在车载网络架构中扮演着至关重要的角色。主要…

深度强化学习中的深度神经网络优化策略:挑战与解决方案

I. 引言 深度强化学习(Deep Reinforcement Learning,DRL)结合了强化学习(Reinforcement Learning,RL)和深度学习(Deep Learning)的优点,使得智能体能够在复杂的环境中学…

无人机点对点技术要点分析!

一、技术架构 1. 网络拓扑 Ad-hoc网络:无人机动态组建自组织网络,节点自主协商路由,无需依赖地面基站。 混合架构:部分场景结合中心节点(如指挥站)与P2P网络,兼顾集中调度与分布式协同。 2.…

MQ,RabbitMQ,MQ的好处,RabbitMQ的原理和核心组件,工作模式

1.MQ MQ全称 Message Queue(消息队列),是在消息的传输过程中 保存消息的容器。它是应用程序和应用程序之间的通信方法 1.1 为什么使用MQ 在项目中,可将一些无需即时返回且耗时的操作提取出来,进行异步处理&#xff0…

django怎么配置404和500

在 Django 中,配置 404 和 500 错误页面需要以下步骤: 1. 创建自定义错误页面模板 首先,创建两个模板文件,分别用于 404 和 500 错误页面。假设你的模板目录是 templates/。 404 页面模板 创建文件 templates/404.html&#x…

各类神经网络学习:(四)RNN 循环神经网络(下集),pytorch 版的 RNN 代码编写

上一篇下一篇RNN(中集)待编写 代码详解 pytorch 官网主要有两个可调用的模块,分别是 nn.RNNCell 和 nn.RNN ,下面会进行详细讲解。 RNN 的同步多对多、多对一、一对多等等结构都是由这两个模块实现的,只需要将对输入…

深度学习篇---深度学习中的范数

文章目录 前言一、向量范数1.L0范数1.1定义1.2计算式1.3特点1.4应用场景1.4.1特征选择1.4.2压缩感知 2.L1范数(曼哈顿范数)2.1定义2.2计算式2.3特点2.4应用场景2.4.1L1正则化2.4.2鲁棒回归 3.L2范数(欧几里得范数)3.1定义3.2特点3…

星越L_灯光操作使用讲解

目录 1.开启前照灯 2左右转向灯、远近灯 3.auto自动灯光 4.自适应远近灯光 5.后雾灯 6.调节大灯高度 1.开启前照灯 2左右转向灯、远近灯 3.auto自动灯光 系统根据光线自动开启灯光

Stable Diffusion lora训练(一)

一、不同维度的LoRA训练步数建议 2D风格训练 数据规模:建议20-50张高质量图片(分辨率≥10241024),覆盖多角度、多表情的平面风格。步数范围:总步数控制在1000-2000步,公式为 总步数 Repeat Image Epoch …

AI 生成 PPT 网站介绍与优缺点分析

随着人工智能技术不断发展,利用 AI 自动生成 PPT 已成为提高演示文稿制作效率的热门方式。本文将介绍几款主流的 AI PPT 工具,重点列出免费使用机会较多的网站,并对各平台的优缺点进行详细分析,帮助用户根据自身需求选择合适的工具…

使用Systemd管理ES服务进程

Centos中的Systemd介绍 CentOS 中的 Systemd 详细介绍 Systemd 是 Linux 系统的初始化系统和服务管理器,自 CentOS 7 起取代了传统的 SysVinit,成为默认的初始化工具。它负责系统启动、服务管理、日志记录等核心功能,显著提升了系统的启动速…

【一维前缀和与二维前缀和(简单版dp)】

1.前缀和模板 一维前缀和模板 1.暴力解法 要求哪段区间,我就直接遍历那段区间求和。 时间复杂度O(n*q) 2.前缀和 ------ 快速求出数组中某一个连续区间的和。 1)预处理一个前缀和数组 这个前缀和数组设定为dp,dp[i]表示:表示…

在Windows和Linux系统上的Docker环境中使用的镜像是否相同

在Windows和Linux系统上的Docker环境中使用的镜像是否相同,取决于具体的运行模式和目标平台: 1. Linux容器模式(默认/常见场景) Windows系统: 当Windows上的Docker以Linux容器模式运行时(默认方式&#xf…

植物来源药用天然产物的合成生物学研究进展-文献精读121

植物来源药用天然产物的合成生物学研究进展 摘要 大多数药用天然产物在植物中含量低微,提取分离困难;而且这些化合物一般结构复杂,化学合成难度大,还容易造成环境污染。基于合成生物学技术获得药用天然产物具有绿色环保和可持续发…

JavaScript |(五)DOM简介 | 尚硅谷JavaScript基础实战

学习来源:尚硅谷JavaScript基础&实战丨JS入门到精通全套完整版 笔记来源:在这位大佬的基础上添加了一些东西,欢迎大家支持原创,大佬太棒了:JavaScript |(五)DOM简介 | 尚硅谷JavaScript基础…

浏览器工作原理深度解析(阶段二):HTML 解析与 DOM 树构建

一、引言 在阶段一中,我们了解了浏览器通过 HTTP/HTTPS 协议获取页面资源的过程。本阶段将聚焦于浏览器如何解析 HTML 代码并构建 DOM 树,这是渲染引擎的核心功能之一。该过程可分为两个关键步骤:词法分析(Token 化)和…