引入
对于计算机来说,所有的数据都是以二进制形式储存。我们可以想象所有的数据都储存在一个“桶”里面。现在想把“桶”里面的水稳定、快速、有指向性的运输到另外一个地方,我们能怎么操作,最好的方法肯定是拿一个水泵,接通管子,以水流的形式进行传输。这样就可以完美满足需求。
计算机也一样,数据的输入/输出操作也是以“流”的形式进行传输的,我们称之为IO流。java.io包下提供提供了各种的关于流的API。我们下面慢慢讲解。
讲解IO流之前,File文件类的了解必不可少。如有不懂可以先了解一下下面这篇博客。点此直接进入
https://blog.youkuaiyun.com/qq_45676489/article/details/116243349
流的分类:
●按流的方向划分
输入流、输出流;输入和输出是站在内存的角度出发,或者站在程序的角度,磁盘文件读入程序里面就是输入流,程序里的数据写入磁盘就是输出流。
●按处理数据的类型划分
字节流、字符流;
字节流:传输的数据是:字节。(0\1表示的二进制数据)。
处理的文件(记事本无法打开)例:图片.jpg、视频.mp4,音频.mp3。
字符流:传输的数据是:字符。(编码表可查到的,能看得懂的)。
处理的文件(记事本可打开)例:文本.txt、C++代码.cpp、java代码.java。
●按功能划分
节点流、处理流;
节点流:处理数据的基本类型。(处理数据慢)
处理流:对已存在的节点流的封装。(比节点流更快)
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | writer |
节点流 | FileInputStream | FileOutputStream | FileReader | Filewriter |
缓冲流 | BufferedFileInputStream | BufferedFileOutputStream | BufferedFileReader | BufferedFilewriter |
转换流 | InputStreamReader | OutputStreamwriter | ||
对象流 | ObjectInputStream | ObjectOutputStream |
流的用法
一:输入标准化过程。
- 创建Fill类对象,指明读取数据的来源。(此文件在磁盘中要实际存在)
- 根据文件类型,创建相应的输入流对象(字节输入流FileInputStream、字符输入流FileReader)。构造器参数为:第一步创建的File类对象。
- 具体的读操作。创建对应数组,调用read()方法。字符流创建char数组,字节流创建byte数组。
- 调用close方法。实现流对象的关闭。
二:输出标准化过程。
- 创建Fill类对象,指明写出数据的位置。(此文件在磁盘中不一定实际存在)
- 根据文件类型,创建相应的输入流对象(字节输出流FileOutputStream、字符输入流FileWriter)。构造器参数为:第一步创建的File类对象。
- 具体的写操作。调用write()方法。
- 调用close方法。实现流对象的关闭。
注:在流操作时会出现异常,请使用try-catch-finaly处理。
下面 一个示例,代表了流的操作的四个步骤,所有流都是如此操作
/*
实现对文件的复制
*/
@Test
public void FileInputOutputStream(){
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
//1、造文件对象
File srcFile = new File("迪丽热巴.jpg");
File destFile = new File("迪丽热巴1.jpg");
//2、造流对象
fileInputStream = new FileInputStream(srcFile);
fileOutputStream = new FileOutputStream(destFile);
//3、读写操作。调用read,write方法
byte[] buffer = new byte[5];
int len;
while ((len = fileInputStream.read(buffer)) != -1){
fileOutputStream.write(buffer,0,len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4、流资源的关闭
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
部分方法的细节
输入流Fileread
读方法有两种,一个是一次读一个字符,另一个一次读一个字符数组。但两个方法的返回值都为int型,如果读取到文件的末尾,就返回-1.
public int read();
public int read(char[] cbuf);
注:如果使用数组读数据,文件长度不一定刚好是数组长度的整数倍。如果每一次读取的数据都是cbuf.length,就会造成读取数据的异常。我们会定义一个临时变量len,储存读取到数据的长度。这样就不会造成数据错误。
字符输出流FileWriter
构造方法
FileWriter(file,false) / FileWriter(file):对原文件的覆盖,不写第二个参数就默认为对文件的覆盖。
FileWriter(file,true):不会对原文件覆盖,而是在原文件基础上追加内容。
write方法:
public void write(char[] cbuf);
public abstract void write(char[] cbuf, int off, int len);off:开始下标,len:数据长度
注:我们一般不用第一个write方法。因为和上面read方法同样的问题。会造成数据读取错误。write(cubf,0,len)依靠read方法中定义的零时变量len,就可以很好的避开这个问题。
缓冲流
缓冲涉及的类:BufferedInPutStream/BufferedOutPutStream和BufferedWriter/BufferedReader
缓冲流的作用:提供流的读取、写入的速度
速度提升的原因:提供了一个缓冲区。
缓冲流特有的一个方法:readLine();一次读一行。
此方法返回值为String,当读到末尾时,返回null。
readLine和普通read方法对比:
//方式1:char形数组
//读写操作
// char[] chars = new char[512];
// int len;
// while ((len = bufferedReader.read(chars)) != -1) {
// bufferedWriter.write(chars, 0, len);
// }
//方式2:使用String
String data;
while ((data = bufferedReader.readLine()) != null){
//方法1
// bufferedWriter.write(data + "\n");//data中不包含换行符
//方法2
bufferedWriter.write(data);
bufferedWriter.newLine();//相当于添加一个换行符
}
转换流InputStreamReader/OutputStreamWriter
转换流作用: 流对象中可以对读取到的字节数据进行指定编码的编码转换。
InputStreamReader:将一个字节的输入流转换为字符的输入流。相当于解码的过程。看不懂->看得懂
OutputStreamWriter:将一个字符的输出流转换为字节的输出流。相当于编码的过程。看得懂->看不懂
图示:
转换流的对应构造函数:
InputStreamReader(InputStream); //通过构造函数初始化,使用的是IDEA默认的编码表utf-8。
InputStreamWriter(InputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
OutputStreamWriter(OutputStream); //通过该构造函数初始化,使用的是IDEA默认的编码表utf-8。
OutputStreamwriter(OutputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
通过转换流改变文件的编码集
@Test
public void test2() {
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
File file1 = new File("皇马.txt");
File file2 = new File("皇马_gdk.txt");
//字节输入流
FileInputStream fis = new FileInputStream(file1);
//字节输出流
FileOutputStream fos = new FileOutputStream(file2);
isr = new InputStreamReader(fis,"utf-8");
osw = new OutputStreamWriter(fos,"gbk");
//读写过程
char[] cbuf = new char[20];
int len;
while ((len = isr.read(cbuf)) != -1){
osw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (osw != null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
此时文件的编码集已被改变,“皇马_gdk.txt”文件IDEA已无法正常打开。
对象流: ObjectInputStream /ObjectOutputStream
ObjectOutputStream:内存中的对象–>存储中的文件、通过网络传输出去:序列化过程
ObjectInputStream:存储中的文件、通过网络接收过来–>内存中的对象:反序列化过程
对象的序列化机制
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象.
实现序列化的对象所属的类需要满足
- 需要实现接口:Serializable
- 当前类提供一个全局常量:serialVersionUID
- 除了当前Person类需要实现Serializable接口之外,还必须保证其内部所属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)