JavaインスタンスオブジェクトのCloneおよびequalsメソッドの書き換え

22540 ワード

インスタンスオブジェクトのclone
8つの基本データ型cloneでは簡単です.
int m=10;
int n=m;

これにより、基本データ型のcloneを実現できますが、上記の操作オブジェクトは、2つの変数が同じオブジェクトを指しているだけなので、いずれの変数でもオブジェクトを修正することで、他方が気づくことはできません.一方、あるインスタンスオブジェクトに対してCloneを行い、その状態を保存する必要がある場合は、オブジェクトのCloneを実現するために専門的な操作が必要になります.これにより、Clone以降、既存のインスタンスオブジェクトを修正すると、Cloneのオブジェクトに影響を与えないため、その時の状態をCloneのオブジェクトで保存することができます.主に2つの方法があります:』Cloneableインタフェースを継承し、Objectのclone()メソッドを再書き込みします.Serializableインタフェースを継承して直列化可能を表します.同時に、オブジェクトの直列化と逆シーケンス化によってclone書き換え方法を実現します.
/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;

-浅いクローン
public class ShallowClone implements Cloneable {
    String name=" clone !";

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    protected Object clone(){
        // TODO Auto-generated method stub
        ShallowClone SC=null;
        try {
            SC=(ShallowClone)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
        return SC;
    }
public static void main(String[] args) {
    ShallowClone SC=new ShallowClone();
    System.out.println("-----------  -----------");
    System.out.println(SC.getName());
    ShallowClone SClone=(ShallowClone)SC.clone();
    System.out.println("-----------   -----------");
    System.out.println(SClone.getName());
    System.out.println("------         ---------");
    System.out.println("  :"+SC.getName());
    SClone.setName("  Clone !");
    System.out.println("   :"+SClone.getName());
    System.out.println("----------           ------------");
    System.out.println(SC==SClone);
    System.out.println("------------             -------------");
    System.out.println(SC.getClass()==SClone.getClass());
    System.out.println();

}
}
    :
-----------  -----------
 clone !
-----------   -----------
 clone !
------         ---------
  : clone !
   :  Clone !
----------           ------------
false
------------             -------------
true

以上から分かるように、SCloneはオブジェクトSCのcloneであり、クローンオブジェクトは元のオブジェクトの値と同じであり、後にSCloneのフィールドname値を変更し、後に出力するとSCloneオブジェクトnameのみが変更されていることがわかりますが、SCの場合は変更されていません.両者が同じオブジェクトを指しているわけではありません.後述の判断では、==で2つのオブジェクトのメモリアドレスが等しいかどうかを判断し、falseは同じオブジェクトではないことを示した.SC.getClass()=SClone.getClass()は2つのクラスタイプが等しいかどうかを判断し、は等しいことを示します(ドキュメントでは強制されていません).(もちろんequals判断はfalseに違いありませんが、具体的には最後を参照してください)実は、このcloneにはShallowCloneフィールド値はjavaの基本的なデータ型なので異常はありませんが、クラスのオブジェクトのフィールド属性値にもオブジェクトがある場合は、この処理方法で異常が発生します.クラスの他の基本的なタイプのフィールド値はすべてcloneできますが、内部フィールドのオブジェクト属性は参照を付与するだけで、cloneは実現されません.cloneオブジェクトによって内部オブジェクト属性の状態を変更する場合、オリジナルのオブジェクトオブジェクトに対応する内部オブジェクトのプロパティも変更されます(両方のオブジェクトのフィールドオブジェクトは同じオブジェクトを指し、フィールドオブジェクトはcloneを実装していません)-深さclone上記の問題を解決するためには、深さcloneが必要です.
//      
public class AddClass implements Cloneable {
    int m;

    public int getM() {
        return m;
    }

    public void setM(int m) {
        this.m = m;
    }

    @Override
    protected Object clone() {
        // TODO Auto-generated method stub
        AddClass  ac=null;
        try {
            ac=(AddClass) super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return ac;
    }


}
public class DeepClone implements Cloneable {
    AddClass addClass;
    int number;
    public AddClass getAddClass() {
        return addClass;
    }
    public void setAddClass(AddClass addClass) {
        this.addClass = addClass;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    @Override
    protected Object clone() {
        // TODO Auto-generated method stub
        DeepClone dc=null;
        try {
            dc=(DeepClone) super.clone();
            dc.addClass=(AddClass) addClass.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return dc;
    }
   public static void main(String[] args) {
    DeepClone dc=new DeepClone();
    AddClass ac= new AddClass();
    dc.setAddClass(ac);
    dc.setNumber(0);
    ac.setM(0);
    System.out.println("  :number="+dc.getNumber()+" dc.addClass.m="+dc.getAddClass().getM());
    DeepClone dclone=(DeepClone) dc.clone();
    System.out.println("   :number="+dclone.getNumber()+" dc.addClass.m="+dclone.getAddClass().getM());
    System.out.println("-----------------------   -----------------------");
    ac.setM(2);
    dclone.setNumber(1);
    System.out.println("  :number="+dc.getNumber()+" dc.addClass.m="+dc.getAddClass().getM());
    System.out.println("   :number="+dclone.getNumber()+" dc.addClass.m="+dclone.getAddClass().getM());
} 
}

    :
  :number=0 dc.addClass.m=0
   :number=0 dc.addClass.m=0
-----------------------   -----------------------
  :number=0 dc.addClass.m=2
   :number=1 dc.addClass.m=0

深さcloneを実装するには、フィールドオブジェクトクラスがcloneableインタフェースを継承し、cloneメソッドを書き換え、外部オブジェクトもcloneableインタフェースを継承してcloneメソッドを実装しますが、このcloneメソッドはフィールドオブジェクトのcloneメソッドを手動で呼び出す必要があります.これにより、深さcloneを実装できます.出力結果から,外部クラスオブジェクトと内部フィールドオブジェクトの両方がcloneを実現し,オリジナルのフィールドオブジェクトを修正してもcloneのオブジェクトに影響を及ぼさないことが分かった.しかし,このように書くとcloneが実現できるが,オブジェクトがフィールドオブジェクトをネストすると書く必要が多くなるため,この方法は適切ではない.別の方法が必要です.Serializableインタフェースを継承し、Serializableインタフェースを継承し、オブジェクトのシーケンス化と逆シーケンス化によって深さcloneを実現します.
public class Other implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -3465331114779243637L;
    String  value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}

public class SDeepClone implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -5427637747572757487L;
    int number;
    Other other;
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public Other getOther() {
        return other;
    }
    public void setOther(Other other) {
        this.other = other;
    }
    public SDeepClone myClone(){
        SDeepClone sdc=null;
        try {
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream  bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);
            sdc=(SDeepClone) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return sdc;
    }
    public static void main(String[] args) {
        SDeepClone sdc=new SDeepClone();
        Other other=new Other();
        other.setValue(" ");
        sdc.setOther(other);
        sdc.setNumber(0);
        SDeepClone sdclone= sdc.myClone();
        System.out.println("---------------clone--------------------");
        System.out.println("  : number="+sdc.getNumber()+" sdc.other.value="+sdc.getOther().getValue());
        System.out.println("   : number="+sdclone.getNumber()+" sdc.other.value="+sdclone.getOther().getValue());
        other.setValue("  ");
        System.out.println("---------------   --------------------");
        System.out.println("  : number="+sdc.getNumber()+" sdc.other.value="+sdc.getOther().getValue());
        System.out.println("   : number="+sdclone.getNumber()+" sdc.other.value="+sdclone.getOther().getValue());

    }
}
    :
---------------clone--------------------
  : number=0 sdc.other.value= 
   : number=0 sdc.other.value= 
---------------   --------------------
  : number=0 sdc.other.value=  
   : number=0 sdc.other.value= 

フィールドオブジェクトのみがserializableインタフェースを実装し、プライマリクラスもserializableインタフェースを実装し、オブジェクトをシーケンス化および逆シーケンス化する方法を書くだけで深さcloneを実装できます.
ちなみにcloneオブジェクトである以上,2つのオブジェクトが等しいか否かを判断するには親objectで継承されているequalsメソッドを用いることはできないが,Objectのequalsで==を用いるとメモリアドレスとなるため,equalsメソッドとhashCodeメソッドを書き換えるにはなぜequalsメソッドを書き換えるにもhashCodeメソッドを書き換えるのか,主にHashSetやHashMap,HashTableなどはオブジェクトのhash値に基づいて使用されますが、自分でkeyとしてオブジェクトを決める場合はequalsメソッドとhashCodeメソッドを書き換えます.たとえば、HashMapでキーとしてカスタムオブジェクトが必要な場合は、keyによって保存されてもvalueを取得してもkeyのhash値を計算する必要があります.equalsメソッドを書き換えると、hashCodeを書き換える方法はありません(ObjectHashCodeメソッドは、オブジェクトのメモリアドレスに対してhash値の計算を行う)ため、2つのオブジェクトの値は等しいがhash値は等しくない.これにより、HashMapのput要素の場合、keyのhash値が計算されるという問題が発生する.その後、対応する配列の下付きを見つけ、ヘッダーに要素を挿入し、2つのオブジェクトが等しいがhashCode値が等しくない場合、第1個の要素オブジェクトをキーとしてhashMapに入れ、getで(Key)valueを取得した場合、オブジェクトは等しいがhash値が等しくないため、hash値を計算して下付き検索を求めるエラーの下付き文字が発生し、equalsメソッドを呼び出して比較しても対応するvalueが見つからない.同時にHashSetにも致命的な影響があり、hash値でオブジェクトオブジェクトを格納し、2つのオブジェクトが等しいがhash値が等しくない場合setに格納されるで、これはsetの初心と衝突し、重複する要素を格納できないことです.次に、上の最初のequalsメソッドとhashCodeメソッドを書き換えます.
public class ShallowClone implements Cloneable {
    String name=" clone !";

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    protected Object clone(){
        // TODO Auto-generated method stub
        ShallowClone SC=null;
        try {
            SC=(ShallowClone)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
        return SC;
    }

@Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return name.hashCode();
    }
    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        if(this==obj){
            return true;
        }
        if(obj==null){
            return false;
        }
        if(this.getClass()!=obj.getClass()){
            return false;
        }
        ShallowClone sc=(ShallowClone)obj;
        if(name==null){
           if(sc.name==null){
               return true; 
           }else{
               return false;
           }
        }
        if((name.equals(sc.name))){
            return true;
        }
        return false;
    }
public static void main(String[] args) {
    ShallowClone SC=new ShallowClone();
    System.out.println("-----------  -----------");
    System.out.println(SC.getName());
    ShallowClone SClone=(ShallowClone)SC.clone();
    System.out.println("-----------   -----------");
    System.out.println(SClone.getName());
    System.out.println("------         ---------");
    System.out.println("  :"+SC.getName());
    System.out.println("equals      :"+SClone.equals(SC));
    System.out.println("hash   :"+(SClone.hashCode()==SC.hashCode()));

}
}
    :
-----------  -----------
 clone !
-----------   -----------
 clone !
------         ---------
  : clone !
equals      :true
hash   :true

equalsメソッドとhashCodeメソッドを書き換えていない場合、最後の2つの比較がfalseとして表示されます.書き換えのequalsメソッドを重点的に見ることができるので,正しい使用を保証するためにequalsメソッドを書き換えるにはhashCodeメソッドを書き換える.