首页 > 编程语言 >JAVA IO流

JAVA IO流

时间:2023-06-10 21:35:52浏览次数:50  
标签:字符 JAVA 字节 文件 int write IO 读取

IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。

Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

字节流

# InputStream(字节输入流)

InputStream用于从源头(通常是文件)读取数据(字节信息)到内存中,java.io.InputStream抽象类是所有字节输入流的父类。

InputStream 常用方法:

  • read():返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节,则代码返回 -1 ,表示文件结束。
  • read(byte b[ ]) : 从输入流中读取一些字节存储到数组 b 中。如果数组 b 的长度为零,则不读取。如果没有可用字节读取,返回 -1。如果有可用字节读取,则最多读取的字节数最多等于 b.length , 返回读取的字节数。这个方法等价于 read(b, 0, b.length)
  • read(byte b[], int off, int len):在read(byte b[ ]) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)。
  • skip(long n):忽略输入流中的 n 个字节 ,返回实际忽略的字节数。
  • available():返回输入流中可以读取的字节数。
  • close():关闭输入流释放相关的系统资源。

从 Java 9 开始,InputStream 新增加了多个实用的方法:

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

FileInputStream 是一个比较常用的字节输入流对象,可直接指定文件路径,可以直接读取单字节数据,也可以读取至字节数组中。

 

 

OutputStream(字节输出流)

OutputStream用于将数据(字节信息)写入到目的地(通常是文件),java.io.OutputStream抽象类是所有字节输出流的父类。

OutputStream 常用方法:

  • write(int b):将特定字节写入输出流。
  • write(byte b[ ]) : 将数组b 写入到输出流,等价于 write(b, 0, b.length)
  • write(byte[] b, int off, int len) : 在write(byte b[ ]) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)。
  • flush():刷新此输出流并强制写出所有缓冲的输出字节。
  • close():关闭输出流释放相关的系统资源。

FileOutputStream 是最常用的字节输出流对象,可直接指定文件路径,可以直接输出单字节数据,也可以输出指定的字节数组。

 

 

 

字符流

不管是文件读写还是网络发送接收,信息的最小存储单元都是字节。 那为什么 I/O 流操作要分为字节流操作和字符流操作呢?

个人认为主要有两点原因:

  • 字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是比较耗时。
  • 如果我们不知道编码类型就很容易出现乱码问题。

因此,I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。

字符流默认采用的是 Unicode 编码,我们可以通过构造方法自定义编码。顺便分享一下之前遇到的笔试题:常用字符编码所占字节数?utf8 :英文占 1 字节,中文占 3 字节,unicode:任何字符都占 2 个字节,gbk:英文占 1 字节,中文占 2 字节。

Reader(字符输入流)

Reader用于从源头(通常是文件)读取数据(字符信息)到内存中,java.io.Reader抽象类是所有字符输入流的父类。

Reader 用于读取文本, InputStream 用于读取原始字节。

Reader 常用方法:

  • read() : 从输入流读取一个字符。
  • read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,等价于 read(cbuf, 0, cbuf.length)
  • read(char[] cbuf, int off, int len):在read(char[] cbuf) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)。
  • skip(long n):忽略输入流中的 n 个字符 ,返回实际忽略的字符数。
  • close() : 关闭输入流并释放相关的系统资源。

InputStreamReader 是字节流转换为字符流的桥梁,其子类 FileReader 是基于该基础上的封装,可以直接操作字符文件。

Writer(字符输出流)

Writer用于将数据(字符信息)写入到目的地(通常是文件),java.io.Writer抽象类是所有字符输出流的父类。

Writer 常用方法:

  • write(int c) : 写入单个字符。
  • write(char[] cbuf):写入字符数组 cbuf,等价于write(cbuf, 0, cbuf.length)
  • write(char[] cbuf, int off, int len):在write(char[] cbuf) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)。
  • write(String str):写入字符串,等价于 write(str, 0, str.length())
  • write(String str, int off, int len):在write(String str) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)。
  • append(CharSequence csq):将指定的字符序列附加到指定的 Writer 对象并返回该 Writer 对象。
  • append(char c):将指定的字符附加到指定的 Writer 对象并返回该 Writer 对象。
  • flush():刷新此输出流并强制写出所有缓冲的输出字符。
  • close():关闭输出流释放相关的系统资源。

OutputStreamWriter 是字符流转换为字节流的桥梁,其子类 FileWriter 是基于该基础上的封装,可以直接将字符写入到文件。

 

字节缓冲流

IO 操作是很消耗性能的,缓冲流将数据加载至缓冲区,一次性读取/写入多个字节,从而避免频繁的 IO 操作,提高流的传输效率。

字节缓冲流这里采用了装饰器模式来增强 InputStreamOutputStream子类对象的功能。

举个例子,我们可以通过 BufferedInputStream(字节缓冲输入流)来增强 FileInputStream 的功能。

// 新建一个 BufferedInputStream 对象
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("input.txt"));
 

字节流和字节缓冲流的性能差别主要体现在我们使用两者的时候都是调用 write(int b)read() 这两个一次只读取一个字节的方法的时候。由于字节缓冲流内部有缓冲区(字节数组),因此,字节缓冲流会先将读取到的字节存放在缓存区,大幅减少 IO 次数,提高读取效率。

我使用 write(int b)read() 方法,分别通过字节流和字节缓冲流复制一个 524.9 mb 的 PDF 文件耗时对比如下:

使用缓冲流复制PDF文件总耗时:15428 毫秒
使用普通字节流复制PDF文件总耗时:2555062 毫秒
 

两者耗时差别非常大,缓冲流耗费的时间是字节流的 1/165。

 

 

 

 

 BufferedInputStream(字节缓冲输入流)

BufferedInputStream 从源头(通常是文件)读取数据(字节信息)到内存的过程中不会一个字节一个字节的读取,而是会先将读取到的字节存放在缓存区,并从内部缓冲区中单独读取字节。这样大幅减少了 IO 次数,提高了读取效率。

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];
    }
}
 

缓冲区的大小默认为 8192 字节,当然了,你也可以通过 BufferedInputStream(InputStream in, int size) 这个构造方法来指定缓冲区的大小。

BufferedOutputStream(字节缓冲输出流)

BufferedOutputStream 将数据(字节信息)写入到目的地(通常是文件)的过程中不会一个字节一个字节的写入,而是会先将要写入的字节存放在缓存区,并从内部缓冲区中单独写入字节。这样大幅减少了 IO 次数,提高了读取效率

类似于 BufferedInputStream ,BufferedOutputStream 内部也维护了一个缓冲区,并且,这个缓存区的大小也是 8192 字节。

 

字符缓冲流

BufferedReader (字符缓冲输入流)和 BufferedWriter(字符缓冲输出流)类似于 BufferedInputStream(字节缓冲输入流)和BufferedOutputStream(字节缓冲输入流),内部都维护了一个字节数组作为缓冲区。不过,前者主要是用来操作字符信息。

# 打印流

下面这段代码大家经常使用吧?

System.out.print("Hello!");
System.out.println("Hello!");
 

System.out 实际是用于获取一个 PrintStream 对象,print方法实际调用的是 PrintStream 对象的 write 方法。

PrintStream 属于字节打印流,与之对应的是 PrintWriter (字符打印流)。PrintStreamOutputStream 的子类,PrintWriterWriter 的子类。

public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable {
}
public class PrintWriter extends Writer {
}
 

# 随机访问流

这里要介绍的随机访问流指的是支持随意跳转到文件的任意位置进行读写的 RandomAccessFile

RandomAccessFile 的构造方法如下,我们可以指定 mode(读写模式)。

// openAndDelete 参数默认为 false 表示打开文件并且这个文件不会被删除
public RandomAccessFile(File file, String mode)
    throws FileNotFoundException {
    this(file, mode, false);
}
// 私有方法
private RandomAccessFile(File file, String mode, boolean openAndDelete)  throws FileNotFoundException{
  // 省略大部分代码
}
 

读写模式主要有下面四种:

  • r : 只读模式。
  • rw: 读写模式
  • rws: 相对于 rwrws 同步更新对“文件的内容”或“元数据”的修改到外部存储设备。
  • rwd : 相对于 rwrwd 同步更新对“文件的内容”的修改到外部存储设备。

文件内容指的是文件中实际保存的数据,元数据则是用来描述文件属性比如文件的大小信息、创建和修改时间。

RandomAccessFile 中有一个文件指针用来表示下一个将要被写入或者读取的字节所处的位置。我们可以通过 RandomAccessFileseek(long pos) 方法来设置文件指针的偏移量(距文件开头 pos 个字节处)。如果想要获取文件指针当前的位置的话,可以使用 getFilePointer() 方法。

andomAccessFile 比较常见的一个应用就是实现大文件的 断点续传 。何谓断点续传?简单来说就是上传文件中途暂停或失败(比如遇到网络问题)之后,不需要重新上传,只需要上传那些未成功上传的文件分片即可。分片(先将文件切分成多个文件分片)上传是断点续传的基础。

RandomAccessFile 可以帮助我们合并文件分片,示例代码如下:

 


 



标签:字符,JAVA,字节,文件,int,write,IO,读取
From: https://www.cnblogs.com/sumingyi/p/17471962.html

相关文章

  • java——微服务——spring cloud——Nacos——Nacos快速入门
            父工程中新增依赖:          ==================================================================================        客户端依赖修改——userservice和orderservice两个修改       ......
  • 时空图预测的方法论,以及 diffusion model 基本概念
    前天和善良的同学聊天,请教了①时空图预测的方法论,②diffusionmodel基本概念,记录下来。①时空图预测的方法论:首先,构造0~t-1时刻的t个图,每个图都有n个点。建图可以按照地理位置/特征相似性来建。貌似一般不会考虑边的权重,只考虑两点是否连接。然后,对这t个图......
  • IO
    I/O(Input/Outpu)即输入/输出 。     从计算机结构的视角来看的话,I/O描述了计算机系统与外部设备之间通信的过程。    从应用程序的视角来看的话,我们的应用程序对操作系统的内核发起IO调用(系统调用),操作系统负责的内核执行具体的IO操作。也就是说,我们的应用......
  • java设计模式之(单例模式)
    单例模式(懒汉式和饿汉式)    在Java中指的是单例设计模式他是软件开发中最常用的设计模式之一。单:唯一例:实例单例设计模式:既某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式要点:  1.某个类只能有一个实例       构造器私有  2.它必须......
  • com.netflix.hystrix.exception.HystrixRuntimeException: xxxFeign#xxxx timed-out a
    问题描述在使用Feign进行远程调用时遇到的bug。原因是因为超时了。需要对超时时间进行设置一下即可。在Nacos进行设置原先的contentTimeout和readTimeout都是2000,修改成20000后bug便解决了。......
  • Android Studio 添加安卓虚拟设备时提示 No emulator installed
    在设置页面勾选两个工具即可,我遇到的情况是明明已经安装了这两个工具还是提示没有安装,于是我取消勾选把两个安装好的工具删除了,再勾选重新安装。果不其然就提示安装失败,不慌再试一遍,第二次安装过程中没报错了,再次添加安卓设备,成功启动了。 ......
  • Collection 接口及其常用方法
    Collection接口及其常用方法Collection接口的特点Collection接口没有直接实现类,提供了更具体的子接口(如Set和List)的实现。Collection实现类(通常通过其中一个子接口间接实现Collection)可以存放多个Object类型的元素。有些Collection接口的实现类可以存放重复的元素(List),有些则......
  • Android Studio 2022.2.1 设置gradle依赖库
    AndroidStudio安装好后,随便创建一个项目编译时提示错误Plugin[id:'com.gradle.enterprise',version:'3.12.3',artifact:'com.gradle:gradle-enterprise-gradle-plugin:3.12.3']wasnotfoundinanyofthefollowingsources:网上查询显示的是gradle依赖库的问题,解决方......
  • Java编程技巧-定义集合常量、定义数组常量的最佳方式
    场景Java中定义集合常量的最佳方式在编码中,经常使用到各种集合常量,比如List(列表)常量、Set(集合)常量、Map(映射)常量等。普通方式一般这样写:publicstaticfinalList<Integer>CONST_VALUE_LIST=Arrays.asList(1,2,3);publicstaticfinalSet<Integer>CONST_VALUE......
  • JavaBean中Boolean类型的字段名不要用isXxx(转)
    addbyzhj: 之前看阿里出品的Java开发手册中提到JavaBean中Boolean类型字段名不要用isXxx命名,一直不明白原因。这篇文章详细说明了原因。我对原文略微进行了修改,将fastjson库改为fastjson2,但对序列化反序列化结果没有影响。原文:https://mp.weixin.qq.com/s/b1q779XdWyRe0QyGxh......