クローニング、私は誰もがそれを聞いたと信じています。世界初の羊であるドリーは、核移植技術を使用して、非常に魔法のような哺乳類の成人体細胞の新しい個人を栽培しています。実際、Javaのクローニングの概念、つまりオブジェクトのコピーを実現する概念もあります。
この記事では、Javaでのクローニングに関する詳細な質問を紹介しようとして、クローニングをよりよく理解するのに役立つことを望んでいます。
簡単な変数をコピーするとします。非常にシンプル:
int apples = 5; int pearls = Apples;
INTタイプだけでなく、他の7つのプリミティブデータ型(Boolean、Char、Byte、Short、Float、Double.Long)もこのタイプの状況に適用されます。
しかし、オブジェクトをコピーすると、状況は少し複雑です。
私が初心者であるとしましょう、私はこれを書くでしょう:
クラス学生{private int number; public int getNumber(){return number; } public void setnumber(int number){this.number = number; }} public class test {public static void main(string args []){student stu1 = new sustent(); stu1.setnumber(12345);学生STU2 = STU1; System.out.println( "学生1:" + stu1.getnumber()); system.out.println( "学生2:" + stu2.getnumber()); }}結果:
学生1:12345
学生2:12345
ここでは、学生クラスをカスタマイズしました。これには、数字フィールドが1つしかありません。
新しい学生インスタンスを作成し、STU2インスタンスに値を割り当てます。 (StudentStu2 = Stu1;)
印刷の結果を見てみましょう。初心者として、私は胸と腹部を軽くたたいたが、オブジェクトはこのようにコピーされた。
これは本当にそうですか?
STU2インスタンスの数字フィールドを変更してから、結果を印刷して、以下を確認しようとします。
stu2.setnumber(54321); System.out.println( "学生1:" + stu1.getnumber()); system.out.println( "学生2:" + stu2.getnumber());
結果:
学生1:54321
学生2:54321
これは奇妙です。学生2の学生数が変更され、学生数の学生数も変更されたのですか?
理由は文(Stu2 = Stu1)にあります。このステートメントの目的は、STU1の参照をSTU2に割り当てることです。
このようにして、STU1とSTU2はメモリヒープ内の同じオブジェクトを指します。写真に示されているように:
では、オブジェクトをコピーするにはどうすればよいですか?
すべての年齢のオブジェクトの王を覚えていますか? 11の方法があり、2つの保護された方法があり、そのうちの1つはクローン方法です。
Javaでは、すべてのクラスがデフォルトでJava言語パッケージのオブジェクトクラスから継承されます。そのソースコードを確認してください。 JDKディレクトリのsrc.zipを別の場所にコピーして解凍できます。すべてのソースコードが含まれています。アクセス予選で保護されたclone()を使用した方法があることがわかりました。
/*このオブジェクトのコピーを作成して返します。 「コピー」の正確な意味は、オブジェクトのクラスに依存する場合があります。一般的な意図は、オブジェクトxの場合、式では、式:1)x.clone()!= xはtrue2)x.clone()。 CloneNotsuportedexceptionを投げます。
よく見ると、それはまだネイティブの方法です。誰もが、ネイティブの方法が非java言語でコードが実装されており、Javaプログラムによる呼び出し用であることを誰もが知っています。 JAVAプログラムはJVM仮想マシンで実行されるため、基礎となるオペレーティングシステム関連のプログラムにアクセスする方法はなく、オペレーティングシステムに近い言語でのみ実装できます。
最初の宣言により、クローンされたオブジェクトに個別のメモリアドレスの割り当てがあることが保証されます。
2番目の宣言は、元のオブジェクトとクローン化されたオブジェクトが同じクラスタイプを持つ必要があることを示していますが、必須ではありません。
3番目のステートメントは、元のオブジェクトとクローン化されたオブジェクトがequals()メソッドで等しく使用されるべきであることを示していますが、必須ではありません。
各クラスの親クラスはオブジェクトであるため、それらにはすべてclone()メソッドが含まれていますが、メソッドが保護されているため、クラスの外部でアクセスすることはできません。
オブジェクトをコピーするには、クローンメソッドをオーバーライドする必要があります。
なぜクローン?
最初に質問について考えてみましょう、なぜオブジェクトをクローンする必要があるのですか?新しいオブジェクトだけで大丈夫ではありませんか?
答えは次のとおりです。クローンされたオブジェクトにはいくつかの変更されたプロパティが含まれている場合があり、新しいオブジェクトのプロパティは初期化時の値であるため、現在のオブジェクトの「状態」を保存するために新しいオブジェクトが必要な場合、クローン法はクローンメソッドに依存します。それで、このオブジェクトの一時的なプロパティを新しいオブジェクトに1つずつ割り当てても大丈夫ではありませんか?それは大丈夫ですが、最初にそれについて話さないでください。第二に、上記のソースコードを通して、誰もがクローンがネイティブ方法であり、これが速くて下部に実装されていることを発見しました。
私たちの共通オブジェクトa = new object();オブジェクトb; b = a;この形式のコードは、参照、つまりメモリ内のオブジェクトのアドレスをコピーし、AとBオブジェクトはまだ同じオブジェクトを指しています。
クローンメソッドを介して割り当てられたオブジェクトは、元のオブジェクトとは独立して存在します。
クローニングを実装する方法
最初に、浅瀬と深いクローニングの2つの異なるクローニング方法を紹介します。
Java言語では、データ型は値タイプ(基本データ型)と参照タイプに分割されます。値の種類には、INT、ダブル、バイト、ブール、チャーなど、参照タイプなどの単純なデータ型が含まれ、クラス、インターフェイス、配列などの複雑なタイプが含まれます。 2つは以下で詳細に紹介されます。
一般的なステップは(浅いクローニング)です。
1.コピーされたクラスは、クローン誘発性インターフェイスを実装する必要があります(実装しない場合、クローンメソッドを呼び出すときにクローンノッツサポートエクセプトがスローされます)。このインターフェイスはタグインターフェイスです(方法なし)
2。clone()メソッドをオーバーライドし、アクセス修飾子を公開に設定します。メソッドのsuper.clone()メソッドを呼び出して、必要なコピーオブジェクトを取得します。 (ネイティブはローカルな方法です)
以下は、上記の方法の変更です。
クラスの学生はcloneable {private int number; public int getnumber(){return number;} public void setnumber(int number){this.number = number;}@override public object clone(){student stu = null; try {stu =(sudent)super.clone();} catch(cloneNotsupportedexection e stu;}} public class test {public static void main(string args []){desute stu1 = new Student(); stu1.setnumber(12345); suste stu2 =(desute)stu1.clone(); system.out.println( "sudent1:" + stu1.getnumber()); Stu2.getNumber()); stu2.setNumber(54321); System.out.println( "sustem1:" + stu1.getnumber()); system.out.println( "student2:" + stu2.getnumber();}}}}結果:
学生1:12345
学生2:12345
学生1:12345
学生2:54321
これらの2つのオブジェクトが同じオブジェクトではないと思わない場合は、この文を見てみましょう。
System.out.println(stu1 == stu2); // 間違い
上記のコピーは浅いクローニングと呼ばれます。
もう少し複雑なディープコピーもあります:
学生クラスにアドレスクラスを追加しましょう。
クラスアドレス{private string add; public string getadd(){return add;} public void setAdd(string add){this.add = add;}}クラスの生徒はcloneable {private int number; private drasts addr; public addr;} public void setaddr(retrunt addr;} public int getnumber;} public addr;} public addr;} public addr;} public addr;} public addr;} setNumber(int number){this.number = number;}@override public object clone(){sustent stu = null; try {stu =(student)super.clone();} catch(clonenotsupportedexception {e.printstacktrace();} return stu;}} public class test {public sitecic void(] {] public static voiid( address(); addr.setadd( "hangzhou city"); desute stu1 = new Student(); stu1.setnumber(123); stu1.setaddr(addr); desute stu2 =(student)stu1.clone(); system.out.println( "desute 1:" + stu1.getnumber() " +" + " +" + " + stu1.getnumber stu1.getaddr()。getadd()); system.out.println( "desute 2:" + stu2.getnumber() + "、add:" + stu2.getaddr()。getadd();}}}}結果:
学生1:123、住所:杭州学生2:123、住所:杭州
一見、問題はありませんが、これは本当にそうですか?
メインメソッドのADDRインスタンスのアドレスを変更しようとします。
addr.setadd( "xihu地区"); System.out.println( "Student 1:" + Stu1.getNumber() + "、add:" + stu1.getaddr()。getadd()); System.out.println( "Student 2:" + Stu2.getNumber() + "、add:" + stu2.getaddr()。getadd());
結果:
学生1:123、住所:杭州学生2:123、住所:杭州学生1:123、住所:Xihu地区学生2:123、住所:Xihu地区
これは奇妙です、なぜ両方の学生の住所が変わったのですか?
その理由は、浅いコピーがADDR変数の参照のみをコピーし、別のスペースを実際に開いていないためです。値をコピーした後、新しいオブジェクトへの参照を返します。
したがって、真のコピーオブジェクトを実現するために、純粋に参照コピーではありません。アドレスクラスをコピーして、クローンメソッドを変更する必要があります。完全なコードは次のとおりです。
パッケージABC;クラスアドレスはcloneable {private string add; public string getadd(){return add; } public void setAdd(string add){this.add = add; } @Override public Object clone(){アドレスaddr = null; try {addr =(address)super.clone(); } catch(clonenotsupportedexception e){e.printstacktrace(); } return addr; }}クラスの学生はcloneable {private int number;プライベートアドレスADDR;パブリックアドレスgetAddr(){return addr; } public void setAddr(アドレスaddr){this.addr = addr; } public int getNumber(){return number; } public void setnumber(int number){this.number = number; } @Override public object clone(){sustent stu = null; try {stu =(desute)super.clone(); // shallow copy} catch(clonenotsupportedexception e){e.printstacktrace(); } stu.addr =(address)addr.clone(); //ディープコピーリターンSTU; }} public class test {public static void main(string args []){address addr = new Address(); addr.setadd( "Hangzhou City");学生stu1 = new Student(); stu1.setnumber(123); stu1.setaddr(addr); Student Stu2 =(学生)stu1.clone(); System.out.println( "Student 1:" + Stu1.getNumber() + "、address:" + stu1.getaddr()。getadd()); System.out.println( "Student 2:" + Stu2.getNumber() + "、address:" + stu2.getaddr()。getadd()); addr.setadd( "xihu地区"); System.out.println( "Student 1:" + Stu1.getNumber() + "、address:" + stu1.getaddr()。getadd()); addr.setadd()); System.out.println( "Student 2:" + Stu2.getNumber() + "、address:" + stu2.getaddr()。getadd()); }}結果:
学生1:123、住所:杭州学生2:123、住所:杭州学生1:123、住所:Xihu地区学生2:123、住所:杭州市
この結果は、私たちのアイデアに沿っています。
最後に、クローンメソッドを実装するAPIのクラスの1つを確認できます。
java.util.date:
/***このオブジェクトのコピーを返します。 */ public Object clone(){date d = null; try {d =(date)super.clone(); if(cdate!= null){d.cdate =(basecalendar.date)cdate.clone(); }} catch(clonenotsupportedexception e){} // }このカテゴリは実際には深いコピーです。
浅いクローンと深いクローン
1。浅いクローニング
浅いクローニングでは、プロトタイプオブジェクトのメンバー変数が値タイプの場合、1つのコピーがクローンされたオブジェクトにコピーされます。プロトタイプオブジェクトのメンバー変数が参照タイプの場合、参照オブジェクトのアドレスはクローンオブジェクトにコピーされます。つまり、プロトタイプオブジェクトのメンバー変数とクローン化されたオブジェクトは同じメモリアドレスを指します。
簡単に言えば、浅いクローニングで、オブジェクトがコピーされると、オブジェクトの値タイプのメンバー変数のみがコピーされ、参照タイプのメンバーオブジェクトがコピーされません。
Java言語では、オブジェクトクラスのclone()メソッドを上書きすることにより、浅いクローニングを実装できます。
2。深いクローニング
ディープクローンでは、プロトタイプオブジェクトのメンバー変数が値タイプであろうと参照タイプであろうと、1つのコピーがクローンオブジェクトにコピーされます。 Deep Cloningは、プロトタイプオブジェクトの参照されたすべてのオブジェクトをクローンオブジェクトにコピーします。
オブジェクト自体がコピーされることを除いて、ディープクローニングで簡単に言えば、オブジェクトに含まれるすべてのメンバー変数もコピーされます。
Java言語では、ディープクローンを実装する必要がある場合は、オブジェクトクラスのclone()メソッドを上書きすることで実装できます。または、シリアル化などで実装できます。
(参照タイプに多くの参照タイプが含まれている場合、または内部参照タイプのクラスに参照タイプが含まれている場合、クローン法を使用するのは非常に面倒です。現時点では、シリアル化を使用してオブジェクトの深いクローンを実装できます。)
シリアル化は、ストリームにオブジェクトを書き込むプロセスです。ストリームに書かれたオブジェクトは元のオブジェクトのコピーであり、元のオブジェクトはメモリに存在します。シリアル化によって実装されたコピーは、オブジェクト自体をコピーするだけでなく、参照するメンバーオブジェクトをコピーすることもできます。したがって、オブジェクトをストリームにシリアル化し、ストリームから読み出すことにより、深いクローンを達成できます。シリアル化を実装できるオブジェクトのクラスは、シリアル化可能なインターフェイスを実装する必要があることに注意してください。そうしないと、シリアル化操作を実装できません。
拡張
Java言語が提供するクローン可能なインターフェイスとシリアル化可能なインターフェイスのコードは非常に簡単です。どちらも空のインターフェイスです。この空のインターフェイスは、識別インターフェイスとも呼ばれます。識別インターフェイスには、メソッドの定義はありません。その機能は、これらのインターフェイスの実装クラスに、クローニングをサポートするかどうか、シリアル化をサポートするかなど、特定の関数を持っているかどうかを伝えることです。
多層クローニングの問題を解決します
参照タイプに多くの参照タイプが含まれている場合、または内部参照型クラスに参照タイプが含まれている場合、クローン法を使用するのは非常に面倒です。この時点で、シリアル化を使用して、オブジェクトの深いクローン化を実装できます。
パブリッククラスの外側はシリアル化可能{private static final long serialversionuid = 369285298572941l; // IDパブリックインナーを明示的に宣言することが最善です。 // discription:[ディープコピーメソッド、オブジェクトとすべてのオブジェクトプロパティをシリアル化する必要があります] public outer myclone(){outer outer = null; {//オブジェクトをストリームにシリアル化してください。ストリームに記述されているものはオブジェクトのコピーであり、元のオブジェクトがJVMにまだ存在するためです。したがって、この機能を使用して、オブジェクトのディープコピーを実現できます。 ObjectOutputStream OOS = new objectOutputStream(baos); oos.writeobject(this); //ストリームをオブジェクトにシリアル化するbytearrayinputStream bais = new bytearrayinputStream(baos.tobytearray()); ObjectInputStream ois = new ObjectInputStream(BAIS); outer =(outer)ois.readObject(); } catch(ioexception e){e.printstacktrace(); } catch(classNotFoundException e){e.printstacktrace(); }外側を返します。 }}また、インナーはシリアル化可能な実装も必要です。そうしないと、シリアル化することはできません。
パブリッククラスの内側はシリアル化可能{private static final long serialversionuid = 872390113109l; // IDパブリック文字列name = "";を明示的に宣言することが最善です。 public Inner(string name){this.name = name; } @Override public String toString(){return "内側の名前値は:" + name; }}これにより、2つのオブジェクトが互いの価値に影響を与えることなく、メモリ空間に完全に独立して存在することもできます。
要約します
オブジェクトのクローニングを実装するには、次の2つの方法があります。
1)。クローン可能なインターフェイスを実装し、オブジェクトクラスでclone()メソッドをオーバーライドします。
2)。シリアル化可能なインターフェイスを実装し、オブジェクトのシリアル化と脱派化を介してクローニングを実装します。これは、真の深いクローニングを実現できます。
注:シリアル化と敏aserializationに基づくクローニングは、単なる深いクローン化ではなく、より重要なことに、一般的な制限を通じて、クローン化されるオブジェクトがシリアル化をサポートするかどうかを確認できます。このチェックはコンパイラによって行われ、実行時に例外をスローしません。このソリューションは、オブジェクトクラスのクローンメソッドを使用して、オブジェクトをクローニングするよりも明らかに優れています。コンパイル時にそれを公開することにより、ランタイムに問題を残す方が常に良いです。
上記は、Javaプログラミングの実装オブジェクトクローニング(コピー)コードのすべての詳細な説明です。すべての人に役立つことを願っています。興味のある友人は、このサイトの他の関連トピックを引き続き参照できます。欠点がある場合は、それを指摘するためにメッセージを残してください。