Java中Cloneable介面

來源:互聯網
上載者:User

 

目錄

預備知識
為什麼要clone
Object的clone以及為什麼如此實現
如何clone
對clone的態度
其他的選擇
和Serializable的比較
效能

預備知識

為了理解java的clone,有必要先溫習以下的知識。
java的類型,java的類型分為兩大類,一類為primitive,如int,另一類為參考型別,如String,Object等等。
java參考型別的儲存,java的參考型別都是儲存在堆上的。Java代碼

    public class B { int a; String b; public B(int a, String b) { super(); this.a = a; this.b = b; } }

       public class B {
        int a;
        String b;

        public B(int a, String b) {
            super();
            this.a = a;
            this.b = b;
        }
    }
    對這樣一個參考型別的執行個體,我們可以推測,在堆上它的記憶體儲存形式(除去指向class的引用,鎖的管理等等內務事務所佔記憶體),應該有一個int值表示a,以及一個引用,該引用指向b在堆上的儲存空間。

    為什麼要clone

    恩,因為需要。廢話。
    有名的GoF設計模式裡有一個模式為原型模式,用原型執行個體指定建立對象的種類,並且通過拷貝這些原型建立新的對象.
    簡單的說就是clone一個對象執行個體。使得clone出來的copy和原有的對象一模一樣。

    插一個簡單使用clone的例子,如果一個對象內部有可變對象執行個體的話,public API不應該直接返回該對象的引用,以防調用方的code改變該對象的內部狀態。這個時候可以返回該對象的clone。

    問題來了,什麼叫一模一樣。
    一般來說,有
    x.clone() != x
    x.clone().getClass() == x.getClass()
    x.clone().equals(x)
    但是這些都不是強制的。
    我們需要什麼樣的clone就搞出什麼樣的clone好了。
    一般而言,我們要的clone應該是這樣的。copy和原型的內容一樣,但是又是彼此隔離的。即在clone之後,改變其中一個不影響另外一個。

    Object的clone以及為什麼如此實現

    Object的clone的行為是最簡單的。以堆上的記憶體儲存解釋的話(不計內務記憶體),對一個對象a的clone就是在堆上分配一個和a在堆上所佔儲存空間一樣大的一塊地方,然後把a的堆上記憶體的內容複寫到這個新分配的記憶體空間上。
    看例子。Java代碼

    class User {
        String name;
        int age;
    }

    class Account implements Cloneable {
        User user;
        long balance;

        @Override
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

    Java代碼

       // user. User user = new User(); user.name = "user"; user.age = 20; // account. Account account = new Account(); account.user = user; account.balance = 10000; // copy. Account copy = (Account) account.clone(); // balance因為是primitive,所以copy和原型是相等且獨立的。 Assert.assertEquals(copy.balance, account.balance); copy.balance = 20000; // 改變copy不影響原型。 Assert.assertTrue(copy.balance != account.balance); // user因為是參考型別,所以copy和原型的引用是同一的。 Assert.assertTrue(copy.user == account.user); copy.user.name = "newName"; // 改變的是同一個東西。 Assert.assertEquals("newName", account.user.name);

      //user
       User user = new User(); 
       user.name = "user"; 
       user.age = 20;
       // account. 
       Account account = new Account(); 
       account.user = user; 
       account.balance = 10000; 
       // copy. 
       Account copy = (Account) account.clone(); 
        
       // balance因為是primitive,所以copy和原型是相等且獨立的。 
       Assert.assertEquals(copy.balance,account.balance); 
       copy.balance =20000;
      // 改變copy不影響原型。 
       Assert.assertTrue(copy.balance != account.balance); 
        
       // user因為是參考型別,所以copy和原型的引用是同一的。 
       Assert.assertTrue(copy.user == account.user); 
       copy.user.name = "newName"; 
       // 改變的是同一個東西。 
       Assert.assertEquals("newName", account.user.name); 

      恩,預設實現是幫了我們一些忙,但是不是全部。
      primitive的確做到了相等且隔離。
      參考型別僅僅是複製了一下引用,copy和原型引用的東西是一樣的。
      這個就是所謂的淺copy了。
      要實現深copy,即複製原型中對象的記憶體copy,而不僅僅是一個引用。只有自己動手了。
      等等,是不是所有的參考型別都需要深copy呢?
      不是!
      我們之所以要深copy,是因為預設的實現提供的淺copy不是隔離的,換言之,改變copy的東西,會影響到原型的內部。比如例子中,改變copy的user的name,影響了原型。
      如果我們要copy的類是不可變的呢,如String,沒有方法可以改變它的內部狀態呢。Java代碼

        class User implements Cloneable { String name; int age; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }

          class User implements Cloneable { 
                String name; 
                int age; 
             
                @Override 
                public Object clone() throws CloneNotSupportedException { 
                    return super.clone(); 
                } 
            } Java代碼

        // user.  
        User user = new User();  
        user.name = "user";  
        user.age = 20;  
           
         // copy 
         User copy = (User) user.clone(); 
          
        // age因為是primitive,所以copy和原型是相等且獨立的。 
         Assert.assertEquals(copy.age, user.age); 
         copy.age = 30; 
         // 改變copy不影響原型。 
         Assert.assertTrue(copy.age != user.age); 
          
         // name因為是參考型別,所以copy和原型的引用是同一的。 
         Assert.assertTrue(copy.name == user.name); 
         // String為不可變類。沒有辦法可以通過對copy.name的字串的操作改變這個字串。 
         // 改變引用新的對象不會影響原型。 
         copy.name = "newname"; 
         Assert.assertEquals("newname", copy.name); 
         Assert.assertEquals("user", user.name); 
         

         // user. User user = new User(); user.name = "user"; user.age = 20; // copy User copy = (User) user.clone(); // age因為是primitive,所以copy和原型是相等且獨立的。 Assert.assertEquals(copy.age, user.age); copy.age = 30; // 改變copy不影響原型。 Assert.assertTrue(copy.age != user.age); // name因為是參考型別,所以copy和原型的引用是同一的。 Assert.assertTrue(copy.name == user.name); // String為不可變類。沒有辦法可以通過對copy.name的字串的操作改變這個字串。 // 改變引用新的對象不會影響原型。 copy.name = "newname"; Assert.assertEquals("newname", copy.name); Assert.assertEquals("user", user.name);

        可見,在考慮clone時,primitive和不可變物件類型是可以同等對待的。

        java為什麼如此實現clone呢?
        也許有以下考慮。
        1 效率和簡單性,簡單的copy一個對象在堆上的的記憶體比遍曆一個對象網然後記憶體深copy明顯效率高並且簡單。
        2 不給別的類強加意義。如果A實現了Cloneable,同時有一個引用指向B,如果直接複製記憶體進行深copy的話,意味著B在意義上也是支援Clone的,但是這個是在使用B的A中做的,B甚至都不知道。破壞了B原有的介面。
        3 有可能破壞語義。如果A實現了Cloneable,同時有一個引用指向B,該B實現為單例模式,如果直接複製記憶體進行深copy的話,破壞了B的單例模式。
        4 方便且更靈活,如果A引用一個不可變對象,則記憶體deep copy是一種浪費。Shadow copy給了程式員更好的靈活性。

        如何clone
        clone三部曲。
        1 聲明實現Cloneable介面。
        2 調用super.clone拿到一個對象,如果父類的clone實現沒有問題的話,在該對象的記憶體儲存中,所有父類定義的field都已經clone好了,該類中的primitive和不可變類型引用也複製好了,可變類型引用都是淺copy。
        3 把淺copy的引用指向原型對象新的複製體。
        給個例子。Java代碼

        class User implements Cloneable {
                    String name;
                    int age;

                    @Override
                    public User clone() throws CloneNotSupportedException {
                            return (User) super.clone();
                    }
            }

            class Account implements Cloneable {
                    User user;
                    long balance;

                    @Override
                 

        相關文章

        聯繫我們

        該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

        如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

        A Free Trial That Lets You Build Big!

        Start building with 50+ products and up to 12 months usage for Elastic Compute Service

        • Sales Support

          1 on 1 presale consultation

        • After-Sales Support

          24/7 Technical Support 6 Free Tickets per Quarter Faster Response

        • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.