|
本帖最后由 煮咖啡 于 2011-4-7 19:21 编辑
Java对象序列化(整理篇)
在网上看了很多有关序列化的文章,我自己也写了两篇,现在感觉这些文 章都没有很好的把序列化说清楚(包括我自己在内),所以在此我将总结前人以及自己的经验,用更浅显易懂的语言来描述该机制,当然,仍然会有不好的地方,希 望你看后可以指出,作为一名程序员应该具有不断探索的精神和强烈的求知欲望!
序列化概述:
简单来说序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是I/O),我们可以对流化后的对象进行 读写操作,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)!在对对象流进行读写操作时会引发一些问题,而序列化机制正是用来 解决这些问题的!
问题的引出:
如上所述,读写对象会有什么问题呢?比如:我要将对象写入一个磁盘文件而后再将其读出来会有什么问题吗?别急,其中一个最大的问题就是对象引用!举个例子 来说:假如我有两个类,分别是A和B,B类中含有一个指向A类对象的引用,现在我们对两个类进行实例化{ A a = new A(); B b = new B(); },这时在内存中实际上分配了两个空间,一个存储对象a,一个存储对象b,接下来我们想将它们写入到磁盘的一个文件中去,就在写入文件时出现了问题!因为 对象b包含对对象a的引用,所以系统会自动的将a的数据复制一份到b中,这样的话当我们从文件中恢复对象时(也就是重新加载到内存中)时,内存分配了三个 空间,而对象a同时在内存中存在两份,想一想后果吧,如果我想修改对象a的数据的话,那不是还要搜索它的每一份拷贝来达到对象数据的一致性,这不是我们所 希望的!
以下序列化机制的解决方案:
1.保存到磁盘的所有对象都获得一个序列号(1, 2, 3等等)
2. 当要保存一个对象时,先检查该对象是否被保存了。
3.如果以前保存过,只需写入"与已经保存的具有序列号x的对象相同"的标记,否则,保 存该对象
通过以上的步骤序列化机制解决了对象引用的问题!
序列化的实现:
将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
例子:
(注:这些方法定义时必须是私有的,因为不需要你显示调用,序列化机制会自动调用的)
使用以上方法我们可以手动对那些你又想序列 化又不可以被序列化的数据字段进行写出和读入操作。
下面是一个典型的例子,java.awt.geom包中的 Point2D.Double类就是不可序列化的,因为该类没有实现Serializable接口,在我的例子中将把它当作LabeledPoint类中 的一个数据字段,并演示如何将其序列化!
- import java.io.*;
- import java.awt.geom.*;
- public class TransientTest
- {
- public static void main(String[] args)
- {
- LabeledPoint label = new LabeledPoint("Book", 5.00, 5.00);
- try
- {
- System.out.println(label);//写入前
- ObjectOutputStream out = new ObjectOutputStream(new
- FileOutputStream("Label.txt"));
- out.writeObject(label);
- out.close();
-
- System.out.println(label);//写入后
-
- ObjectInputStream in = new ObjectInputStream(new
- FileInputStream("Label.txt"));
- LabeledPoint label1 = (LabeledPoint)in.readObject();
- in.close();
- System.out.println(label1);//读出并加1.0后
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
-
- }
- class LabeledPoint implements Serializable
- {
- public LabeledPoint(String str, double x, double y)
- {
- label = str;
- point = new Point2D.Double(x, y);
- }
-
- private void writeObject(ObjectOutputStream out) throws IOException
- {
- /**
- *必须通过调用defaultWriteObject()方法来写入
- *对象的描述以及那些可以被序列化的字段
- */
- out.defaultWriteObject();
- out.writeDouble(point.getX());
- out.writeDouble(point.getY());
- }
-
- private void readObject(ObjectInputStream in)
- throws IOException, ClassNotFoundException
- {
- /**
- *必须调用defaultReadObject()方法
- */
- in.defaultReadObject();
- double x = in.readDouble() + 1.0;
- double y = in.readDouble() + 1.0;
- point = new Point2D.Double(x, y);
- }
-
- public String toString()
- {
- return getClass().getName()
- + "[label = "+ label
- + ", point.getX() = "+ point.getX()
- + ", point.getY() = "+ point.getY()
- + "]";
- }
-
- private String label;
- transient private Point2D.Double point;
- }
复制代码
什么是java序列化,如何实现java序列化? 收藏
Java 串行化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播, 并可以随时把对象持久化到数据库、文件等系统里。Java的串行化机制是RMI、EJB等技术的技术基础。用途:利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。 可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对 象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的 对象写出(即保存其状态), 要恢复的话则用输入流。
2、 串行化的特点:
(1)如果某个类能够被串行化,其子类也可以被串行化。如果该类有父 类,则分两种情况来考虑,如果该父类已经实现了可串行化接口。则其父类的相应字段及属性的处理和该类相同;如果该类的父类没有实现可串行化接口,则该类的父类所有的字段属性将不 会串行化。
(2)声明为static和transient类型的成员数据不能被串行化。因为static代表类的状态, transient代表对象的临时数据;
(3)相关的类和接口:在java.io包中提供的涉及对象的串行化的类与接口有ObjectOutput接口、ObjectOutputStream类、ObjectInput接口、ObjectInputStream类。
(1)ObjectOutput接口:它继承DataOutput接口并且支持对象的串行化,其内的writeObject()方法实现存储一个对象。ObjectInput接口:它继承DataInput接口并且支持对象的串行化,其内的readObject()方法实现读取一个对象。
(2)ObjectOutputStream类:它继承OutputStream类并且实现ObjectOutput接口。利用该类来实现将对象存储(调用ObjectOutput接口中的writeObject()方法)。ObjectInputStream类:它继承InputStream类并且实现ObjectInput接口。利用该类来实现读取一个对象(调用ObjectInput接口中的readObject()方法)。
对于父类的处理,如果父类没有实现串行化接口,则其必须有默认的构造函数(即没有参数的构造函数)。否则编译的时候就会报错。在反串行化的时候,默认构造函数会被调用。但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。这是为什么呢?这是因为Java 对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成。
- import java.io.*;
- public class Cat implements Serializable {
- private String name;
- public Cat () {
- this.name = "new cat";
- }
- public String getName() {
- return this.name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public static void main(String[] args) {
- Cat cat = new Cat();
- try {
- FileOutputStream fos = new FileOutputStream("catDemo.out");
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- System.out.println(" 1> " + cat.getName());
- cat.setName("My Cat");
- oos.writeObject(cat);
- oos.close();
- } catch (Exception ex) { ex.printStackTrace(); }
- try {
- FileInputStream fis = new FileInputStream("catDemo.out");
- ObjectInputStream ois = new ObjectInputStream(fis);
- cat = (Cat) ois.readObject();
- System.out.println(" 2> " + cat.getName());
- ois.close();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- }//writeObject和readObject本身就是线程安全的,传输过程中是不允许被并发访问的。所以对象能一个一个接连不断 的传过来
复制代码
该贴已经同步到 煮咖啡的微博 |
上一篇: “怎么走”升级?独家发布?商务、旅游、日常出行--航班火车时刻查下一篇: [车辆管理] aCar Pro v2.5 汉化版 (10年12月21日)
|