一.IO流概述
在Java程序中,对于数据的输入/输出操作以"流" (stream)方式进行;Java提供了各种各样的“流"类,用以获取不同种类的数据;程序中通过标准的方法输入或输出数据。Java的流类型一般位于java.io包中。
二.流的分类
输出流输入流
我们想想平时我们在windows移动文件的时候是要进行什么操作,是不是先找到原文件然后复制,在去到目的地粘贴原文件。这里的原文件就是数据源,去到目的地粘贴原文件就是目的地,中转站在windows是内部帮我做的处理,在开发中我们的中转站可以看成是一个程序。
输出流:数据流向是数据源到程序(一般以InputStream,Read结尾)
输出流:数据流向是程序到目的地(一般以OutputStream,Write结尾)
输入输出流是相对于程序而言的,程序读取源文件,是不是数据往中转站里写,就是输入流。程序写数据到目的,是不是数据从程序里出去,就是输出流。
字节流字符流
字节流:以字节为单位获取数据的流(一般以InputStream,OutputStream结尾),可以传输任何文件(音频,文本等)。
字符流:以字符为单位获取数据的流(一般以Read,Write结尾),只能获取用记事本能打开的文件。
其实在java中只有字节流,字符流的底层使用的其实也是字节流。
节点流处理流
节点流:直接从数据源或目的地读写数据(FileInputStream,FileRead)。
处理流:不直接从数据源或目的地读写数据(BufferInputStream,BufferRead)。
节点流和处理流关系
只有节点流可以和数据直接连接,处理流不能,但是处理流能对节点流进行包装,提高性能和灵活性
数据流对象流
数据流:可以方便的对基本数据类型和引用数据类型(String)进行数据读写,并保留其本身的数据类型。如 DataInputStream(字节流,处理流)
对象流:可以方便的对引用数据类型进行数据读写,并保留其本身的数据类型。如 ObjectInputStream(字节流,处理流)
序列化与反序列化
在使用对象流的时候,我们需要对对象实现Serializable接口。因为我们的对象是存储在jvm中的方法区和堆中的,当我们需要将对象存在外部内存中,外部内存是没有jvm的这些区域,所以需要实现Serializable接口将对象转化为二进制数据保存。
import java.io.*;
public class ioLearn1 {
public static void main(String[] args) throws Exception {
write();
read();
}
//读文件
public static void read() throws Exception {
InputStream is = new FileInputStream("D:\\IoLearn\\readme.txt");
BufferedInputStream bis = new BufferedInputStream(is);
ObjectInputStream ois = new ObjectInputStream(bis);
System.out.println(ois.readInt());
System.out.println(ois.readUTF());
System.out.println(ois.readBoolean());
Student s = (Student)ois.readObject();
System.out.println(s.getName());
//关闭流
ois.close();
}
//写文件
public static void write() throws Exception{
//节点流 字节流
OutputStream os = new FileOutputStream("d:/IoLearn/readme.txt");
//缓冲流
BufferedOutputStream bos = new BufferedOutputStream(os);
//处理流 字节流
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeInt(1);
oos.writeUTF("bug一定有");
oos.writeBoolean(true);
Student s = new Student("王二愣",20);
oos.writeObject(s);
//关闭流
oos.close();
}
}
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID=7981560250804078637l;
String name ;
int age ;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
这里有个问题???
Q:为什么Serializable接口里没东西还要实现它进行序列化
A:我们点ObjectOutputStream里的writeObject->writeFatalException开源码看看就知道怎么回事了,在源码中,我们会判断该对象是否是Serializable会对对象进行特殊处理。
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
三.流的体系结构
四.IO流使用报错异常
NotSerializableException
Exception in thread "main" java.io.NotSerializableException: com.wq.ioLearn.Student
(1)对应的类没有实现Serializable接口。
(2)类对象里还包含类对象,包含的类对象需要要实现Serializable接口
InvalidClassException
Exception in thread "main" java.io.InvalidClassException: com.wq.ioLearn.Student; local class incompatible: stream classdesc serialVersionUID = 2327102107518923782, local class serialVersionUID = 3821233561147490119
序列化会有一个版本号控制,当对应的对象进行修改后,序列化的版本号也会有对应的修改,写读的版本号不一值的时候会出现这样的异常。
可以在对应的实体类里定义
private static final long serialVersionUID=7981560250804078637l;
EOFException
Exception in thread "main" java.io.EOFException
ObjectOutputStream和ObjectInputStream(DataOutputStream和DataInputStream)数据要按顺序读取,比如先writeInt->writeUTF->writeBoolean就要readInt->readUTF->readBoolean,少读没问题,多读会报错。
五.结束语
以前刚学IO流的时候,劈里啪啦,劈里啪啦一大堆类直接砸过来,懵逼的我怎么可能记得住这么多,后来也就不了了之了,再回过头来看看,数据的分类和规则其实理解了也没那么难记,果然二刷能变强。