真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

關(guān)于Java序列化的問題有哪些-創(chuàng)新互聯(lián)

本篇內(nèi)容主要講解“關(guān)于Java序列化的問題有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“關(guān)于Java序列化的問題有哪些”吧!

創(chuàng)新互聯(lián)是一家以網(wǎng)絡(luò)技術(shù)公司,為中小企業(yè)提供網(wǎng)站維護、網(wǎng)站設(shè)計制作、網(wǎng)站建設(shè)、網(wǎng)站備案、服務(wù)器租用、域名申請、軟件開發(fā)、小程序設(shè)計等企業(yè)互聯(lián)網(wǎng)相關(guān)業(yè)務(wù),是一家有著豐富的互聯(lián)網(wǎng)運營推廣經(jīng)驗的科技公司,有著多年的網(wǎng)站建站經(jīng)驗,致力于幫助中小企業(yè)在互聯(lián)網(wǎng)讓打出自已的品牌和口碑,讓企業(yè)在互聯(lián)網(wǎng)上打開一個面向全國乃至全球的業(yè)務(wù)窗口:建站來電聯(lián)系:18980820575

問題一:什么是 Java 序列化?

序列化是把對象改成可以存到磁盤或通過網(wǎng)絡(luò)發(fā)送到其它運行中的 Java 虛擬機的二進制格式的過程,并可以通過反序列化恢復對象狀態(tài)。Java 序列化API給開發(fā)人員提供了一個標準機制:通過實現(xiàn) java.io.Serializable 或者 java.io.Externalizable 接口,ObjectInputStream 及ObjectOutputStream 處理對象序列化。實現(xiàn)java.io.Externalizable 接口的話,Java 程序員可自由選擇基于類結(jié)構(gòu)的標準序列化或是它們自定義的二進制格式,通常認為后者才是最佳實踐,因為序列化的二進制文件格式成為類輸出 API的一部分,可能破壞 Java 中私有和包可見的屬性的封裝。

序列化到底有什么用?

實現(xiàn) java.io.Serializable。

定義用戶類:

class User implements Serializable {
    private String username;
    private String passwd;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

我們把對象序列化,通過ObjectOutputStream存儲到txt文件中,再通過ObjectInputStream讀取txt文件,反序列化成User對象。

public class TestSerialize {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername("hengheng");
        user.setPasswd("123456");
        System.out.println("read before Serializable: ");
        System.out.println("username: " + user.getUsername());
        System.err.println("password: " + user.getPasswd());
        try {
            ObjectOutputStream os = new ObjectOutputStream(
                    new FileOutputStream("/Users/admin/Desktop/test/user.txt"));
            os.writeObject(user); // 將User對象寫進文件
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream(
                    "/Users/admin/Desktop/test/user.txt"));
            user = (User) is.readObject(); // 從流中讀取User的數(shù)據(jù)
            is.close();
            System.out.println("\nread after Serializable: ");
            System.out.println("username: " + user.getUsername());
            System.err.println("password: " + user.getPasswd());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

運行結(jié)果如下:

序列化前數(shù)據(jù): 
username: hengheng
password: 123456
序列化后數(shù)據(jù): 
username: hengheng
password: 123456

到這里,我們大概知道了什么是序列化。

問題二:序列化時,你希望某些成員不要序列化,該如何實現(xiàn)?

答案:聲明該成員為靜態(tài)或瞬態(tài),在 Java 序列化過程中則不會被序列化。

  • 靜態(tài)變量:加static關(guān)鍵字。

  • 瞬態(tài)變量: 加transient關(guān)鍵字。

我們先嘗試把變量聲明為瞬態(tài)。

class User implements Serializable {
    private String username;
    private transient String passwd;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

在密碼字段前加上了transient關(guān)鍵字再運行。運行結(jié)果:

序列化前數(shù)據(jù): 
username: hengheng
password: 123456
序列化后數(shù)據(jù): 
username: hengheng
password: null

通過運行結(jié)果發(fā)現(xiàn)密碼沒有被序列化,達到了我們的目的。

再嘗試在用戶名前加static關(guān)鍵字。

class User implements Serializable {
    private static String username;
    private transient String passwd;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

運行結(jié)果:

序列化前數(shù)據(jù): 
username: hengheng
password: 123456
序列化后數(shù)據(jù): 
username: hengheng
password: null

我們發(fā)現(xiàn)運行后的結(jié)果和預期的不一樣,按理說username也應(yīng)該變?yōu)閚ull才對。是什么原因呢?

原因是:反序列化后類中static型變量username的值為當前JVM中對應(yīng)的靜態(tài)變量的值,而不是反序列化得出的。

我們來證明一下:

public class TestSerialize {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername("hengheng");
        user.setPasswd("123456");
        System.out.println("序列化前數(shù)據(jù): ");
        System.out.println("username: " + user.getUsername());
        System.err.println("password: " + user.getPasswd());
        try {
            ObjectOutputStream os = new ObjectOutputStream(
                    new FileOutputStream("/Users/admin/Desktop/test/user.txt"));
            os.writeObject(user); // 將User對象寫進文件
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        User.username = "小明";
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream(
                    "/Users/admin/Desktop/test/user.txt"));
            user = (User) is.readObject(); // 從流中讀取User的數(shù)據(jù)
            is.close();
            System.out.println("\n序列化后數(shù)據(jù): ");
            System.out.println("username: " + user.getUsername());
            System.err.println("password: " + user.getPasswd());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class User implements Serializable {
    public static String username;
    private transient String passwd;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

在反序列化前把靜態(tài)變量username的值改為『小明』。

User.username = "小明";

再運行一次:

序列化前數(shù)據(jù): 
username: hengheng
password: 123456
序列化后數(shù)據(jù): 
username: 小明
password: null

果然,這里的username是JVM中靜態(tài)變量的值,并不是反序列化得到的值。

問題三:serialVersionUID有什么用?

我們經(jīng)常會在類中自定義一個serialVersionUID:

private static final long serialVersionUID = 8294180014912103005L

這個serialVersionUID有什么用呢?如果不設(shè)置的話會有什么后果?

serialVersionUID 是一個 private static final long 型 ID,當它被印在對象上時,它通常是對象的哈希碼。serialVersionUID可以自己定義,也可以自己去生成。

不指定 serialVersionUID的后果是:當你添加或修改類中的任何字段時,已序列化類將無法恢復,因為新類和舊序列化對象生成的 serialVersionUID 將有所不同。Java 序列化的過程是依賴于正確的序列化對象恢復狀態(tài)的,并在序列化對象序列版本不匹配的情況下引發(fā) java.io.InvalidClassException 無效類異常。

舉個例子大家就明白了:

我們保持之前保存的序列化文件不變,然后修改User類。

class User implements Serializable {
    public static String username;
    private transient String passwd;
    private String age;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
}

加了一個屬性age,然后單另寫一個反序列化的方法:

public static void main(String[] args) {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream(
                    "/Users/admin/Desktop/test/user.txt"));
            User user = (User) is.readObject(); // 從流中讀取User的數(shù)據(jù)
            is.close();
            System.out.println("\n修改User類之后的數(shù)據(jù): ");
            System.out.println("username: " + user.getUsername());
            System.err.println("password: " + user.getPasswd());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

關(guān)于Java序列化的問題有哪些

報錯了,我們發(fā)現(xiàn)之前的User類生成的serialVersionUID和修改后的serialVersionUID不一樣(因為是通過對象的哈希碼生成的),導致了InvalidClassException異常。

自定義serialVersionUID:

class User implements Serializable {
    private static final long serialVersionUID = 4348344328769804325L;
    public static String username;
    private transient String passwd;
    private String age;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
}

再試一下:

序列化前數(shù)據(jù): 
username: hengheng
password: 123456
序列化后數(shù)據(jù): 
username: 小明
password: null

運行結(jié)果無報錯,所以一般都要自定義serialVersionUID。

問題四:是否可以自定義序列化過程?

答案當然是可以的。

之前我們介紹了序列化的第二種方式:

實現(xiàn)Externalizable接口,然后重寫writeExternal() 和readExternal()方法,這樣就可以自定義序列化。

比如我們嘗試把變量設(shè)為瞬態(tài)。

public class ExternalizableTest implements Externalizable {
    private transient String content = "我是被transient修飾的變量哦";
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(content);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        content = (String) in.readObject();
    }
    public static void main(String[] args) throws Exception {
        ExternalizableTest et = new ExternalizableTest();
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
                new File("test")));
        out.writeObject(et);
        ObjectInput in = new ObjectInputStream(new FileInputStream(new File(
                "test")));
        et = (ExternalizableTest) in.readObject();
        System.out.println(et.content);
        out.close();
        in.close();
    }
}

運行結(jié)果:

我是被transient修飾的變量哦

這里實現(xiàn)的是Externalizable接口,則沒有任何東西可以自動序列化,需要在writeExternal方法中進行手工指定所要序列化的變量,這與是否被transient修飾無關(guān)。

到此,相信大家對“關(guān)于Java序列化的問題有哪些”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!


分享文章:關(guān)于Java序列化的問題有哪些-創(chuàng)新互聯(lián)
本文地址:http://weahome.cn/article/dghjej.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部