美文网首页小卜java
JAVA基础之序列化与反序列化

JAVA基础之序列化与反序列化

作者: 汤太咸啊 | 来源:发表于2021-11-11 20:09 被阅读0次

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

    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);
    
    

    相关文章

      网友评论

        本文标题:JAVA基础之序列化与反序列化

        本文链接:https://www.haomeiwen.com/subject/tfmgzltx.html