网站首页 > 基础教程 正文
I/O流的概念
在Java API中,可以从其中读入一个字节序列的对象称作输入流,而可以向其中写入一个字节序列的对象称作输出流。这些字节序列的来源地和目的地可以是文件,而且通常都是文件,但是也可以是网络连接,甚至是内存块。抽象类InputStream和OutputStream构成了输入/输出(I/O)类层次结构的基础。
下图是IO流常用的层次结构图(Java流家族各种输入/输出流类型超过60个,这里仅列出常用类,详情可以参考JDK API文档):
由图我们可以发现很多流都是成对的出现的,以下是我们常用到的类:
- InputStream和OutputStream:字节流的抽象类,以下这两个类的实现类都是以字节为单位操作的。
- FileInputStream和FileOutputStream:节点流,以字节为单位直接操作**【文件】**,也可称为文件流。
- ByteArrayInputStream和ByteArrayOutputStream:节点流,以字节为单位直接操作**【字节数组对象】**,也可称为字节数组流。
- ObjectInputStream和ObjectOutputStream:处理流,以字节为单位直接操作**【对象】,也可称为对象流,也称为序列化/反序列化**。
- DataInputStream和DataOutputStream:处理流,以字节为单位直接操作**【基本数据类型与字符串类型】**,也可称为数据流。
- BufferedInputStream和BufferedOutputStream:处理流,将InputStream/OutputStream对象进行包装,增加缓存功能,提高读写效率。
- Reader和Writer:字符流的抽象类,以下这两个类的实现类都是以字符为单位操作的。
- FileReader和FileWriter:节点流,以字符为单位直接操作**【文本文件】**。
- BufferedReader和BufferedWriter:处理流,将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。
- InputStreamReader和OutputStreamWriter:处理流,将字节流对象转化成字符流对象。
- PrintStream:处理流,将OutputStream进行包装,它可以方便地输出字符,更加灵活。
下文中会逐一讲解这些类中的常用方法的使用。
I/O流的四大抽象类
InputStream/OutputStream和Reader/Writer类是所有I/O流的抽象父类,他们的作用就是操作对象单位的不同,前者的单位为字节,后者的单位为字符。
1、InputStream
InputStream表示字节输入流的所有类的抽象父类。因为它是一个抽象类,所以它不可以实例化。数据的读取需要由它的子类来实现。根据节点的不同,它派生了不同的节点流子类。
常用方法:
int read():读取一个字节的数据,并将字节的值作为int类型返回(0~255之间的一个值)。如果未读出字节则返回-1(返回值为-1表示读取结束)。
void close():关闭输入流对象,释放相关系统资源。
2、OutputStream
OutputStream表示字节输出流的所有类的抽象父类,它同样不可以实例化。输出流接收输出直接并将这些字节发送到某个目的地。
常用方法:
void write(int n):向目的地中写入一个字节。
void close():关闭输出流对象,释放相关系统资源。
3、Reader
Reader用于读取的字符流抽象类,数据单位为字符。
常用方法:
int read():读取一个字符的数据,并将字符的值作为int类型返回(0~65535之间的一个值,即Unicode值)。如果未读出字符则返回-1(返回值为-1表示读取结束)。
void close():关闭字符输入流对象,释放相关系统资源。
4、Writer
Writer用于写入的字符流抽象类,数据单位为字符。
常用方法:
- void write(int n):向输出流中写入一个字符。
- void close():关闭字符输出流对象,释放相关系统资源。
常用类的详解
所有常用类的操作大部分都可以分为四个步骤1.创建源|2.选择流|3.操作|4.释放资源,只要熟练这四个步骤,我们就能很熟练的掌握使用I/O流的操作。因为文章篇幅太长,本文中就简要介绍使用常用的几个方法,需要了解更详细的信息可以参考Java API文档。
文件字节流
FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(如图像、视频、文本文件等)。Java中也提供了FileReader专门读取文本文件。
FileOutputStream通过字节的方式写数据到文件中,适合所有类型的文件。Java中也提供了FileWriter专门写入文本文件。
以下是FileInputStream的使用示例:
以下是FileOutputStream的使用示例:
以下是使用文件字节输入、输出流实现文件拷贝:
文件字节流的注意事项
- 为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法为read(byte[] b);写入时的方法为write(byte[] b,int off,int length)。
- 程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法关闭的情况。
文件字符流
前面介绍的文件字节流可以处理所有的文件,但是字节流不能很好地处理Unicode字符,经常会出现“乱码”现象。所以处理文本文件时一般可以使用文件字符流,它以字符为单位进行操作。
FileReader使用示例:
FileWriter使用示例:
缓冲字节流
Java缓冲流本身并不具有I/O流的读写功能,只是在其他的流上加上缓冲功能以提高效率,就像是把其他的流包装起来一样,因此缓冲流是一种处理流(包装流)。
缓冲字节流的使用就是将其他的字节流包起来,下面用一个例子对比是否添加缓冲流的性能:
缓冲字节流的注意事项
- 在关闭流时,应该先关闭最外层的包装流,即“先开启的后关闭”。
- 缓存区的默认大小是8192字节,也可以使用其他的构造器来指定大小。
缓冲字符流
BufferedReader和BufferedWriter同样增加了缓冲机制,大大提高了读写文本文件的效率,同时,提供了更方便的按行读取的方法readLine()以及写入方法writeLine()和newLine()。在处理文本时,可以使用缓冲字符流。
以下是使用缓冲字符流的拷贝文件的例子:
缓冲字符流的注意事项
- readLine()方法是BufferedReader特有的方法,可以对文本文件进行更加方便的读取操作。
- 写入一行后要记得使用newLine()方法换行。
字节数组流
ByteArrayInputStream和ByteArrayOutputStream经常用在需要流和数组之间转化的情况,FileInputStream把文件当作数据源,而ByteArrayInputStream则是把内存中的“某个字节数组对象”当作数据源。
ByteArrayInputStream的使用例子:
ByteArrayOutputStream的使用例子:
字节数组流的注意事项
- 字节数组流可以不需要释放资源,但是为了代码完整性,我们也可以将释放资源的代码加上。
- 字节数组的数据源是内存中的“某个字节数组对象”。
数据流
数据流将“基础数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式从底层输入/输出流中的操作Java基本数据类型与字符串类型。
以下是一个数据流使用的例子:
数据流的使用注意事项
使用数据流时,读取顺序一定要与写入顺序一致,否则不能正确读取数据。
对象流(序列化/反序列化)
上栏中数据流仅能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外)。如果要对某个对象进行读写操作,则就需要使用对象流。
ObjectInputStream和ObjectOutputStream是以“对象”为数据源,但是必须对传输的对象进行序列化与反序列化操作。
以下是对象流的一个简单使用的例子(为了代码简单异常直接抛出了):
import java.io.BufferedInputStream:
对象流的使用注意事项
- 对象流不仅可以读写对象,还可以读写基本数据类型。
- 使用对象流读写对象时,该对象必须经过序列化与反序列化。
- 系统提供的类(如Data等)已经实现了序列化接口,自定义类必须手动实现序列化接口。
Java对象的序列化与反序列化
作用
将Java对象转换为字节序列的过程称为对象的序列化;将字节序列回复为Java对象的过程称为对象的反序列化。
对象的序列化的作为如下:
- 持久化:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,例如休眠的实现、服务器的session的持久化,hibernate持久化对象等。
- 网络通信:在网络上传送对象的字节序列,例如服务器之间的数据通信、对象传递等。
序列化与反序列化在对象流中的使用
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法可以从一个源输入流中读取字节序列,再将其反序列化为一个对象并返回。
只有实现了Serializable接口的类的对象才能被序列化。Serializable接口是一个空接口,只起标记作用。
注意事项
- static属性不参与序列化。
- 对象中的某些属性如果不想被序列化,不能使用static,而应使用transient修饰。
- 为了防止读和写的序列化ID不一致,一般指定一个固定的序列化ID。
转换流
InputStreamReader和OutputStreamWriter用来实现将字节流转化成字符流。例如下面的例子:
随意访问文件流
RandomAcessFile可以实现两个作用:
- 实现对一个文件的读和写操作。
- 可以访问文件的任意位置,不像其他流只能按照先后顺序读取。
随意访问文件流的三个核心方法:
- RandomAccessFile(String name,String mode):name用于确定文件,mode取r(读)或rw(可读写),通过mode可以确定流对文件的访问权限。
- seek(long a):用于定位流对象读写文件的位置,a确定读写位置距离文件开头的字节数。
- getFilePointer():用于获取流的当前读写位置。
以下是一个使用RandomAccessFile分割文件的例子:
打印流
这个流仅需要了解即可,以下是一个简单的例子:
commons-io的使用
Apache-commons-io工具包提供了IOUtils/FileUtils,它可以非常方便地对文件和目录进行操作。大大提高实际开发中的工作效率(没有必要重复造轮子)。
FileUtils类的常用方法
阅读FileUtils的API文档,可以归纳出以下在开发工作中比较常用的方法(详细的可以查看commons-io的API文档):
- cleanDirectory:清空目录,但不删除目录。
- contentEquals:比较两个文件的内容是否相同。
- copyDirectory:将一个目录中的内容复制到另一个目录,可以通过FileFilter过滤需要复制的文件。
- copyFile:将一个文件复制到一个新地址。
- copyFileToDirectory:将一个文件复制到某个目录下。
- copyInputStreamToFile:将一个输出流中的内容复制到某个文件。
- deleteDirectory:删除目录。
- deleteQuietly:删除文件。
- listFiles:列出指定目录下的所有文件。
- openInputStream:打开指定文件的输入流。
- readFileToString:将文件内容作为字符串返回。
- readLines:将文件内容按行返回到一个字符串数组中。
- size:返回文件或目录的大小。
- write:将字符串内容直接写到文件中。
- writeByteArrayToFile:将字节数组内容写到文件中。
- writeLines:将容器中元素的toString方法返回的内容依次写入文件。
- writeStringToFile:将字符串内容写到文件中。
IOUtils类的常用方法
- buffer:将传入的流进行包装,变成缓冲流,并可以通过参数指定缓冲大小
- closeQueitly:关闭流。
- contentEquals:比较两个流中的内容是否一致。
- copy:将输入流中的内容复制到输出流中,并指定字符编码。
- copyLarge:将输入流中的内容复制到输出流中,适合用于大于2GB的内容复制。
- lineIterator:返回可以迭代每一行内容的迭代器。
- read:将输入流中的所有内容读入到字节数组中。
- readFully:将输入流中的所有内容读入到字节数组中。
- readLine:读取输入流内容中的一行。
- toBufferedInputStream,toBufferedReader:将输入流转为带缓冲的输入流。
- toByteArray,toCharArray:将输入流的内容转为字节数组、字符数组。
- toString:将输入流或数组中的内容转化为字符串。
- write:向流里面写入内容。
- writeLine:向流里面写入一行内容。
常用方法的例子
读取内容
写出内容
拷贝文件/文件夹
统计文件夹或文件夹大小
遍历文件
结语
IO流Java中一个特别重要的知识点,本篇内容对于IO流的讲解仅限于常用的使用方法,需要了解更详细的方法,可以参阅官方API文档。本篇至此完结,如有错误的地方,望在评论区指正。
猜你喜欢
- 2024-11-08 你居然只知道蓝绿发布?今天教你全链路灰度
- 2024-11-08 redis 分布式锁的 5个坑,真是又大又深
- 2024-11-08 Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权
- 2024-11-08 Spring Boot利用filter实现xss防御
- 2024-11-08 Spring连环CVE-2015-5211和CVE-2020-5421漏洞升级教程
- 2024-11-08 如何进行权限系统设计,一文吃透 如何设计一个权限系统
- 2024-11-08 基于Spring Boot的注解驱动式公众号极速开发框架FastBootWeixin
- 2024-11-08 教育平台项目前端:项目前后端接口联调,项目上线部署发布
- 2024-11-08 HTTP通讯框架选型HttpClient/OkHttp
- 2024-11-08 微信公众号自动回复功能开发 微信公众号平台自动回复功能
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)