2

JAVA基础之序列化

 2 years ago
source link: https://segmentfault.com/a/1190000040941021
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JAVA基础之序列化

发布于 34 分钟前

今天来讲讲序列化以及反序列化

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;

还可以自定义序列化以及反序列化的方法,在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<String, BankAccount> 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);

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK