将 对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象 将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象,也就是反序列化。
序列化也叫串行化,譬如我们把对象写到流里,把对象转化为json、xml等都是序列化。这里我们主要讲把对象转为二进制流。
Java序列化实现
Java中使用 ObjectInputStream 和 ObjectOutputStream 对实现了Serializable接口的对象的进行序列化和反序列化过程。一个类实现了Serializable接口就表示可以被序列化。Serializable接口没有方法,可以看作是一个标记接口。序列化示例代码:
//序列化方法
public static void serial(Object obj)throws IOException
{
FileOutputStream fileOutputStream=null;
ObjectOutputStream objOutputStream=null;
try
{
fileOutputStream=new FileOutputStream("d:\\s.tmp");
objOutputStream=new ObjectOutputStream(fileOutputStream);
objOutputStream.writeObject(obj);
} catch (IOException e)
{
throw e;
}finally{
if(objOutputStream!=null)objOutputStream.close();
if(fileOutputStream!=null)fileOutputStream.close();
}
}
//反序列化方法
public static <T> T deserial(File file)throws IOException,ClassNotFoundException
{
FileInputStream fileInputStream=null;
ObjectInputStream objInputStream=null;
try
{
fileInputStream=new FileInputStream(file);
objInputStream=new ObjectInputStream(fileInputStream);
return (T)objInputStream.readObject();
} catch (IOException e)
{
throw e;
} catch (ClassNotFoundException e)
{
throw e;
}finally{
if(objInputStream!=null)objInputStream.close();
if(fileInputStream!=null)fileInputStream.close();
}
}
//调用方法
public static void main(String[] args)
{
Shop tmpShop=new Shop();
tmpShop.setShopName("商店名");
tmpShop.setShopNum("001");
tmpShop.setShopAddress("北京路88号");
try
{
serial(tmpShop);
Shop newShop=deserial(new File("D:\\s.tmp"));
System.out.println(newShop.getShopName());//反序列化后比较两个对象
System.out.println(newShop.equals(tmpShop));//结果false
System.out.println(newShop==tmpShop); //结果false
} catch (IOException e)
{
e.printStackTrace();
} catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
Java序列化有以下几个特点:
1. 只有实现了 Serializable接口或 Externalizable接口的类才能被序列化, Externalizable继承自 Serializable,可以实现序列化过程的逻辑自定义;
2. 静态变量不会被序列化;
3. Transient修饰的变量不会被序列化;
4. 如果父类没有实现Serializable接口,父类相关内容将不会被序列化,也就意谓着反序列化时父类的变量值将丢失。
5. Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用
可序列化类的版本
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量 private static final long serialVersionUID,默认serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。当版本标识不一样时,即时类的代码完全一样反序列化也会失败。
如果不在代码中显式定义这个值,则Java会提供默认实现,对类的源代码作了修改再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化,而不同虚拟机的实现也有可能是不同的,所以建议显式的定义该值。
对象的克隆
对象的克隆也叫复制,分为浅复制和深复制。浅复制复制出来的对象的所有变量与原对象相同,而所有对其他对象的引用仍然指向原对象。而深复制自然是连引用对象也复制了一份出来了。
Java中浅复制实现就简单,Object类提供了Clone()方法,代码如下:
public class Entity implements Cloneable
{
private String name;
private String age;
private Shop shop;
public Object clone()
{
try
{
return super.clone();
} catch (CloneNotSupportedException e)
{
return null;
}
}
}
Cloneable接口是个标记类,告诉java虚拟机该类可以使用安全使用clone方法,如果类未实现Cloneable接口调用clone方法就会抛出CloneNotSupportedException异常。
那么深复制是如何实现的呢?这里就要用到前面提到的序列化了。思路就是序列化一个对象到临时内存中,然后再反序列化生成一个新对象。
public class Entity implements Cloneable,Serializable
{
private String name;
private int age;
private Shop shop;//一个引用类型成员变量
public Shop getShop()
{
return shop;
}
public void setShop(Shop shop)
{
this.shop = shop;
}
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;
}
//浅复制
public Object clone()
{
try
{
return super.clone();
} catch (CloneNotSupportedException e)
{
return null;
}
}
//序列化实现深复制
public Object deepClone()throws IOException, ClassNotFoundException
{
//序列化本身
ByteArrayOutputStream byteOutStream=new ByteArrayOutputStream();
ObjectOutputStream objOutStream=new ObjectOutputStream(byteOutStream);
objOutStream.writeObject(this);
//反序列化生成新对象
ByteArrayInputStream byteInStream=new ByteArrayInputStream(byteOutStream.toByteArray());
ObjectInputStream objInStream=new ObjectInputStream(byteInStream);
return objInStream.readObject();
}
public static void main(String[] args)throws Exception
{
Entity e1=new Entity();
e1.setName("charles");
e1.setAge(18);
e1.setShop(new Shop("001","shop1"));
Entity e2=(Entity)e1.clone();
Entity e3=(Entity)e1.deepClone();
System.out.println(e1.getShop().equals(e2.getShop()));//结果输出true
System.out.println(e1.getShop().equals(e3.getShop()));//结果输出false
}
}