对象的序列化
何为序列化和反序列化?
序列化机制允许将实现序列化的java对象转换成字节序列,这些字节序列可以保存在磁盘上,或者通过网络的传输,以备以后重新恢复成原来的对象。序列化机制使得对象可以脱离程序的运行独立存在。
对象的序列化指将一个java对象写入IO流中,与此对应的是,对象的反序列化则指从IO流中恢复该java对象。
如何将对象设置为可序列化的?
将类实现Serializable接口。这只是一个标记接口,没有任何的方法。
对象的序列化的用途?
所有可能在网络上传输的对象都应该是可序列化的,否则程序将会出现异常。比如RMI(远程方法调用,是java ee的基础)过程的参数和返回值;所有因为保存到磁盘里的对象的类都必须可序列化,
比如Web应用中需要保存到HttpSession或者ServletContext属性的java对象。
因为序列化是RMI过程的参数和返回值都必须实现的机制,而RMI又是java EE 技术的基础---------所有的分布式应用常常需要跨平台,跨网络,所以要求所有传递的参数,返回值必须实现序列化,
因此:序列化机制是java EE平台的基础。通常建议:程序创建的每一个JavaBean类都实现Seralizable.
对象序列化与反序列化的应用实例:
1.将对象写入磁盘,然后读出。
1.将对象所属的类实现Serialize接口:
public class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
public class Test {
public static void main(String[] args) {
Student st=new Student("徐才厚", 17);
//创建File对象
File file=new File("D:\\3.txt");
try {
//创建一个ObjectOutPutStream输出流
ObjectOutputStream oos=new ObjectOutputStream
(new FileOutputStream(file));
//序列化teacher对象
oos.writeObject(st);
//创建一个ObjectInputStream输入流
ObjectInputStream ois=new ObjectInputStream
(new FileInputStream(file));
Student stu=(Student) ois.readObject();
System.out.println(stu);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2.将对象在网络上传输,在网络上传输时,在服务的另一端,反序列化读取的仅仅是java对象的数据,而不是Java类,因此反序列化恢复java对象时,必须提供Java对象所属类的class文件,否则将会引发ClassNotFoundException异常。
但是在客户端和服务器端都必须有Person类的class文件。
客户端:
public class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Client {
public static void main(String[] args) {
try {
Socket socket=new Socket("localhost", 9999);
OutputStream os=socket.getOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(os);
Person per=new Person("徐才厚", 19);
oos.writeObject(per);
//加flush与不加flush影响不大,但是一般都加上,确保数据全部都能在通道里传输。
oos.flush();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
服务器端:
public class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class server {
public static void main(String[] args) {
try {
ServerSocket ss=new ServerSocket(9999);
Socket socket=ss.accept();
InputStream is=socket.getInputStream();
ObjectInputStream ois=new ObjectInputStream(is);
Object obj=ois.readObject();
System.out.println(obj);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2.自定义序列化
实现一:
如果我们不想序列化对象中的某个字段,我们一般在对象所属的类中的字段前加上transient关键字。这样在序列化对象时,此字段的值不会被序列化。就像上一个Person类,如果age这个字段
被transient修饰,则,打印对象的时候,这时候age为默认值为0.
实现二:
我们可以重写Person对象的
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
} 和
private void readObject(java.io.ObjectInputStream io) throws IOException,ClassNotFoundexception{
}
writeObject()方法负责写入特定类的实例状态,以便相应的readObject()方法可以恢复它,通过重写此方法,我们可以完全获得对序列化
机制的控制,可以自主决定哪些Fileld需要序列化,需要怎样的序列化。这样同时保证了网络传输的安全性,因为在传输时,进行了加密。
同时readObject()方法负责从流中读取并恢复对象Field,通过重写该方法,我们可以完全获得对反序列化机制的控制,可以自主决定需要反序列化
哪些字段,以及如何进行反序列化。如果writeObject()方法中对java对象进行了一些处理,则应该在readObject()方中,对其Field字段
进行相应的处理,以便正确的恢复该对象。
代码示例:其他的代码,与上面的代码一样。
public class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/***
* 功能:对于自定义的序列化,序列化和反序列化Person实例没有任何的
* 区别,区别在于序列化的对象流,即使有Cracker截获到Person对象流
* ,看到的name也是加密后的name,这样就提高了序列化的安全性,这样
* 在网络流中是安全的。同时在序列化时,你可以自行的定义哪些字段可以序列化
* 哪些字段不可序列化。这个这样的话可以进行加密。网络传输更安全。重写
* writeObject()方法和readObject()方法。
* *******/
private void writeObject(java.io.ObjectOutputStream out){
try {
//将name Field值反转后写入二进制流
out.writeObject(new StringBuffer(name).reverse());
//out.writeInt(age);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void readObject(java.io.ObjectInputStream in){
try {
//将读取的字符串反转后赋给name field
this.name=((StringBuffer)in.readObject()).reverse().toString();
//this.age=in.readInt();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
实现三:
Java的序列化机制保证在序列化某个对象之前,先调用该对象的writeReplace()方法,如果该方法返回另一个对象
,则系统转化为序列化另一个Java对象。
/**
*功能的描述:
*java的序列化机制保证在序列化某个对象之前,先调用该该对象的
*writeReplace()方法,如果该方法返回另一个java对象,则系统
*转化为序列化另一个对象,此程序表面上是序列化Person对象,实际
*上是序列化ArrayList对象。
*
* ***/
public class Teacher implements java.io.Serializable {
private String name;
private int age;
private char sex;
public Teacher(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private Object writeReplace() throws ObjectStreamException
{
ArrayList<Object>list=new ArrayList<Object>();
list.add(name);
list.add(age);
return list;
}
}
测试代码:
public class testWriteReplace {
public static void main(String[] args) {
//创建File对象
File file=new File("D:\\3.txt");
try {
//创建一个ObjectOutPutStream输出流
ObjectOutputStream oos=new ObjectOutputStream
(new FileOutputStream(file));
Teacher t1=new Teacher("徐才厚", 19);
//序列化teacher对象
oos.writeObject(t1);
ObjectInputStream ois=new ObjectInputStream
(new FileInputStream(file));
ArrayList<Object>array=(ArrayList<Object>) ois.readObject();
System.out.println(array);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
与writeReplace()方法相对的是readResolve()方法
这个方法紧接着readObject()之后被调用,该方法的返回值会代替原来反序列化的对象,而原来readobject()反序列化的对象将会被立即丢弃。readResolve()在序列化单例类,枚举类时,有用。
这个的意思是我们在序列化和反序列单例时,应该得到得是同一份对象。但是如果不重写readResolve()方法,readObject()方法会返回一个不同的对象,此时在执行readResolve()时,此时重写readresolve()
会返回重写后的一个对象,原有对象丢弃。
public class Orientation implements Serializable{
public static final Orientation HORIZONTAL=new Orientation(1);
public static final Orientation VERTICAL=new Orientation(2);
private int value;
public Orientation(int value) {
this.value = value;
}
/*****
功能测试:readResolve()方法会在readObject()之后被调用
该方法的返回值将会代替原来的反序列化的对象,而原来readObject()
反序列化的对象将会立即丢弃。它在序列化单例类和枚举类时尤其有用。
这只在把数据往磁盘中写时,起作用。
* *******/
private Object readResolve()throws ObjectStreamException{
if(value==1){
return HORIZONTAL;
}
if(value==2){
return VERTICAL;
}
return null;
}
}
public class testOrientation {
public static void main(String[] args) throws Exception{
File file=new File("D:\\4.txt");
ObjectOutputStream oos=new
ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(Orientation.HORIZONTAL);
ObjectInputStream ois=new
ObjectInputStream(new FileInputStream(file));
Object obj=ois.readObject();
System.out.println(obj==Orientation.HORIZONTAL); //打印true.
}
}
本文出自 “没有水勒鱼” 博客,转载请与作者联系!
原文地址:http://javaqun.blog.51cto.com/10687700/1703118