JAVA基础之序列化

今天来讲讲序列化以及反序列化 Serializable接口,没有method,实现该接口表明可以序列化以及反序列化
ObjectOutputStream,将Object序列化为stream流
ObjectInputStream,通过stream流反序列化转化为Object
序列化时,计算出一个Serial version unique identifier标识这个class(安全的哈希值),并将这个哈希值其存储到stream中,而反序列化时java再次计算这个class的标识值(如果class类的定义已改变则值也会发生变化),新计算的值与从stream取出来的值做比较,如果不一致,表示该类的序列化版本与我们拥有的当前类定义不兼容。会产生InvalidClassException。

public class BankAccount implements Serializable { private String id; private int balance = 0; public BankAccount(String id){ this.id = id; } public BankAccount(String id, int startBalance){ this.id = id; balance = startBalance; } public String getId(){ return id; } public synchronized int getBalance(){ return balance; } public synchronized void deposit(int amount){ balance += amount; } public synchronized void withdrawal(int amount){ balance -= amount; } }

序列化与反序列化方法
public class Main { public static void main(String[] args) { BankAccount ba = new BankAccount("10",500); ba.deposit(250); saveAccount(ba,"/Users/buxuesong/Documents/svn_code/demo/account.dat"); BankAccount bb = loadAccount("/Users/buxuesong/Documents/svn_code/demo/account.dat"); System.out.println(bb.getId() +" | " + bb.getBalance()); } private static void saveAccount(BankAccount ba, String fileName){ try(ObjectOutputStream os = new ObjectOutputStream(Files.newOutputStream(Paths.get(fileName)))){ os.writeObject(ba); }catch(Exception e){ System.out.println(e.getSuppressed() +" | "+ e.getMessage()); } } private static BankAccount loadAccount(String fileName){ BankAccount ba = null; try(ObjectInputStream oi = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)))){ ba = (BankAccount) oi.readObject(); }catch(Exception e){ System.out.println(e.getSuppressed() +" | "+ e.getMessage()); } return ba; } } 输出 10 | 750

当改变了class的定义字段后,与原有序列化的唯一标识不同,导致无法反序列化,这就需要自定义serialVersionUID,以后修改了该类,也会可以反序列化回来,只是新增的字段默认值为空或者是原始默认值(int的话就是0)
private static final long serialVersionUID = -23324324324L;

【JAVA基础之序列化】还可以自定义序列化以及反序列化的方法,在BankAccount中
private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); }private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{ ObjectInputStream.GetField fields = in.readFields(); id = (String) fields.get("id",null); balance = fields.get("balance",0); lastTxType = fields.get("lastTxType",'u'); lastTxAmount = fields.get("lastTxAmount",-1); }

执行方法
BankAccount bb = loadAccount("/Users/buxuesong/Documents/svn_code/demo/account.dat"); System.out.println(bb.getId() +" | " + bb.getBalance()+" | " +bb.getLastTxType()+" | "+bb.getLastTxAmount()); 输出 10 | 750 | u | -1

还可以针对某些字段不必须做序列化处理,需要通过transient修饰该字段,可以通过其它方式获取回来,这样节省了序列化的时间
具体如下
public class AccountGroup implements Serializable { private static final long serialVersionUID = 106962907155393149L; private Map accountMap = new HashMap(); private transient int totalBalance; public int getTotalBalance(){ return totalBalance; } public void addAccount(BankAccount account){ totalBalance += account.getBalance(); accountMap.put(account.getId(), account); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); for(BankAccount account: accountMap.values()) totalBalance += account.getBalance(); } }

执行方法中的存储到流以及从流中取回的方法
private static void saveGroup(AccountGroup g, String fileName){ try(ObjectOutputStream os = new ObjectOutputStream(Files.newOutputStream(Paths.get(fileName)))){ os.writeObject(g); }catch(Exception e){ System.out.println(e.getSuppressed() +" | "+ e.getMessage()); } }private static AccountGroup loadGroup(String fileName){ AccountGroup g = null; try(ObjectInputStream oi = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)))){ g = (AccountGroup) oi.readObject(); }catch(Exception e){ System.out.println(e.getSuppressed() +" | "+ e.getMessage()); } return g; }

执行方法:
BankAccount acct1 = new BankAccount("1234", 500); BankAccount acct2 = new BankAccount("9866", 750); AccountGroup group = new AccountGroup(); group.addAccount(acct1); group.addAccount(acct2); saveGroup(group, "/Users/buxuesong/Documents/svn_code/demo/group.dat"); AccountGroup group2 = loadGroup("/Users/buxuesong/Documents/svn_code/demo/group.dat"); System.out.println("group2.getTotalBalance:"+group2.getTotalBalance());

输出
group2.getTotalBalance:1250

其他序列化方法,实现Externalizable接口
void writeExternal(ObjectOutput out); void readExternal(ObjectInput in);

    推荐阅读