HeadFirstJava学习——对象的序列化和文件的保存

4 篇文章 0 订阅

对象可以被序列化也可以展开。对象有状态和行为两种属性。行为存在于类中,而状态则保存在个别对象中。java中数据的保存分为2种:

1. 序列化(Serialization):将序列化的对象写入到文件中。然后你就可以让你的程序去文件中读取序列化的对象并把它们展开到活生生的状态【只有自己写的java程序会用到这些数据】(序列化的文件是很难让一般人阅读的,但是它比纯文本文件更容易让程序恢复之前的状态,也比较安全,因为一般问不会知道要如何动手脚改数据);

2. 写一个纯文本文件。用其他应用程序可以解析的特殊字符写到文件中。例如写成tab字符来分割的文件以便让电子表格或数据库应用程序能够应用【如果数据需要被其他应用程序所引用】。

如果要让类能够被序列化,就需要实现Serializable接口,该接口又被称为maker或者tag类的标记用接口,因为此接口中并没有任何方法是需要实现的。它的唯一目的就是声明有实现它的类是可以被序列化的。也就是说,此类型的对象可以通过序列化的机制来存储。如果某类是可序列化的,则其子类也自动可以序列化(接口的本意就是如此)。


如果某个实例变量不能或者不应该被序列化,就把它标记位transient(瞬时)的。


反序列化(Deserialization):还原对象

将对象进行序列化整件事情的重点就在于你可以在事后,在不同的JVM执行期(甚至不是同一个JVM虚拟机),把对象恢复到存储时的状态。


静态变量不会被序列化,因为static代表“每个类一个”而不是“每个对象一个”。当对象被还原时,静态变量会维持类中原来的样子,而不是存储时的样子。


对象在进行反序列化的时候,新的对象会被配置在堆上,但是构造方法不会执行!很明显,这样会把对象的状态抹去又变成新的,而这不是我们想要的结果。我们需要的是对象回到存储时的状态。

如果对象在继承树上有个不可序列化的祖先类,则该不可序列化的类以及它之上的类(就算是可序列化的也一样)的构造方法就会执行。一旦构造方法连锁启动之后将无法停止。也就是说,从第一个不可序列化的父类开始,它之上的全部回到初始状态。

对象的实例变量会被还原成序列化时点的状态值。transient变量会被赋值null的对象引用或者primitive主数据类型的默认值0、false等值。

SerialVersionUID:

每当对象被序列化的同时,该对象(以及所有在其版图上的对象)都会被“盖”上上一个类的版本识别ID。这个ID就是SerialVersionUID。它是根据类的结构信息计算出来的。在对象被解序列化的时候,如果在对象被序列化之后类有了不同的SerialVersionUID,则还原操作就会失败!当java尝试还原对象的时候,它会比对对象和JVM上的类的SerialVersionUID。因此解决方案是把SerialVersionUID放在class中,让类在演化的过程中维持相同的ID(使用JDK的serialver.exe查看)

对象读取的顺序必须和写入的顺序相同。

import java.io.Serializable;

public class Student implements Serializable{
	/*需要实现序列化接口*/
	private int age;
	private String name;
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Student(int age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	@Override
	public String toString() {
		return "Student [age=" + age + ", name=" + name + "]";
	}
	
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectSerializableDemo {

	public static void main(String[] args) {
		Student s1 = new Student(12, "Tom");
		Student s2 = new Student(13, "Kitty");
		Student s3 = new Student(15, "Happy");

		// 假设此处有改变对象状态的代码

		/* 序列化对象 */
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(
					new FileOutputStream(new File("E:" + File.separator + "tmp"
							+ File.separator + "Student.info")));
			oos.writeObject(s1);
			oos.writeObject(s2);
			oos.writeObject(s3);																        oos.close();		//记得关闭流哦!
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		s1 = s2 = s3 = null; // 设定成null,因此无法存储堆上的对象

		/* 反序列化对象 */
		try {
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
					new File("E:" + File.separator + "tmp" + File.separator
							+ "Student.info")));
			s1 = (Student) ois.readObject();
			s2 = (Student) ois.readObject();
			s3 = (Student) ois.readObject();

			System.out.println(s1);
			System.out.println(s2);
			System.out.println(s3);																        ois.close();//记得关闭流哦!
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}
运行结果:E盘会生成一个Student.info的文件(该文件不是文本文件,用记事本打开是乱码)控制台输出:

Student [age=12, name=Tom]
Student [age=13, name=Kitty]
Student [age=15, name=Happy]

如果将上述代码中Student类中的age属性加入transient关键字修饰,那么age将不被序列化,读取对象的时候默认是0.

Student [age=0, name=Tom]
Student [age=0, name=Kitty]
Student [age=0, name=Happy]


写入和读取文本文件:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterAndFileReaderDemo {

	public static void main(String[] args) {
		File file = new File("E:" + File.separator + "tmp" + File.separator
				+ "hello.txt");
		try {
			FileWriter fileWriter = new FileWriter(file);

			/* 将字符串写入文件 */
			fileWriter.write("日照香炉生紫烟");
			fileWriter.write("\r\n遥看瀑布挂前川");

			fileWriter.close(); // 记得关闭流哦!
		} catch (IOException e) {
			e.printStackTrace();
		}

		BufferedReader reader;
		try {
			reader = new BufferedReader(new FileReader(file));

			String str = null;
			while ((str = reader.readLine()) != null) {
				System.out.println(str);
			}
			reader.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
PS:获取系统的换行符,我们可以使用 System.getProperty("line.separator")。InputStreamReader和OutputStreamWriter的构造方法中可以指定字符集,常用来解决乱码问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值