概述
Java的IO包主要关注数据源的读取和输出到目标媒介。示意图如下
常用的源数据和目标媒介如下:
- Files
- Pipes
- Network Connections
- In-memory Buffers (e.g. arrays)
- System.in, System.out, System.error
流从概念上说一个连续的数据流,既可以从流中读取数据,也可以往流中写入数据。主要分为字节流和字符流,字节流指以字节为单位进行读写,字符流指以字符为单位进行读写。
一个程序需要InputStream或者Reader从数据源读取数据,需要OutputStream或者Writer将数据写入到目标媒介中。示意图如下:
- File Access
- Network Access
- Internal Memory Buffer Access
- Inter-Thread Communication (Pipes)
- Buffering
- Filtering
- Parsing
- Reading and Writing Text (Readers / Writers)
- Reading and Writing Primitive Data (long, int etc.)
- Reading and Writing Objects
通过输入、输出、基于字节或者字符、以及其他比如缓冲、解析之类的特定用途可以将Java IO流进行如下划分:
InputStreamReader Writer
OutputStreamWriter Arrays ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter Files FileInputStream
RandomAccessFile FileOutputStream
RandomAccessFile FileReader FileWriter Pipes PipedInputStream PipedOutputStream PipedReader PipedWriter Buffering BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter Filtering FilterInputStream FilterOutputStream FilterReader FilterWriter Parsing PushbackInputStream
StreamTokenizer PushbackReader
LineNumberReader Strings StringReader StringWriter Data DataInputStream DataOutputStream
Data - Formatted PrintStream PrintWriter Objects ObjectInputStream ObjectOutputStream Utilities SequenceInputStream
File类
在讨论Stream的具体使用前,我们先看看IO库里面的File类。
Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等,但File对象却不能直接访问文件内容本身,要查看内容,则需要使用IO流。
通过以下构造方法创建一个File对象。
- 通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。
- 通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。
- 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
- 通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。
由于其方法均比较简单,不再罗列,具体见https://docs.oracle.com/javase/8/docs/api/。一个具体的实例如下:
查看当前项目,按照字典顺序排序后,再判断其子路径是文件还是目录,并输出相关结果。
在上面的层次图中,为简便区分,用蓝色表示接口,红色表示抽象类,绿色表示类。
中,主要通过5个接口来定义区别不同的流,分别是,,,。其中的父类接口主要是用于基于的异常处理。
实现接口,而的父类接口主要是用于基于的异常处理,在后续的示例代码中将会说明。
InputStream类图
具体看的类图如下:
实现接口,并有五个子类,而其子类作为装饰功能类,类图如下:
输入流使用基本流程
其中data.txt文件内容为,程序运行日志如下:
read()
自定义的方法输入文件,然后我们构造实例,通过循环调用方法从流中读取一个字节的内容。
数据读取后可以将返回的类型转换成类型。
如果到达流末尾时,方法返回-1。此时则可以关闭流。
read(byte[])
包含了2个从中读取数据并将数据存储到缓冲数组中的方法,他们分别是:
一次性读取一个字节数组的方式,比一次性读取一个字节的方式快的多,所以,尽可能使用这两个方法代替方法。
方法会尝试读取与给定字节数组容量一样大的字节数,返回值说明了已经读取过的字节数。如果内可读的数据不足以填满字节数组,那么数组剩余的部分将包含本次读取之前的数据。记得检查有多少数据实际被写入到了字节数组中。
方法同样将数据读取到字节数组中,不同的是,该方法从数组的位置开始,并且最多将个字节写入到数组中。同样地,方法返回一个变量,告诉你已经有多少字节已经被写入到字节数组中,所以请记得在读取数据前检查上一次调用的返回值。
这两个方法都会在读取到达到流末尾时返回-1。
在这段代码中共有4个地方可能会抛出异६io 基础java0;,分别是,,,。
想象一下,从try块内部抛出异常。然后finally执行该块,而从finally块抛出的异常如果我们不捕获的话,将在调用堆栈中向上传播。
结构显示的关闭流显得单调乏味,并且异常捕获模板代码众多,看着一点都不优雅。在后可以使用结构来处理,示例如下:
我们在方法中实现了和方法一样的功能,但代码显得简洁明了。其中在括号内创建了这个资源,当程序执行离开块时,该资源将会自动关闭。如果try()括号内有多个资源,资源将按与括号内创建/列出顺序相反的顺序关闭。
IO流中,资源之所以会自动关闭,是因为IO流(包括,,,)均实现了接口,具体可参考全局类图。我们也可以将自定义类实现接口,然后
与结构一起使用。
FilterInputStream
FilterInputStream主要有4个子类,可以用来修改InputStream的内部行为。
4个字类添加的功能如下:
我们用BufferedInputStream给FileInputStream方法添加缓存区,改写后的read方法如下:
OutputStream类图
的层次关系如下:
实现和接口,其装饰功能子类,类图如下:
程序输出如下:
OutputStream常用方法如下。
write(byte[])
同样包含了将字节数据中全部或者部分数据写入到输出流中的方法,分别是和。
把字节数组中所有数据写入到输出流中。
把字节数据中从位置开始,个字节的数据写入到输出流。
flush()
的方法将所有写入到的数据冲刷到相应的目标媒介中。比如,如果输出流是,那么写入到其中的数据可能并没有真正写入到磁盘中。即使所有数据都写入到了,这些数据还是有可能保留在内存的缓冲区中。通过调用方法,可以把缓冲区内的数据刷新到磁盘(或者网络,以及其他任何形式的目标媒介)中。
close()
当你结束数据写入时,需要关闭。通过调用可以达到这一点。因为的各种方法可能会抛出异常,所以你需要把调用的关闭操作方在块中执行。如果使用基于的异常处理程序则由于实现了接口,不用显示关闭。
FilterOutputStream
FilterOutputStream主要有3个子类,可以用来修改OutputStream的内部行为。
3个字类添加的功能如下:
Reader和Writer
Reader类图
Reader类图如下,Reader实现Readable和Closeable接口,根据不同的功能有众多的子类。
子类必须实现的方法只有 read() 和 close()
read()
用于读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。用于支持高效的单字符输入的子类应重写此方法。
返回:作为整数读取的字符,范围在 0 到 65535 之间 ( 0x00-0xffff),如果已到达流的末尾,则返回 -1
read(char[] cbuf,int off,int len)
将字符读入数组的某一部分。在某个输入可用、发生 I/O 错误或者到达流的末尾前,此方法一直阻塞。
输出结果如下:
需要注意的是,Java内部使用UTF8编码表示字符串。输入流中一个字节可能并不等同于一个UTF8字符
,如果你从输入流中以字节为单位读取UTF8编码的文本,并且尝试将读取到的字节转换成字符,你可能会得不到预期的结果。
如果输入的文件不是UTF8编码的话,由于FileReader不能指定编码,则需要利用字节流,然后利用转换流将字节流转换为字符流。
主要的api接口如下:
write(String str)
public void write(String str) throws IOException
写入字符串。
参数:
str - 要写入的字符串
抛出:
IOException - 如果发生 I/O 错误
write(String str,int off,int len)
flush()
字节流和字符流转换
输入字节流转换
是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集,即编码。
的构造函数输入字节流
通过调用类的方法,设置var2为null
进而调用类的方法,设置编码格式为UTF-8。
其他指定编码格式的构造函数如下:
如下我们看一个实例,从控制台输入字符串,并将输入字节流转换为字符流。
输出字节流转换
是字符流通向字节流的桥梁:可使用指定的 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
每次调用 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 方法的字符没有缓冲。
为了获得最高效率,可考虑将 包装到 中,以避免频繁调用转换器。例如:
Java IO 的一般使用原则 :
按数据来源或去向
文件: FileInputStream, FileOutputStream,FileReader, FileWriter
byte[] : ByteArrayInputStream, ByteArrayOutputStream
Char[]: CharArrayReader, CharArrayWriter
String: StringBufferInputStream, StringBufferOuputStream,StringReader, StringWriter
网络数据流: InputStream, OutputStream, Reader, Writer
按是否格式化输出
格式化输出: PrintStream, PrintWriter
按是否要缓冲
缓冲: BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter
按数据格式
二进制格式(只要不能确定是纯文本的) : InputStream, OutputStream 及其所有带 Stream 结束的子类。
纯文本格式(含纯英文与汉字或其他编码方式); Reader, Writer 及其所有带 Reader, Writer 的子类。
本文详细介绍讲述了Java IO的相关内容,涉及到字节流和字符流的设计,使用原则等。
本文参考:
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/2393.html