对象可以被序列化也可以展开
对象序列化的步骤:
1.创建出FileOutputStream
FileOutputStream fileStream = new FileOutputStream("MyGame.ser");//如果文件不存在,它会自动创建出来
2.创建ObjectOutputStream
ObjectOutputStream os = new ObjectOutputStream(fileStream);
3.写入对象
os.writeObject(characterOne);
os.writeObject(characterTwo);
os.writeObject(characterThree);//将变量所引用的对象序列化并写入MyGame.ser这个文件
4.关闭ObjectOutputStream
os.close();
串流
将串流连接起来代表来源与目的地(文件或网络端口)的连接。串流必须要连接到某处才能算是个串流。Java的输入输出API带有连接类型的串流,它代表来源与目的地之间的连接,连接串流即把串流与其它串流连接起来。
对象序列化
1.在堆上的对象有状态——实例变量的值。这些值让同一类的不同实例有不同的意义。
2.序列化后的对象保存了实例变量的值,因此之后可以在堆上带回一模一样的实例。
3.序列化程序会将对象版图上的所有东西存储起来。被对象的实例变量所引用的所有对象都会被序列化。
4.静态变量不会被序列化,因为所有对象都是共享同一份静态变量值。
如果要让类能够被序列化,就实现Serializable接口。
如果某类是可序列化的,则它的子类也自动地可以序列化。
如果某实例变量不能或不应该被序列化,就把它标记为transient的。
import java.net.*;
class Chat implements Serializable {
transient String currentID;
String userName;
// more code
}
解序列化
1.创建FileInputStream
FileInputStream fileStream = new FileInputStream(“MyGame.ser”);//文件不存在会抛出异常
2.创建ObjectInputStream
ObjectInputStream os = new ObjectInputStream(fileStream);
3.读取对象
Object one = os.readObject();
Object two = os.readObject();
Object three = os.readObject();//每次调用都会从stream中读取下一个对象
4.转换对象类型
GameCharacter elf = (GameCharacter) one;
GameCharacter troll = (GameCharacter) two;
GameCharacter magician = (GameCharacter) three;
5.关闭ObjectInputStream
os.close();
**解序列化时发生的事:
1.对象从流中读取
2.JVM通过存储的信息判断对象的class类型。
3.JVM尝试查找并加载对象的类。如果JVM无法找到或加载类,JVM抛出一个异常和反序列化失败。
4.在堆上给一个新对象空间,但构造函数不执行!显然,对象的状态会被抹去又变成全新的,而那不是我们想要的。我们希望还原对象到存储时的状态。
5.如果对象在它的继承树的某个地方有一个不可序列化的父类,该不可序列化类的构造函数将与它上面的类的构造函数一起执行。一旦构造函数连锁启动后无法停止,这意味着从第一个不可序列化的父类开始,全部都会重新初始化。
6.对象的实例变量会被还原为序列化时的状态值。transient变量会被值为null的对象引用或primitive主数据类型的默认值(0,false,等等)。
写入文本文件
import java.io.*;
class WriteFile {
public static void main(String[] args) {
try {
FileWriter writer = new FileWriter("Foo.txt");
writer.write("hello foo!");
writer.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
java.io.File class
File对象代表磁盘上的文件或目录的路径名称,但它并不能读取或代表文件中的数据。
可以对File做的事情:
1.创建出代表现存盘文件的File对象:
File f = new File("MyCode.txt");
2.建立新的目录:
File dr = new File("Chapter14");
dir.mkdir();
3.列出目录下的内容:
if(dir.isDirectory()) {
String[] dirContents = dir.list();
for(int i =0;i<dirContents.length;i++) {
System.out.println(dirContents[i]);
}
}
缓冲区
缓冲区的好处在于使用缓冲区比不使用缓冲区的效率要好。
BufferedWriter writer = new BufferedWriter(new FileWriter(aFile));
只有调用writer.flush()就可以要求缓冲区马上把内容写下去。
读取文本文件:
import java.io.*;
class ReadAFile {
public static void main (String[] args) {
try {
File myFile = new File(“MyText.txt”);
FileReader fi leReader = new FileReader(myFile);
BufferedReader reader = new BufferedReader(fi leReader);
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
解析字串
String的split()会用参数所指定的字符把字符串拆开成String的数组。
序列化的识别
1.每当对象被序列化的同时,该对象都会盖上上一个类的版本识别ID,这个ID被称为serialVersionUID。当对象解序列化时,如果对象序列化后类被修改,导致有了不同的serialVersionUID,则还原会失败。
2.如果你认为类有可能会演化,就把版本识别ID放在类中,让类在演化的过程中维持相同的ID。
3.JDK中的serialver工具可以查询类的serialVersionUID。
当类有可能演化时:
1.使用serialver工具取得版本ID。
2.把输出拷贝到类上
public class Dog {
static final long serialVersionUID = -6849794470754667710L;
private String name;
private int size;
// method code here
}
3.在修改类的时候要确定修改程序的后果。例如新的类要能够处理旧的对象解序列化后新加入变量的默认值。