Java Serialization transient
2010-05-12 13:35
85 查看
1. 什么是Serialization?
串行化(Serialization)是计算机科学中的一个概念,它是指将对象存储到介质(如文件、内在缓冲区等)中或是以二进制方式通过网络传输。之后 可以通过反串行化从这些连续的位数据重新构建一个与原始对象状态相同的对象,因此在特定情况下也可以说是得到一个副本,但并不是所有情况都这样。
Java有Serialization API为开发者提供了一种标准的机制来串行化类。
2. 为什么要Serilzation?
特别地,串行化主要有三种用途:
1)作为一种持久化机制
如果使用的是FileOutputStream流的方式,则数据将被自动地写入文件中,
2)作为一种复制机制
如果使用的是
3)作为一种通信机制
如果是使用套接字(Socket)流的方式,则数据自动地通过网络连接传输一另一个端点,并由这个端点上的程序来决定做什么。
3. Serialization的基本用法:默认机制
将要串行化的类必须实现java.io.Serializable接口,或者是继承实现了该接口的类。然后通过java.io.ObjectOutputStream类来实现持久化,如果用保存到文件上还需要用到java.io.FileOutputStream类。因为ObjectOutputStream被认为是java.io包中的高级类所以可用它来包装低级的类FileOutputStream。在持久化过程中调用的一个方法是ObjectOutputStream对象的writeObject(obj)方法。
当要从文件中恢复对象时,则是使用java.io.OjbectInputStream与FileInputStream类,调用一方法是ObjectInputStream对象的readObject()方法。
示例1:
/*定义一个实现了Serializable接口的类:Cat*/
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;
}
}
/*将Cat类的对象串行化与反串行化*/
import java.io.*;
public class CatDemo {
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 (IOException 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 (IOException ex) {
ex.printStackTrace();
}
}
}
4. Serialization常见问题
正如前面提到的,所有可串行化的类必须直接或是通过继承方式间接地实现java.io.Serializable接口,由于Object类关没有实现这个 接口,所以并不是所有类的对象都是可串行化的。像AWT与Swing的GUI组件、字符串、数组等都是可串行化的,而像一些系统级的类(Thread
问题一:如果在一个可串行化的类中Has-As不可串行化的类该怎么处理?
在这种情况下在运行时会抛出NotSerializableException。
为了解决类似问题,Java中提供了transient关键字来跳过对不可串行化类的对象的处理。但这依然可能会引起一些问题,在反串行化时,被标识为transient变量不会恢复到其原始状态,而是提供默认值,如示例2中的pig引用将赋值为null,age变量赋值为0;
附:基本类型和引用类型的默认值
对象引用:null
byte, short, int, long :0
float, double:0.0
boolean:false
char:'/u0000'(这是Unicode字符集的空格)
示例2:
假如在一个可以串行化的类中有一个不可串行化的对象,但又想保存该对象的状态信息该如何是好?在这样情况下可以在这个可串行化的类中实现writeObject()和readObject()。
示例4:
import java.io.*;
import java.util.Scanner;
public class NewPig3 implements Serializable {
private String newName;
private transient Pig pig = new Pig();
private transient int age = 2;
public NewPig3() {
newName = "new Pig 3";
//pig = new Pig();
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeChars(pig.getName());
//oos.writeInt(this.age);
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
pig = new Pig();
Scanner in = new Scanner(ois);
if (in.hasNextLine()) {
pig.setName(in.nextLine());
}
}
public Pig getPig() {
return this.pig;
}
public void setName(String name) {
this.newName = name;
}
public String getName() {
return this.newName;
}
public int getAge() {
return this.age;
}
}
串行化(Serialization)是计算机科学中的一个概念,它是指将对象存储到介质(如文件、内在缓冲区等)中或是以二进制方式通过网络传输。之后 可以通过反串行化从这些连续的位数据重新构建一个与原始对象状态相同的对象,因此在特定情况下也可以说是得到一个副本,但并不是所有情况都这样。
Java有Serialization API为开发者提供了一种标准的机制来串行化类。
2. 为什么要Serilzation?
特别地,串行化主要有三种用途:
1)作为一种持久化机制
如果使用的是FileOutputStream流的方式,则数据将被自动地写入文件中,
2)作为一种复制机制
如果使用的是
ByteArrayOutputStream流的方式,数据将写入内存中的字节数组中。该字节数组可以用来创建初始对象的副本,
3)作为一种通信机制
如果是使用套接字(Socket)流的方式,则数据自动地通过网络连接传输一另一个端点,并由这个端点上的程序来决定做什么。
3. Serialization的基本用法:默认机制
将要串行化的类必须实现java.io.Serializable接口,或者是继承实现了该接口的类。然后通过java.io.ObjectOutputStream类来实现持久化,如果用保存到文件上还需要用到java.io.FileOutputStream类。因为ObjectOutputStream被认为是java.io包中的高级类所以可用它来包装低级的类FileOutputStream。在持久化过程中调用的一个方法是ObjectOutputStream对象的writeObject(obj)方法。
当要从文件中恢复对象时,则是使用java.io.OjbectInputStream与FileInputStream类,调用一方法是ObjectInputStream对象的readObject()方法。
示例1:
/*定义一个实现了Serializable接口的类:Cat*/
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;
}
}
/*将Cat类的对象串行化与反串行化*/
import java.io.*;
public class CatDemo {
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 (IOException 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 (IOException ex) {
ex.printStackTrace();
}
catch(ClassNotFoundException ex) { ex.printStackTrace(); }
}
}
4. Serialization常见问题
正如前面提到的,所有可串行化的类必须直接或是通过继承方式间接地实现java.io.Serializable接口,由于Object类关没有实现这个 接口,所以并不是所有类的对象都是可串行化的。像AWT与Swing的GUI组件、字符串、数组等都是可串行化的,而像一些系统级的类(Thread
,OutputStream等)和Socket类是不可串行化的。
问题一:如果在一个可串行化的类中Has-As不可串行化的类该怎么处理?
在这种情况下在运行时会抛出NotSerializableException。
为了解决类似问题,Java中提供了transient关键字来跳过对不可串行化类的对象的处理。但这依然可能会引起一些问题,在反串行化时,被标识为transient变量不会恢复到其原始状态,而是提供默认值,如示例2中的pig引用将赋值为null,age变量赋值为0;
附:基本类型和引用类型的默认值
对象引用:null
byte, short, int, long :0
float, double:0.0
boolean:false
char:'/u0000'(这是Unicode字符集的空格)
示例2:
import java.io.*; public class NewPig2 implements Serializable { private String newName; private transient Pig pig = new Pig(); private transient int age = 2; public NewPig2() { newName = "new Pig 2"; //pig = new Pig(); } public Pig getPig() { return this.pig; } public void setName(String name) { this.newName = name; } public String getName() { return this.newName; } public int getAge() { return this.age; } }
问题二:如果父类不可串行化,子类实现了Serializable会怎样? 如果有一个Animal类是不可串行化的,而有一个Dog类继承自Animal类并且实现了Serializabl接口,则没有串行化时没有任何问题,但是在反串行化时将会重新调用Animal的构造函数,如示例3所示。 示例3的运行结果如下:
1> No Color - new Dog 2> Green - My Dog 4> No Color - My Dog 因为Animal不可串行化,所以必须运行构造函数,但不会在实现Serializable的反串行化类上运行构造函数。
示例3: /*Animal.java*/ public class Animal { private String color; public Animal () { this.color = "No Color"; } public void setColor(String color) { this.color = color; } public String getColor () { return this.color; } } /*Dog.java*/ import java.io.*; public class Dog extends Animal implements Serializable { private String name; public Dog () { this.name = "new Dog"; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } } /*DogTest.java*/ import java.io.*; public class DogTest { public static void main(String[] args) { Dog dog = new Dog(); System.out.println(" 1> " + dog.getColor() + " - " + dog.getName()); dog.setColor("Green"); dog.setName("My Dog"); System.out.println(" 2> " + dog.getColor() + " - " + dog.getName()); try {//串行化 FileOutputStream fos = new FileOutputStream("myDog.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(dog); oos.close(); } catch (Exception ex) { ex.printStackTrace(); } try {//反串行化 FileInputStream fis = new FileInputStream("myDog.out"); ObjectInputStream ois = new ObjectInputStream(fis); dog = (Dog) ois.readObject(); System.out.println(" 4> " + dog.getColor() + " - " + dog.getName()); ois.close(); } catch (Exception ex) { ex.printStackTrace(); } } } 5.自定义默认方法:使用writeOject和readObject
假如在一个可以串行化的类中有一个不可串行化的对象,但又想保存该对象的状态信息该如何是好?在这样情况下可以在这个可串行化的类中实现writeObject()和readObject()。
示例4:
import java.io.*;
import java.util.Scanner;
public class NewPig3 implements Serializable {
private String newName;
private transient Pig pig = new Pig();
private transient int age = 2;
public NewPig3() {
newName = "new Pig 3";
//pig = new Pig();
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeChars(pig.getName());
//oos.writeInt(this.age);
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
pig = new Pig();
Scanner in = new Scanner(ois);
if (in.hasNextLine()) {
pig.setName(in.nextLine());
}
}
public Pig getPig() {
return this.pig;
}
public void setName(String name) {
this.newName = name;
}
public String getName() {
return this.newName;
}
public int getAge() {
return this.age;
}
}
相关文章推荐
- Java 类方法与其线程安全
- java - 线程1打印1-10,当线程打印到5后,线程2打印“hello”,然后线程1继续打印
- Java程序员的成长之路
- Flex array数组到java Object[] 的转换方法
- java实现邮件发送准备工作(前期配置)
- java获取html
- java读取文件内容的方法总结
- Java 使用Runtime.getRuntime.exec 和 ProcessBuilder 调用外部命令
- Java BIO, NIO, AIO 总结
- 反编译Apk得到Java源代码
- 【Java部分源码分析之io篇】1.InputStream
- JAVA线程安全之synchronized关键字的正确用法
- JAVA日志系统
- java hashmap的put函数实现源码
- Java导出Excel合并单元格
- 在Java程序中设置代理
- Java连接HBase超时解决方法
- java基础知识点积累
- java.net.URISyntaxException的解决办法
- Java正则表达式详解