关于IO流技术的介绍
什么是IO
输入(Input)指的是:可以让程序从外部系统获得数据(核心含义是“读”,读取外部数据)。
输出(Output)指的是:程序输出数据给外部系统从而可以操作外部系统(核心含义是“写”,将数据写出到外部系统)。
java.io包为我们提供了相关的API,实现了对所有外部系统的输入输出操作。
什么是数据源
数据源data source,提供数据的原始媒介。常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO设备。
数据源分为:源设备、目标设备。
源设备:为程序提供数据,一般对应输入流。
目标设备:程序数据的目的地,一般对应输出流。
流的概念
流是一个抽象、动态的概念,是一连串连续动态的数据集合。
对于输入流而言,数据源就像水箱,流(stream)就像水管中流动着的水流,程序就是我们最终的用户。
我们通过流(A Stream)将数据源(Source)中的数据(information)输送到程序(Program)中。
对于输出流而言,目标数据源就是目的地(dest)。
我们通过流(A Stream)将程序(Program)中的数据(information)输送到目的数据源(dest)中。
输入/输出流的划分是相对程序而言的,并不是相对数据源。
在JDK7以及以后的版本中可以使用try-with-resource语法更优雅的关闭资源。
java.lang.AutoCloseable接口:在java.lang.AutoCloseable接口中包含了一个close方法,该方法用于关闭资源。
只要是实现了java.lang.AutoCloseable接口的对象,都可以使用try-with-resource关闭资源。
Java中流的概念细分
按流的方向分类:
1.输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
2.输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。
按处理的数据单元分类:
1.字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。
2.字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。
按处理对象不同分类:
1.节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
2.处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
Java中IO流类的体系
Java为我们提供了多种多样的IO流,我们可以根据不同的功能及性能要求挑选合适的IO流,如图所示,为Java中IO流类的体系。
我们下面简单做个总结:
InputStream/OutputStream:字节流的抽象类。
Reader/Writer:字符流的抽象类。
FileInputStream/FileOutputStream:节点流:以字节为单位直接操作“文件”。
ByteArrayInputStream/ByteArrayOutputStream:节点流:以字节为单位直接操作“字节数组对象”。
ObjectInputStream/ObjectOutputStream:处理流:以字节为单位直接操作“对象”。
DataInputStream/DataOutputStream:处理流:以字节为单位直接操作“基本数据类型与字符串类型”。
FileReader/FileWriter:节点流:以字符为单位直接操作“文本文件”(注意:只能读写文本文件)。
BufferedReader/BufferedWriter:处理流:将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。
BufferedInputStream/BufferedOutputStream:处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高读写效率
InputStreamReader/OutputStreamWriter:处理流:将字节流对象转化成字符流对象。
PrintStream:处理流:将OutputStream进行包装,可以方便地输出字符,更加灵活。
Java中IO的四大抽象类
nputStream/OutputStream和Reader/writer类是所有IO流类的抽象父类,我们有必要简单了解一下这个四个抽象类的作用。
InputStream
此抽象类是表示字节输入流的所有类的父类。InputSteam是一个抽象类,它不可以实例化。
数据的读取需要由它的子类来实现。根据节点的不同,它派生了不同的节点流子类 。
继承自InputSteam的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。
常用方法:
方法名 使用说明
int read() 读取一个字节的数据,并将字节的值作为int类型返回(0-255之间的一个值)。
如果未读出字节则返回-1(返回值为-1表示读取结束)
void close() 关闭输入流对象,释放相关系统资源
OutputStream
此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。
常用方法:
方法名 使用说明
void write(int n) 向目的地中写入一个字节
void close() 关闭输出流对象,释放相关系统资源
Reader
Reader用于读取的字符流抽象类,数据单位为字符。
方法名 使用说明
int read() 读取一个字符的数据,并将字符的值作为int类型返回(0-65535之间的一个值,即Unicode值)。
如果未读出字符则返回-1(返回值为-1表示读取结束)
void close() 关闭流对象,释放相关系统资源
Writer
Writer用于输出的字符流抽象类,数据单位为字符。
方法名 使用说明
void write(int n) 向输出流中写入一个字符
void close() 关闭输出流对象,释放相关系统资源
常用流详解
文件字节流:
1.FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)。
2.FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件(图像、视频、文本文件等)。
通过字节缓冲区提高读写效率
通过创建一个指定长度的字节数组作为缓冲区,以此来提高IO流的读写效率。该方式适用于读取较大文件时的缓冲区定义。
注意:缓冲区的长度一定是2的整数幂。一般情况下1024长度较为合适。
在使用字节缓冲区时,我们需要注意:
1.为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法为:read(byte[] b);写入时的方法为:write(byte[ ] b, int off, int length)
2.程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法关闭的情况。
缓冲字节流
Java缓冲流本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包装流)。
BufferedInputStream和BufferedOutputStream这两个流是缓冲字节流,通过内部缓存数组来提高操作流的效率。
在关闭流时,应该先关闭最外层的包装流,即“后开的先关闭”。
缓存区的大小默认是8192字节,也可以使用其它的构造方法自己指定大小。
缓冲字符流
BufferedReader/BufferedWriter增加了缓存机制,大大提高了读写文本文件的效率。
字符输入缓冲流
BufferedReader是针对字符输入流的缓冲流对象,提供了更方便的按行读取的方法:readLine();
在使用字符流读取文本文件时,我们可以使用该方法以行为单位进行读取。
字符输出缓冲流
BufferedWriter是针对字符输出流的缓冲流对象,在字符输出缓冲流中可以使用newLine();方法实现换行处理。
注意
1.readLine()方法是BufferedReader的方法,可以对文本文件进行更加方便的读取操作。
2.newLine()方法BufferedWriter的方法,可以使用newLine()方法换行。
转换流
InputStreamReader/OutputStreamWriter用来实现将字节流转化成字符流。
通过转换流解决乱码:ANSI(American National Standards Institute)美国国家标准协会
通过转换流实现键盘输入屏幕输出
1.System.in是字节流对象,代表键盘的输入。
2.System.out是字节流对象,代表输出到屏幕。
字符输出流
在Java的IO流中专门提供了用于字符输出的流对象PrintWriter。
该对象具有自动行刷新缓冲字符输出流,特点是可以按行写出字符串,并且可通过println();方法实现自动换行。
数据流
数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。
DataInputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如:int、double、String等)的方法。
对象流
我们前边学到的数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外)。
如果要对某个对象进行读写操作,我们需要学习一对新的处理流:ObjectInputStream/ObjectOutputStream。
处理基本数据类型数据:ObjectInputStream/ObjectOutputStream处理基本数据类型。
注意
1.对象流不仅可以读写对象,还可以读写基本数据类型。
2.读写基本数据类型时,读取的顺序一定要与写入的顺序一致,否则不能正确读取数据。
Java对象的序列化和反序列化
序列化和反序列化是什么:
当两个进程远程通信时,彼此可以发送各种类型的数据。 无论是何种类型的数据,都会以二进制序列的形式在网络上传送。
比如,我们可以通过http协议发送字符串信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。
序列化涉及的类和接口:
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable接口的类的对象才能被序列化。 Serializable接口是一个空接口,只起到标记作用。
将对象序列化到文件
ObjectOutputStream可以将一个内存中的Java对象通过序列化的方式写入到磁盘的文件中。被序列化的对象必须要实现Serializable序列化接口,否则会抛出异常。
File类在IO中的作用
当以文件作为数据源或目标时,除了可以使用字符串作为文件以及位置的指定以外,我们也可以使用File类指定。
装饰器模式构建IO流体系
装饰器模式简介:装饰器模式是GOF23种设计模式中较为常用的一种模式。它可以实现对原有类的包装和装饰,使新的类具有更强的功能。
IO流体系中的装饰器模式:IO流体系中大量使用了装饰器模式,让流具有更强的功能、更强的灵活性。
FileUtils类中常用方法的介绍
打开FileUtils的api文档,我们抽出一些工作中比较常用的方法,进行总结和讲解。总结如下:
方法名 使用说明
cleanDirectory 清空目录,但不删除目录
contentEquals 比较两个文件的内容是否相同
copyDirectory 将一个目录内容拷贝到另一个目录。可以通过FileFilter过滤需要拷贝的文件
copyFile 将一个文件拷贝到一个新的地址
copyFileToDirectory 将一个文件拷贝到某个目录下
copyInputStreamToFile 将一个输入流中的内容拷贝到某个文件
deleteDirectory 删除目录
deleteQuietly 删除文件
listFiles 列出指定目录下的所有文件
openInputSteam 打开指定文件的输入流
readFileToString 将文件内容作为字符串返回
readLines 将文件内容按行返回到一个字符串数组中
size 返回文件或目录的大小
write 将字符串内容直接写到文件中
writeByteArrayToFile 将字节数组内容写到文件中
writeLines 将容器中的元素的toString方法返回的内容依次写入文件中
writeStringToFile 将字符串内容写到文件中
IOUtils的妙用
打开IOUtils的api文档,我们发现它的方法大部分都是重载的。所以,我们理解它的方法并不是难事。因此,对于方法的用法总结如下:
方法名 使用说明
buffer 将传入的流进行包装,变成缓冲流。并可以通过参数指定缓冲大小
closeQueitly 关闭流
contentEquals 比较两个流中的内容是否一致
copy 将输入流中的内容拷贝到输出流中,并可以指定字符编码
copyLarge 将输入流中的内容拷贝到输出流中,适合大于2G内容的拷贝
lineIterator 返回可以迭代每一行内容的迭代器
read 将输入流中的部分内容读入到字节数组中
readFully 将输入流中的所有内容读入到字节数组中
readLine 读入输入流内容中的一行
toBufferedInputStream,toBufferedReader 将输入转为带缓存的输入流
toByteArray,toCharArray 将输入流的内容转为字节数组、字符数组
toString 将输入流或数组中的内容转化为字符串
write 向流里面写入内容
writeLine 向流里面写入一行内容