序列化和文件的输入/输出
保存对象
1.序列化:可以让你的程序读取序列化对象
2.写纯文本文件:数据需要被其他程序引用。
对象序列化的方法:
1.创建出FileOutputStream
FileOutputStream fileStream = new FileOutputStream("MyGame.ser");
2.创建ObjectOutputStreamObjectOutputStream os = new ObjectOutputStream(fileStream);
3.写入对象os.writeObject(characterOne);
os.writeObject(characterTwo);
4.关闭ObjectOutputStreamos.close();
数据在串流(stream)中流动,它代表来源于目的地之间的连接。
对象被序列化时发生了什么?
1.堆上的对象有状态——实例变量的值
2.被序列化的对象,保存了实例变量的值
当对象被序列化时,被该对象引用的实例变量也会被序列化。
让类能够被序列化:实现Serializable接口:Serializable接口又称为marker或tag类标记用接口,没有任何方法需要实现。
序列化是全有或全无的。
如果某实例变量不能/不应该被序列化,就把它标记为transient(瞬时)的,恢复对象时transient的引用实例变量会返回null
序列化可以分辨两个对象是否相同。如果两个对象都有引用实例变量指向相同的对象,只有一个对象会被存储。
解序列化(Deserialization):还原对象
1.创建FileInputStream
FileInputStream fileStream = new FileInputStream("MyGame.ser");//文件不存在会抛出异常
2.创建ObjectInputStreamObjectInputStream os = new ObjectInputStream(fileStream);
3.读取对象Object one = os.readObject();//每次调用readObject读取一个对象,读取顺序和写入顺序相同
Object two = os.readObject();
4.转换对象类型GameCharacter elf = (GameCharacter) one;
GameCharacter troll = (GameCharacter) two;
5.关闭ObjectInputStreamos.close();//FileInputStream会自动跟着关掉
1.对象从stream中读出来
2.JVM通过存储的信息判断出对象的class类型
3.JVM尝试寻找和加载对象的类,如果找不到或无法加载类,JVM会抛出异常
4.新的对象会被配置在堆上,但是构造函数不会执行。
5.如果对象在继承树上有个不可序列化的祖先类,该不可序列化类以及它之上的类的构造函数就会执行。从第一个不可序列化的父类开始,全部都会重新初始状态。
6.对象的实例变量会被还原成序列化时的状态值,transient变量会被赋值null或原始数据类型的默认0\、false等值。
Q:为什么类不会存储成对象的一部分?这样就不会出现找不到类的问题了?
A:这样非常浪费且有很多额外的工作。对于通过网络传送序列化对象来说,事实上有一种机制让类使用URL来定位,该机制用在Java的Remote Method Invocation(RMI,远程程序调用机制)
静态变量不会被序列化。static代表“每个类一个”而不是“每个对象一个”
将字符串写入文本文件
FileWriter writer = new FileWriter("Foo.txt");
writer.write("hello foo!");
writer.close();
java.io.File class:代表磁盘上的文件而不是文件的内容,或目录的路径名称。
可以对File做的事:
1.创建出代表现存盘文件的File对象
File f = new FIle("MyCode.txt");
2.建立新的目录File dir = new File("Chapeter7");
dir.mkdir();
3.列出目录下的内容if(dir.isDirectory()){
String[] dirContents = dir.list();
for(int i = 0;i < dirContents.length; i++){
System.out.println(dirContents[i]));
}
}
4.取得文件或目录的绝对路径System.out.println(dir.getAbsolutePath());
5.删除文件或目录(成功返回true)boolean isDeleted = f.delete();
BufferedWriter writer = new BufferedWriter (new FileWrite(aFile));
writer.write("Boulder");
writer.close();
要强制缓冲区立即写入:writer.flush();
读取文本文件
import java.io.*;
class ReadFile{
public static void main(String[] args){
try {
File myFile = new File("MyTest.txt");
FileReader fileReader = new FileReader(myFile);//FileReader 是字符连接到文本文件的串流
BufferedReader reader = new BufferedReader (fileReader);//将FileReader链接到BufferedReader 以获取更高的效率。它只会在缓冲区读空的时候才会回去取磁盘读取。
String line = null;
while ((line = reader.readLine()) !=null){
System.out.println(line);
}
reader.close();
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
VersionID:序列化的识别
每当对象被序列化的同时,该对象会被“盖”上一个类的版本识别ID,这个ID被称为serialVersionUID,它是根据类的结构信息计算出来的。在对象被解序列化时,如果在对象被序列化之后类有了不同的serialVersonUID,则还原操作会失败。
当Java尝试要还原对象时,它会比对对象与JVM上的类的serialVersionUID。若版本不相符,JVM会在还原过程中抛出异常。
解决方案:把serialVersionUID放在class中,让类在演化过程中维持相同的ID。
若想知道某个类的serialVersionUID,可以使用Java Development Kit里面所带的serialver工具来查询