Java IO详解

2023-10-26

一、I/O简介

IO即Input和Output,即输入和输出。这里的输入和输出都是相对于内存来说的,具体见下图。
在这里插入图片描述
InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。
字节是计算机存储容量的基本单位(Byte),1B=8b,二进制中占8位。
字符是文字或符号的统称。
字节流什么类型的文件都可以读取,字符流只能读取纯文本文件。

二、字节流

1、InputStream && OutputStream

InputStream 类图如下:
在这里插入图片描述
OutputStream类图如下:
在这里插入图片描述

从jdk8文档中,InputStream 方法如下:
在这里插入图片描述
OutputStream方法如下:
在这里插入图片描述
从 Java 9 开始,InputStream 新增加了多个实用的方法:

readAllBytes():读取输入流中的所有字节,返回字节数组。
readNBytes(byte[] b, int off, int len):阻塞直到读取 len 个字节。
transferTo(OutputStream out):将所有字节从一个输入流传递到一个输出流。

2、FileInputStream && FileOutputStream

我们日常使用的话,比较常见的就是FileInputStream了,其可直接指定文件路径,可以直接读取单字节数据,也可以读取至字节数组中。
FileInputStream 示例代码如下:

 public static void main(String[] args) {
        try {
            FileInputStream fileInputStream = new FileInputStream("inputTest.txt");
            //1、返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。
            int available = fileInputStream.available();
            System.out.println("available:" + available);
            //2、跳过并丢弃来自此输入流的 2字节数据。
            long skip = fileInputStream.skip(2);
            System.out.println("skip:" + skip);
            //3、读取文件内容,为了减少IO,我们创建一个Byte数组作为接收缓冲区
            byte[] bytes = new byte[4];
            int read;
            //4、从输入流读取一些字节数,并将它们存储到缓冲区 b 。
            while ((read = fileInputStream.read(bytes)) != -1) {
                // 把byte数组转换成字符串
                System.out.print(new String(bytes, 0, read));
            }
            if (fileInputStream != null) {
                fileInputStream.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

inputTest.txt文件如下:
在这里插入图片描述
输出结果如下:

available:11
skip:2
adafadsff

FileOutputStream有如下构造方法:

public FileOutputStream(String name, boolean append)

boolean append为true表示在创建的文件存在时,不会清空原文件内容,以追加的方式写入。
若文件不存在,则新增文件。
示例代码如下:

 public static void main(String[] args) {
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("outputTest.txt");
            String s = "lalalaaadhawe";
            // 将字符串转换为byte数组
            byte[] bytes = s.getBytes();
            fileOutputStream.write(bytes);
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

运行结果如下:
在这里插入图片描述

3、字节流的文件复制

学完了FileInputStream && FileOutputStream,我们可以利用其做文件复制,代码如下:

public static void main(String[] args) {
        try {
            FileInputStream fileInputStream = new FileInputStream("inputTest.txt");
            FileOutputStream fileOutputStream = new FileOutputStream("copy.txt");
            //一次拷贝4个字节
            byte[] bytes = new byte[4];
            int read;
            //4、从输入流读取一些字节数,并将它们存储到缓冲区 b 。
            while ((read = fileInputStream.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, read);
            }
            fileOutputStream.flush();
            if (fileInputStream != null) {
                fileInputStream.close();
            }
            if (fileOutputStream != null) {
                fileOutputStream.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4、BufferedInputStream && BufferedOutputStream

我们通过上面代码,可以看到,为了减少IO次数,可以设立一个缓冲池byte,提高读取效率。而BufferedInputStream 和BufferedOutputStream的设计初衷就是如此,通常我们也是使用带缓冲区的BufferedInputStream ,其源码如下:

public
class BufferedInputStream extends FilterInputStream {
    // 内部缓冲区数组
    protected volatile byte buf[];
    // 缓冲区的默认大小
    private static int DEFAULT_BUFFER_SIZE = 8192;
    // 使用默认的缓冲区大小
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
    // 自定义缓冲区大小
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
}

5、DataInputStream && DataOutputStream

这一对类可以直接写入java基本类型数据(没有String),但写入以后是一个二进制文件的形式,不可以直接查看。
示例代码如下:

DataInputStream dis = new DataInputStream(fileInputStream);
            dis.readInt();
            /**
             * 即使存入越界的树65538,也不会报错,因为超出部分不会被存入,存入的只是超出的部分。
             * short类型占据16位的空间,因此将65538转为二进制数,超出16位的部分自动截掉,只保留16为以内的数据。
             */
            dis.readShort();
            dis.readLong();
            dis.readByte();
            dis.readChar();
            dis.readDouble();
            dis.close()

6、序列化和反序列化 ObjectInputStream && ObjectOutputStream

Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是将字节序列转换成目标对象的过程。具体参考:java中的序列化和transient关键字
ObjectInputStream 用于从输入流中读取 Java 对象(反序列化),ObjectOutputStream 用于将对象写入到输出流(序列化)。
示例如下:

ObjectInputStream input = new ObjectInputStream(new FileInputStream("object.data"));
MyClass object = (MyClass) input.readObject();
input.close();

7、打印流PrintStream

PrintStream源自OutputStream,其为标准字节的输出流,默认输出到控制台,也就是我们常用的System.out.println()方法。
在这里插入图片描述
日志框架的实现原理就是通过PrintStream的使用,输出到日志文件,示例如下:

public static void log(String msg) {
        try {
            // 指向一个日志文件
            PrintStream ps = new PrintStream(new FileOutputStream("log.txt",true));
            // 改变流的输出方向
            System.setOut(ps);
            // 获取系统当前时间
            Date nowTime = new Date();
            // 日期格式化
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
            String strTime = sdf.format(nowTime);
            System.out.println(strTime+": "+msg);
            ps.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        log("第1次打印日志");
        log("第2次打印日志");
        log("第3次打印日志");
    }

运行结果如图:
在这里插入图片描述

三、字符流

首先,确认一个问题,已经有字节流了,可以读取任意文件,为什么还要有字符流呢?
主要有以下原因:
1、对于字符文件,先作为字节传输,再转成字符,耗时耗力。
2、对于字符文件,转成字节之后,再转回来,如果是中文,很容易乱码。

1、Reader && Writer

类图如下:
在这里插入图片描述
在这里插入图片描述

2、FileReader && FileWriter

字符流常用的读写类为FileReader 与 FileWriter,我们还用复制文件来做示例,示例代码如下:

 public static void main(String[] args) throws IOException {
            FileReader fileReader = new FileReader("file.txt");
            FileWriter fileWriter = new FileWriter("file2.txt");
            //一次拷贝4个字节
            char[] chars = new char[1024*1024];
            int read;
            while ((read = fileReader.read(chars)) != -1) {
                fileWriter.write(chars, 0, read);
            }
            fileWriter.flush();
            if (fileReader != null) {
                fileReader.close();
            }
            if (fileWriter != null) {
                fileWriter.close();
            }

    }

执行结果如下:
在这里插入图片描述
中文没有乱码。
BufferedReader和BufferedWriter,类似于BufferedInputStream和BufferedOutputStream,内部都维护了一个字节数组作为缓冲区。

四、随机访问流RandomAccessFile

RandomAccessFile 支持随意跳转到文件的任意位置进行读写,任意位置进入文件。
RandomAccessFile 比较常见的就是断点续传,适用于大文件的分片上传,后面单独开一篇文章进行总结。

五、IO中的设计模式

1、适配器模式

适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。具体参考:java(面向对象)的23种设计模式(5)——适配器模式。
在Java IO中,为了实现字符流和字节流之间的相互转换,就产生了两个适配器的类,InputStreamReader 和 OutputStreamWriter 。
代码如下:

InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
BufferedReader bufferedReader = new BufferedReader(isr);

2、装饰器模式

装饰器模式可以将新功能动态地附加于现有对象而不改变现有对象的功能。具体参考:java(面向对象)的23种设计模式(7)——装饰模式。
InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。增强了子类对象的功能。

3、工厂模式

工厂模式,通过定义工厂父类负责创建对象的公共接口,而子类则负责生成具体的对象。具体参考:
java(面向对象)的23种设计模式(3)——工厂模式。

NIO 中大量用到了工厂模式,比如 Files 类的 newInputStream 方法用于创建 InputStream 对象(静态工厂)、 Paths 类的 get 方法创建 Path 对象(静态工厂)、ZipFileSystem 类(sun.nio包下的类,属于 java.nio 相关的一些内部实现)的 getPath 的方法创建 Path 对象(简单工厂)。

4、观察者模式

观察者模式:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。具体参考:java(面向对象)的23种设计模式(11)——观察者模式。
NIO 中的文件目录监听服务基于 WatchService 接口和 Watchable 接口。WatchService 属于观察者,Watchable 属于被观察者。WatchService 用于监听文件目录的变化,同一个 WatchService 对象能够监听多个文件目录。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java IO详解 的相关文章

  • 如何以编程方式找出我的 PermGen 空间使用情况?

    我正在尝试诊断java lang OutOfMemoryError PermGen Space在 Sun 的 Hotspot JVM 上运行时出现错误 并且想知道我的程序在不同时刻使用了多少 PermGen 空间 有没有办法以编程方式找出这
  • 指纹奇异点检测

    我正在尝试确定指纹的核心点和增量点 我正在使用庞加莱指数方法 但我无法成功检测到这一点 而且我不明白为什么 First I divide the image in 15x15 blocks then I calculate the x an
  • 在哈希图中存储字符和二进制数

    我正在尝试存储字母到二进制数的映射 这是我的映射 h 001 i 010 k 011 l 100 r 101 s 110 t 111 为此 我创建了一个哈希映射并存储了键值对 我现在想显示给定句子的相应二进制值 这是我的代码 package
  • 使用 JAXB 编组 LocalDate

    我正在构建一系列链接类 我希望能够将其实例编组到 XML 以便我可以将它们保存到文件中并稍后再次读取它们 目前我使用以下代码作为测试用例 import javax xml bind annotation import javax xml b
  • 空 EntityManager/EJB 注入 MDB

    我有一个消息驱动 bean MDB 部署到 WebLogic 12 1 3 我尝试使用 PersistenceContext 注释将实体管理器注入 MDB 但实体管理器为空 我还尝试注入一个简单的无状态会话 bean 它也是空的 但是 Me
  • 如果按下 Esc 则中断循环

    我用 JAVA 语言编写了一个程序 它使用 Scanner 类接受来自控制台的输入 现在我想将此功能添加到我的代码中 以便在用户按下 Esc 按钮时存在循环 while 到目前为止 我认为键盘类可以帮助我 但它就像扫描仪一样 我尝试使用事件
  • 动画图像视图

    目前我正在开发一款游戏 这是我的游戏的详细信息 用户应选择正确的图像对象 我希望图像从左到右加速 当他们到达终点时 他们应该再次出现在活动中 这是我正在处理的屏幕截图 我有 5 个图像视图 它们应该会加速 您有此类动画的示例代码吗 非常感谢
  • Codility 钉板

    尝试了解 Codility NailingPlanks 的解决方案 问题链接 https app codility com programmers lessons 14 binary search algorithm nailing pla
  • Hystrix是否可以订阅CircuitBreaker开启事件?

    对于单元测试 我希望能够订阅 Hystrix 事件 特别是在断路器打开或关闭时发生事件 我四处寻找示例 似乎解决方法是利用指标流并监视断路器标志 由于 Hystrix 是基于 RxJava 构建的 我认为应该在某个地方有一个事件订阅接口 在
  • 如何使用 Spring MVC 和 Thymeleaf 添加静态文件

    我的问题是如何添加 CSS 和图像文件等静态文件 以便我可以使用它们 我正在使用 Spring MVC 和 Thymeleaf 我查看了有关此主题的各种帖子 但它们对我没有帮助 所以我才来问 根据这些帖子 我将 CSS 和图像文件放在res
  • Netty中连接关闭后重新连接的最佳方法是什么

    简单场景 扩展 SimpleChannelUpstreamHandler 的较低级别的类 A 此类是发送消息和接收响应的主力 系统其他部分可以使用顶级类 B 来发送和接收消息 可以模拟同步和异步 此类创建 ClientBootstrap 设
  • NoSuchMethodError:将 Firebase 与应用程序引擎应用程序集成时

    我试图将 firebase 实时数据库与谷歌应用程序引擎应用程序集成 我在调用时收到此错误 gt DatabaseReference ref FirebaseDatabase gt getInstance gt getReference t
  • setKeyListener 将覆盖 setInputType 并更改键盘

    大家好 我在两个设备之间遇到问题 在实践中使用InputType和KeyListener我正在操纵一个EditText让它从数字键盘接收逗号和数字 有关更多背景信息 请检查我之前的question https stackoverflow c
  • 在方法内声明类 - Final 关键字 [重复]

    这个问题在这里已经有答案了 给定方法中的以下内部类 IsSomething public class InnerMethod private int x public class Something private int y public
  • 如何将任务添加到 gradle 中的主要“构建”任务

    当我尝试使用以下代码将任务添加到主构建任务时 rootProject tasks getByName build dependsOn mytask 当我跑步时它抱怨gradle w build输出 Where Build file line
  • 如何使用maven创建基于spring的可执行jar?

    我有一个基于 Maven 的 Spring WS 客户端项目 我想将其打包为单个 jar 在eclipse中 一切运行正常 当我尝试将其打包为可执行 jar 时 我收到 ClassNotFound 异常 因为 Spring jar 未包含在
  • 无法在 BlackBerry Playbook 上设置音量

    我在更改黑莓游戏书的音量时遇到问题 首先 我将 Android 应用程序重新打包到 Palybook 应用程序 我需要使用搜索栏更改黑莓剧本的音量 并在搜索监听器中设置音频管理器音量 这是代码 audioManager AudioManag
  • 当我在 Java 中输入 IP 时无法连接到我的服务器

    好的 我正在尝试学习 Java 客户端 服务器的内容 并且正在浏览教程代码 如下所示 当我将 localhost 更改为我的 IP 时 它会停止工作 请帮忙 编辑 127 0 0 1 似乎也可以工作 但不是我的真实IP Copyright
  • 条件查询:按计数排序

    我正在尝试执行一个标准查询 该查询返回 stackoverflow 中回答最多的问题 例如常见问题解答 一个问题包含多个答案 我正在尝试使用标准查询返回按每个问题的答案数排序的回答最多的问题 任何人都知道我应该在 hibernate cri
  • 编译时在代码中替换Java静态最终值?

    在java中 假设我有以下内容 fileA java class A public static final int SIZE 100 然后在另一个文件中我使用这个值 fileB java import A class b Object t

随机推荐

  • C6385:从“buffer”中读取的数据无效: 可读大小为“recv()`72”个字节,但可能读取了“25”个字节。

    C 网络编程中接收结构体对象遇到的问题 想从客户端发送一个结构体对象到服务器 在网上查询后发现可以在客户端用memcpy把结构体拷贝到字符串上发送给客户端 再在客户端把字符串转化为结构体 具体代码如下 结构体 typedef struct
  • 算法知识之最长公共子序列问题(动态规划)

    最近朋友让帮做个关于动态规划的最长公共子序列的问题 翻看以前的笔记并完成该题后 顺便写这样一篇文章 希望对大家有所帮助 同时也帮助自己回顾该知识点 一 最长公共子序列的定义 子序列 若给定序列X x1 x2 xm 则另一序列Z z1 z2
  • 刷脸支付必定是站在时代舞台中央的新星

    2020年初这场突如其来的疫情 打乱了各行各业的节奏 据2019年中国刷脸支付技术应用社会价值专题研究报告预计 2022年刷脸支付市场规模将突破7 6亿人 这说明现在大家习惯的支付是扫码支付 而一两年后人们将形成全新的支付习惯 刷脸支付 疫
  • 【布局 Widget】 Flutter SizedBox

    Flutter Sizedbox 是一个 布局组件 用来给 child 添加 tight 约束的 也可以用来添加空白 width height是 Sizedbox 的参数 BoxConstraints get additionalConst
  • 用户名 不在 sudoers文件中,此事将被报告。

    sudo password for lkp lkp 不在 sudoers 文件中 此事将被报告 sudo命令可以让你以root身份执行命令 来完成一些我们这个帐号完成不了的任务 其实并非所有用户都能够执行sudo 因为有权限的用户都在 et
  • CTFshow web1~8 WP Write by SunnyDog

    文章目录 web签到题 查看源码 web2 SQL注入 web3 input伪协议 log注入 Web4 log注入 Web5 代码审计 Web6 SQL注入 但是过滤 Web7 SQL注入 Web8 脚本布尔盲注 web签到题 查看源码
  • 解决:c++: internal compiler error: Killed (program cc1plus)

    TOC 解决 c internal compiler error Killed program cc1plus 1 出现问题 博主在VMware虚拟机中安装sophus库 执行make命令时出现错误 如下图所示 2 分析问题 多方查找 确定
  • 文件自动化处理

    文件自动化处理 1 1 读写文件 1 1 1 文件与文件路径 1 1 2 当前工作目录 1 1 3 路径操作 1 1 3 1 绝对路径和相对路径 1 1 3 2 路径操作 1 1 3 3 路径有效性检查 1 1 4 文件及文件夹操作 1 1
  • 光线追踪算法

    文章目录 光线追踪算法 1 光线追踪算法介绍 2 光线追踪算法重要的公式 3 光线追踪算法在matlab中的应用实例 4 光线追踪算法在Lingo中的应用实例 5 个人感悟 光线追踪算法 1 光线追踪算法介绍 光线追踪算法是一种基于物理光线
  • Android--自定义数字输入框

    工具类 前台页面 keyboadview xml 键盘页面 Java后台 KeyboardPopupWindow 步骤 1 initView 每自定义一个数字输入框 方法内部的代码都要重写并绑定相应输入框控件 2 onKeyDown int
  • 城市扩张对生境质量影响评价与生态安全格局构建—以黄河流域“几”字弯为例

    1 亮点 以高生境质量为 源地 构建生态廊道 城市扩张对生境质量的影响是异质的 提出 几 字弯 两屏三廊七区 的生态格局 2 关键词 城市扩张 生境质量 生态安全格局 城市化 InVEST模型 MCR模型 可持续发展目标 干旱地区 3 摘要
  • awk命令详解

    http www cnblogs com serendipity archive 2011 08 01 2124118 html 简单使用 awk 对于文件中一行行的独处来执行操作 awk F print 1 4 使用 来分割这一行 把这一
  • 二维我的世界源代码(c++)

    include
  • C++ 多线程:互斥量(mutex)

    C 11中的互斥量 声明在
  • Linux命令_pstree & 显示进程树

    目录 1 语法 1 1 常用参数 2 常见用法 2 1 指定PID 2 2 指定用户 2 3 显示某个进程详细信息 2 4 指定查看某个进程的父进程 1 语法 pstree 参数 pstree 参数 PID pstree 参数 USER 1
  • python进程间通信

    进程间通信表示进程之间的数据交换 为了开发并行应用程序 需要在进程间交换数据 下图显示了多个子过程之间同步的各种通信机制 各种通信机制 队列 队列可以用于多进程程序 多处理模块的Queue类与Queue Queue类相似 因此 可以使用相同
  • 双向链表的结点增删细节(p->next->prior = s是啥意思,p->next->prior究竟代表下一个结点的prior指针还是p本身)

    一 写在开头 最近在看双向链表 其中双向链表的结点增删操作代码困扰了我很久 下面就写下一些自己的感悟 大家轻点喷 二 相关代码 include
  • Tomcat后台管理

    Tomcat后台管理讲解 1 Tomcat是什么 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器 属于轻量级应用服务器 2 为什么使用它 优势 适用范围 范围 在中小型系统和并发访问用户不是很多的场合下被普遍使用 是开发和调
  • 气温常年在25度的地方_我国夏季最清凉的5座城市,平均不超过25℃,昆明,贵阳上榜...

    在这个炎热的夏季中 不知道为什么今年的天气非常的怪异 夏天来的非常早 而且气温上升非常迅速 可能第一天还是在25度左右 第二天可能就能达到32度 毫不夸张 所以在这个时候 我们在出去旅游的时候一定要选择一些清凉的地方 今天小编就告诉大家我国
  • Java IO详解

    一 I O简介 IO即Input和Output 即输入和输出 这里的输入和输出都是相对于内存来说的 具体见下图 InputStream Reader 所有输入流的基类 前者是字节输入流 后者是字符输入流 OutputStream Write