一、Java对象的存储
首先我们先来理解一下Java对象在内存中的存储!
JVM的内存分为三个部分:栈(stack)、堆栈(heap)、方法区(method area):
栈:主要存储基本数据类型变量、方法的形参、引用数据类型等;
堆栈:存储实例对象;
方法区:存储静态变量(静态常量池)、class信息等
如:当new出来一个对象后,对象的引用在栈中分配,指向堆中的实例对象;当局部对象变量使用完后,引用立即从栈中回收,堆中的对象等待GC回收;
再如:数组,在栈中分配数据名,堆中保存数据的大小和实例元素。
因此,java程序中的对象实例默认都是直接存储在内存中,一旦内存中被回收或意外丢失,将无法重构;
这就涉及到了java对象的序列化!
二:什么是对象序列化
在JavaBean的文章中曾提到,serializable的目的是类属性的持久化存储。比如需要开发MySQL的存储,那么将一条记录写入MySQL中的时候就需要实现Serializable接口。
那么到底什么是对象序列化呢?
对象序列化就是将对象实例的状态(是属性,不包括方法)保存到文件或者其它数据库,并且可以通过反序列化的方式对对象进行重构。
(对象序列化将内存中的变量实例(如JavaBean中的User bean)转换为二进制流,更加高效地在网络或存储介质上进行传输)
三:序列化示例
假设有如下类LoginUser,定义有username和password两个属性,main函数中创建了一个LoginUser对象,username值为50,password值为30;
然后将当前LoginUser对象的属性值保存到文件foo.ser中。
1 import java.io.*; 2 3 public class LoginUser { 4 private String username; 5 private String passwd; 6 public String getUsername() { 7 return username; 8 } 9 public void setUsername(String username) { 10 this.username = username; 11 } 12 public String getPasswd() { 13 return passwd; 14 } 15 public void setPasswd(String passwd) { 16 this.passwd = passwd; 17 } 18 19 20 public static void main(String[] args){ 21 LoginUser loginUser = new LoginUser(); 22 loginUser.setUsername("50"); 23 loginUser.setPasswd("30"); 24 25 try{ 26 FileOutputStream fs = new FileOutputStream("foo.ser"); 27 ObjectOutputStream os = new ObjectOutputStream(fs); 28 os.writeObject(loginUser); 29 os.close(); 30 }catch(Exception ex){ 31 ex.printStackTrace(); 32 } 33 } 34 35 }
此时,运行该代码会出现如下异常:
1 java.io.NotSerializableException: com.longshine.zwp.test.LoginUser 2 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1081) 3 at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:302) 4 at com.longshine.zwp.test.LoginUser.main(LoginUser.java:29)
提示找不到对应的序列化类型,需要对LoginUser类定义做出如下修改:
1 public class LoginUser implements Serializable{ 2 3 }
四、序列化注意事项
a)序列化时,只对对象的状态进行保存,而不管对象的方法;
b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
d)静态变量不能做序列化;
e)并不是所有的对象都能做序列化,比如Thread、socket等不能被序列化。