在网上看了很多有关序列化的文章,我自己也写了两篇,现在感觉这些文章都没有很好的把序列化说清楚(包括我自己在内),所以在此我将总结前人以及自己的经验,用更浅显易懂的语言来描述该机制,当然,仍然会有不好的地方,希望你看后可以指出,作为一名程序员应该具有不断探索的精神和强烈的求知欲望!
序列化概述:
简单来说序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是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的对象写出(即保存其状态),要恢复的话则用输入流。
例子:
import java.io.*;
public class Test
{
public static void main(String[] args)
{
Employee harry = new Employee("Harry Hacker", 50000);
Manager manager1 = new Manager("Tony Tester", 80000);
manager1.setSecretary(harry);
Employee[] staff = new Employee[2];
staff[0] = harry;
staff[1] = manager1;
try
{
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("employee.dat"));
out.writeObject(staff);
out.close();
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("employee.dat"));
Employee[] newStaff = (Employee[])in.readObject();
in.close();
/**
*通过harry对象来加薪
*将在secretary上反映出来
*/
newStaff[0].raiseSalary(10);
for (int i = 0; i < newStaff.length; i++)
System.out.println(newStaff);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Employee implements Serializable
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
/**
*加薪水
*/
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name = "+ name
+ ",salary = "+ salary
+ "]";
}
private String name;
private double salary;
}
class Manager extends Employee
{
public Manager(String n, double s)
{
super(n, s);
secretary = null;
}
/**
*设置秘书
*/
public void setSecretary(Employee s)
{
secretary = s;
}
public String toString()
{
return super.toString()
+ "[secretary = "+ secretary
+ "]";
}
//secretary代表秘书
private Employee secretary;
}
修改默认的序列化机制:
在序列化的过程中,有些数据字段我们不想将其序列化,对于此类字段我们只需要在定义时给它加上transient关键字即可,对于transient字段序列化机制会跳过不会将其写入文件,当然也不可被恢复。但有时我们想将某一字段序列化,但它在SDK中的定义却是不可序列化的类型,这样的话我们也必须把他标注为transient,可是不能写入又怎么恢复呢?好在序列化机制为包含这种特殊问题的类提供了如下的方法定义:
private void readObject(ObjectInputStream in) throws
IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out) throws
IOException;
(注:这些方法定义时必须是私有的,因为不需要你显示调用,序列化机制会自动调用的)
使用以上方法我们可以手动对那些你又想序列化又不可以被序列化的数据字段进行写出和读入操作。
下面是一个典型的例子,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.io.Serializable` 接口。任何实现了此接口的类的对象都可以被序列化。在类定义中添加 `implements Serializable` 即可: ```java public class MyClass implements Serializable ...
java核心知识点整理,面试很有用 Java核心知识点2.JVM JVM 是可运行 Java 代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个...5.6JAVA 序列化(创建可复用的 Java 对象) 5.7JAVA 复制 6. Spring 原理
除此之外,Java的IO还包括对象流,如`ObjectInputStream`和`ObjectOutputStream`,用于序列化和反序列化对象。还有用于处理网络通信的套接字流,如`SocketInputStream`和`SocketOutputStream`。 NIO(New Input/...
它可以确保在不同版本的Java应用程序中,对象的序列化和反序列化都是正确的。同时,显式地定义serialVersionUID也可以确保类的不同版本对序列化兼容或不兼容。 在Eclipse中,当采用程序的Add default Serial ...
反射机制允许程序在运行时动态地获取类的信息(如类名、属性、方法等)并进行操作,提供了对Java对象模型的动态访问能力。 这些只是Java面试中可能会遇到的一部分问题,涵盖范围广泛,从基础语法到高级特性,深入...
在Java编程语言中,序列化是一个关键的概念,它允许我们将对象的状态转换成字节流,以便于存储或在网络上传输。这个过程对于数据持久化、跨网络通信以及实现某些高级功能(如对象缓存和分布式计算)至关重要。下面将...
### Java私塾学习笔记整理 #### 第一章:Java入门 **一、Java是什么?** Java是一种广泛使用的高级编程语言,由Sun Microsystems于1995年推出。它旨在为跨平台开发提供一种通用的语言环境,使开发者能够在任何...
3. 序列化:知道如何对集合进行序列化和反序列化,以及序列化的标识符`transient`关键字。 四、并发编程 1. 线程:掌握线程的创建、同步和协作,包括synchronized、volatile、ThreadLocal关键字的用法。 2. ...
在JDK 1.1.6之后,对象序列化协议进行了升级,`useProtocolVersion()`方法允许在序列化时选择协议版本。这使得能够兼容旧版本的序列化数据,并且提供了向后兼容性。 `defaultWriteObject()`和`enableReplaceObject...
7. **输入输出流**:Java的I/O流系统处理数据的读取和写入,包括文件I/O、网络I/O以及对象序列化。 8. **多线程**:Java内置对多线程的支持,可以通过实现Runnable接口或继承Thread类创建并运行线程,实现并发执行...
3. Java 中对象的序列化技术 - 鬼谷军师的日志 - 网易博客.htm:此文件详细讲解了JAVA中的对象序列化技术,包括序列化的概念、使用场景、操作步骤以及相关注意事项,对于理解JAVA高级特性非常有帮助。 4. Java面试...
5. **IO流**:Java的输入输出系统支持字节流和字符流,包括文件操作、网络通信和对象序列化。BufferedReader和PrintWriter用于文本处理,FileInputStream和FileOutputStream处理二进制数据。 6. **多线程**:Java...
9. org.springframework.util.xml.SerializationUtils 序列化和反序列化工具类,常用的方法有 serialize()、deserialize() 等。 二、Guava 工具类 1. com.google.common.base.Joiner 字符串连接器工具类,常用的...
- **对象序列化**: 理解如何将对象持久化为文件,以及反序列化的概念。 6. **网络编程** - **套接字编程**: 掌握基于TCP和UDP的网络通信基础,创建Socket和ServerSocket进行数据交换。 - **HTTP客户端**: 学习...
反序列化漏洞的本质是反序列化机制打破了数据和对象的边界,导致攻击者注入的恶意序列化数据在反序列化过程中被还原成对象,控制了对象可能在目标系统上面执行攻击代码,而不可信的输入和未检测反序列化对象的...
- Java中的序列化机制,用于对象状态的持久化。 - Java中的泛型,包括泛型的定义、使用以及类型擦除。 - Java的异常处理机制,包括异常的分类(checked、unchecked)以及异常的捕获和处理。 - Java集合框架中的...