前言
今天我们来说一下在Java里对文件的操作,IO流(输入输出流)。首先在计算机中文件在计算机上的信息集合,可以是文本、图片、视频等 。文件是以二进制的方式存放的
输入和输出流
输入流:可以从其中读入一个字节序列的对象称作输入流
输出流:可以从其中写入一个字节序列的对象称作输出流
字符序列的来源地和目的地可以是文件(通常是)、网络连接、内存块等
IO流常用类
InputStream和OutputStream
在常用类中抽象类InputStream和OutPutStream构成了输入和输出类层次结构的基础。它们也被称为字节输入/输出流。它们是所有类字节输入/输出流的父类。
类图:
Reader和Writer
因为字节流是以一个字节来作为处理的基本单位,对于使用Unicode形式储存的信息(文本信息)不是特别方便的去进行处理,因为在Unicode中每个字符都使用了多个字节来表示。抽象类Reader和Writer中存在一个继承出来一个专门用于处理Unicode字符的单独类层次结构,它们是基于两个字节的Char值(即一个Unicode码元)进行读写操作的,而不是一个字节
类图:
IO流的使用
对文件的操作
对非文本文件建议使用字节流
package com.cheng.IOLearn;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
// 1、创建文件的输入流,将文件读入到程序
// 2、创建文件到输出流,将读取到的文件数据,写入到指定到文件
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
String filePath = "//Users//chenzouquan//Desktop//未命名文件夹//截屏2021-10-20 下午5.21.49.png";
String destPath = "//Users//chenzouquan//Desktop//未命名文件夹//text.png";
try {
fileInputStream = new FileInputStream(filePath);
fileOutputStream = new FileOutputStream(destPath);
// 定义一个字节数组,提高读取效率
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1){
// 读取到后,就写入到文件,即边读边写
fileOutputStream.write(buf,0,readLen);// 一定要使用这个方法
}
System.out.println("拷贝成功");
} catch (IOException e) {
e.printStackTrace();
}finally {
// 关流
try {
if (fileInputStream != null){
fileInputStream.close();
}
if (fileOutputStream != null){
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对文本文件建议使用字节流
package com.cheng.IOLearn;
import java.io.FileReader;
import java.io.IOException;
public class LearnFileReader {
public static void main(String[] args) {
// 1.指定文本路径
String strPath = "//Users//chenzouquan//Desktop//未命名文件夹//test.txt";
// 2、创建文件的输入流,将文件读入到程序
FileReader fileReader = null;
int data = 0;
try {
fileReader = new FileReader(strPath);
// 读取到后,就写入到文件,即边读边输出
while( (data = fileReader.read()) != -1){
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关流
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在进行操作的要点:在执行完输入和输出操作后一定要记得关流(close)或者刷新流(flush),负责文件不能保存原因是在程序执行close或者flush之前并没有真正的的修改文件而是在执行close或者flush方法中进行正式的修改文件。close方法中会执行flush方法所以我们一般只要进行关流就足够了。
节点流和处理流
节点流:可以从一个数据源进行读写操作(如FileReader/FileWriter,FileInputStream/FileOutputStream…)
处理流:包装一个节点流或者处理流,对它们进行业务拓展给程序提供更好的读写功能(如BufferedReader/BufferedWriter…)
从源码上看这里使用了装饰器模式:即把一个装饰器对象中存入一个要进行操作的对象,在这个装饰器对象里对这个对象进行操作
以BufferedReader为例,他有属性Reader所以可以封装Reader子类进行操作
package com.cheng.IOLearn;
public abstract class Reader_ {
public void Reader_File() {
}
}
class FileReader_ extends Reader_{
@Override
public void Reader_File(){
System.out.println("FileReader_");
}
}
class StringReader_ extends Reader_{
@Override
public void Reader_File(){
System.out.println("StringReader_");
}
}
/**
* 处理流/包装流
*/
class BufferedReader_ extends Reader_{
private Reader_ reader;
public BufferedReader_(Reader_ reader) {
this.reader = reader;
}
// 下面可以扩展对应功能
}
对象流(序列化和反序列化)
序列化
储存几个数据包括数据类型
Int num; char a; String str;
public class TestObjectOutputStreamLearn {
public static void main(String[] args) throws IOException {
// 序列化保存文件位置
String path = "//Users//chenzouquan//Desktop//未命名文件夹//data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
// 序列化数据
oos.write(100);
oos.writeChar('a');
oos.writeUTF("测试");
// 序列化对象要让需要序列化的对象实现 Serializable接口
oos.writeObject(new Cat());
// 关流
oos.close();
System.out.println("序列化完毕");
}
}
Java对象
public class Cat implements Serializable {
// 提供序列化版本号,这样在类修改后,序列化和反序列化的时候就会认为这是一次更新而不是一个新的类
private static final long serialVersionUID = 1L;
private String name = "hello";
int age = 11;
public String CarName(){
return this.name;
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public Cat() {
}
}
反序列化
public class TestObjectOutputStreamLearn {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 文件位置
String path = "//Users//chenzouquan//Desktop//未命名文件夹//data.dat";
ObjectInputStream oos = new ObjectInputStream(new FileInputStream(path));
// 读取数据要和序列化顺序一致
int i = oos.readInt();
char c = oos.readChar();
String s = oos.readUTF();
System.out.println(i+c+s);
Object o = oos.readObject();
// 如果使用原来的对象的方法和属性,要转化为原来对象
Cat cat = (Cat) o;
cat.CarName();
oos.close();
}
}