IO 流
Java I / O 系统中流的概念
程序的运行都在内存中进行,在程序结束时,数据全部流失。为了让操作后的数据永久保存下来,我们需要使用外部存储器来完成。
那么,完成内存与外存之间的数据传输,可以使用输入输出(I/O)机制来完成。
I表示Input,意为输入,O表示Output,意为输出。
流
流(Stream)的基本概念
在Java中把I/O操作设计成了一个统一的模型,也就是“流模型”。
这种模型将输入输出看成是两个端点之间建立一根管道,然后数据就像流水 一样从数据源流向目的地。
数据源 --------> 管道 --------> 目的地
流的分类
1、
在Java语言中,流的方向以内存作为参照物,当从数据源中将数据读取到内存中时,称为输入流,也叫读取流。
按照数据传输方向分:
数据源 --------> 内存
当从内存中将数据写入到数据源时,称为输出流,也叫写入流。
内存 --------> 数据源
2、
在Java语言中,按照流处理数据的最小单位(管道粗细)分,分为:
字节流 --------> 以字节byte为最小单位传输
字符流 --------> 以字符char为最小单位传输
3、
按照流的功能分:
节点流:流的一个端点是外部设置(数据源)
处理流:基于节点流或处理流进行创建的,可以在数据传输过程中加入处理操作
节点流 --------> 可以直接 从 / 向数据源 读 / 写 数据
处理流 --------> 可以在数据传递过程中执行某个处理任务
PS:处理流不单独使用,而是连接在已经存在的流上
Java.io包
流操作的相关类都在 Java.io 包中存放
字节流有两个抽象父类:InputStream 和 OutputStream
字节流的输出流 | OutputStream |
字节流的输入流 | InputStream |
字符流的输出流 | Writer |
字符流的输入流 | Reader |
流的子类
不同的数据源读写数据的方式不同,所以在字节流和字符流的父类中,预留了读写数据的抽象方法,不同的子类根据自己数据源的特点分别去实现。
BufferedInputStream --------> 数据源是缓冲区
FileInputStream --------> 数据源是文件
ObjectInputStream --------> 数据源是对象
字节输入流InputStream共有方法
字节流只能对字节数据尽心操作
方法: | |
---|---|
read() | 从流中读取下一个字节 |
read(byte[ ] b) | 从输入流中读取一些字节,并存入数组b |
read(byte[ ] b , int off , int len) | 从输入流中读取前len个字节,并存入数组b |
close() | 关闭数据流 |
输入流的操作步骤
字节输入流InputStream方法
1、建立流,建立那内存和数据源的数据通道
2、处理数据
3、关闭流,释放资源
read(byte[ ] b)进行打印时会出现数据冗余的问题
通过read(byte[ ] b , int off , int len)可以解决冗余问题
字节输出流OutputStream方法
1、建立数据源传输数据到目的地的通道
2、对数据进行处理,写入流中
3、将缓存区的数据,强制写入数据源
4、关闭流,释放资源
字符读取流Reader类
字符流用于操作字符数据,可以直接对字符数据进行读取和写入。
Reader读取字符流常见的子类:
BufferedReader | 读取文件中的字符信息 |
InputStreamReader | 将字节流转化为字符流 |
BufferedReader | 套接字符流,需要以另一个字符流作为基础。将数据读入缓冲区,然后从缓冲区获取 |
BufferedReader
提供了readLine
()方法,可以从文本文件中整行的读取数据。
字符写入流Writer
try {
//方式一:
//字符输出流
// Writer writer = new FileWriter("2.txt");
//
// writer.write("星野爱\n" + "神木光");
// writer.flush();
// writer.close();
//方式二:
Writer writer = new FileWriter("2.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
bufferedWriter.write("为了部落");
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
关闭流
流操作后,需要关闭以释放内存。
1、如果操作完流以后,不关闭流,那么,在内存和数据源之间建立的数据通道不会消失,会浪费内存资源。
2、写入流将数据写入数据源时,需要通过flush()刷新语句,才能将数据真正写入数据源。在流关闭时,会自动执行flush()刷新语句。所以,写入流在不关闭,也不 刷新的情况下,有可能写不进数据。
3、在开发时,一般将流的关闭书写在finally语句块中。避免因为发生了异常,而没有对流进行关闭操作。
为什么需要关闭流?
因为当我们完成流的操作后,流不会自动进行关闭(除非程序结束),为了让无用的流不再占据消耗内存资源,需要主动调用close()方法完成流的关闭,释放内存。
PS:流关闭时,先开启的流,后关闭。
并且为了保证流在除程序结束(虚拟机关闭)的情况下,都会执行关闭操作,需要将关闭流的代码,写在就算有异常发生都会被执行的的位置(finally语句块),并且我们在调用流的close()方法前,应当判断流是否被成功的建立(流的变量是否为null)。
File类
当我们创建一个操作文件的流时(FileInputStream/FileReader),根据路径找不到文件,产生FileNotFoundException(文本未找到异常)。
所以为了让我们再创建流时,可以确定文件或目录是否存在,可以通过File来完成。
File类中提供了描述本地系统文件和目录的功能,属于Java.io包
File 类常用方法
String getAbsolutePath() | 得到当前文件对象的物理路径 |
---|---|
boolean exists() | 判断文件或目录是否存在 |
boolean isFile() | 判断文件对象是否是文件 |
boolean isDirectory() | 判断文件对象是否是目录 |
boolean delete( ) | 删除当前文件对象表示的文件或目录,如果删除目录,那么目录必须为空 |
boolean mkdir( ) | 创建一个目录 |
File[] listFiles() | 得到目录里所有的文件对象 |
String getAbsolutePath()
## 对象流
### 对象序列化
a、在对字符流进行读取和写入时,需要完成对象数据和字符数据的转换,需要进行大量的字符串拼接操作。通过对象流,可以直接对对象数据进行操作。
b、对象流再操作对象数据时,是无法直接对对象进行传输。所以,在传输之前,先将对象打散成字节序列,以利于传输。这个过程,称为序列化过程。
c、在字节序列到达目的地以后,又需要将字节序列还原成对象。这个过程,称为反序列化过程。
### 实现对象序列化和反序列化的方法
在Java中,所有需要实现对象序列化的对象,必须先实现***Java.io.Serializable***接口
``java.io.Serializable ``序列化接口是标识性接口,该接口中没有定义方法。
如果对未实现序列化接口的对象进行传输,那么会抛出``java.io.NotSerializableException``异常
### 对象流
在Java语言中,我们可以将对象直接保存在硬盘上,此时,对象中的引用及相关状态就永久保存了。在需要的时候我们可以将硬盘上的拷贝重新读入内存并恢复成原对象。
在``java.io``包中,提供了``ObjectOutputStream``,对对象信息进行保存操作。
ObjectOutoutStream(对象字节输出流)writrObject
ObjecInputStream(对象字节输入流)readObject;
transient
a、在传输对象时,默认情况下,对象的每个属性值都会被传输,如果想对对象的属性进行选择性传输,可以使用transient关键字。
b、transient是用于属性的修饰符,表示在对象序列化时,被transient修饰的属性值,不被传输。