Java
 Computer >> コンピューター >  >> プログラミング >> Java

Javaのclone()メソッドの重要性とは?シャローコピーとディープコピーの違いを徹底解説

Javaにおけるclone()メソッドは、Cloneableインターフェースを実装したクラスのオブジェクトを複製(コピー)するために使用されます。デフォルトではフィールド単位のコピーが行われます。これは、Objectクラスが呼び出し元クラスの具体的なメンバー構成を知らないためです。

そのため、クラスがプリミティブ型のメンバーのみを持つ場合は、新しいオブジェクトのコピーが作成され、その参照が返されます。しかし、クラスが参照型(クラス型)のメンバーを持つ場合には、オブジェクトへの参照だけがコピーされ、元のオブジェクトと複製されたオブジェクトの両方が同じインスタンスを指すことになります。

また、Cloneableインターフェースを実装していないクラスのオブジェクトに対してclone()メソッドを呼び出すと、CloneNotSupportedExceptionがスローされます。このインターフェースはマーカーインターフェースと呼ばれるもので、実装自体にメソッドは含まれておらず、「このクラスのオブジェクトに対してObject.clone()メソッドを呼び出してよい」という意図を示すためだけに存在します。

clone()メソッドの構文

protected Object clone() throws CloneNotSupportedException

コピーの実装方法は2種類ある

clone()メソッドを使ったオブジェクトの複製には、主に以下の2つの方法があります。

1. シャローコピー(浅いコピー)

シャローコピーは、Object.clone()メソッドが提供するデフォルトの複製動作による結果です。クラスが非プリミティブ型(参照型)のメンバーを持っている場合でも、オブジェクトの参照のみがコピーされ、参照先のオブジェクト自体は複製されません。その結果、元のオブジェクトとクローンされたオブジェクトは、同じ内部オブジェクトを共有することになります。

シャローコピーのコード例

public class ShallowCopyTest {
    public static void main(String args[]) {
        A a1 = new A();
        A a2 = (A) a1.clone();
        a1.sb.append("Tutorialspoint!");
        System.out.println(a1);
        System.out.println(a2);
    }
}
class A implements Cloneable {
    public StringBuffer sb = new StringBuffer("Welcome to ");
    public String toString() {
        return sb.toString();
    }
    public Object clone() {
        try {
            return super.clone();
        } catch(CloneNotSupportedException e) {
        }
        return null;
    }
}

実行結果

Welcome to Tutorialspoint!
Welcome to Tutorialspoint!

この例では、a1のStringBufferに文字列を追加しただけで、a2の出力にも同じ変更が反映されています。これは両者が同一のStringBufferインスタンスを共有しているためです。これがシャローコピーの典型的な挙動です。

2. ディープコピー(深いコピー)

非プリミティブ型のメンバーを持つクラスでディープコピーを実現するには、clone()メソッドをオーバーライドする必要があります。デフォルトの複製メカニズムではメンバーオブジェクト自体は複製されないため、参照型のフィールドについても明示的に新しいインスタンスを作成して代入する必要があります。

ディープコピーのコード例

public class DeepCopyTest {
    public static void main(String args[]) {
        A a1 = new A();
        A a2 = (A) a1.clone();
        a1.sb.append(" TutorialsPoint!");
        System.out.println(a1);
        System.out.println(a2);
    }
}
class A implements Cloneable {
    public StringBuffer sb = new StringBuffer("Welcome to ");
    public String toString() {
        return sb.toString();
    }
    public Object clone() {
        try {
            A a = (A) super.clone();
            a.sb = new StringBuffer(sb.toString());
            return a;
        }
        catch(CloneNotSupportedException e) {
        }
        return null;
    }
}

実行結果

Welcome to TutorialsPoint!
Welcome to

この例では、clone()メソッド内でStringBufferの新しいインスタンスを作成し直しています。そのため、a1の内容を変更してもa2には影響せず、それぞれが完全に独立した状態になっています。これがディープコピーです。

まとめ:clone()メソッドを使う際のポイント

  • clone()を使用するには、対象クラスがCloneableインターフェースを実装している必要があります。
  • デフォルトのclone()はシャローコピーであり、参照型フィールドは元のオブジェクトと共有されます。
  • 独立した複製が必要な場合は、clone()をオーバーライドしてディープコピーを実装しましょう。
  • 実際の開発では、コピーコンストラクタやシリアライゼーションを利用した複製の方が安全で推奨されるケースも多いため、設計時に検討すると良いでしょう。
  1. JavaのThreadクラスで使えるisAlive()メソッドの基本と使い方

    isAlive()メソッドとは isAlive()メソッドは、JavaのThreadクラスに用意されているメソッドの一つで、対象のスレッドが現在「生存(Alive)」しているかどうかを判定するために使用されます。ここでいう生存とは、start()メソッドによってスレッドが起動済みであり、まだ終了していない状態を指します。run()メソッドが呼び出されると、スレッドは一定期間動作した後に実行を停止します。 構文 final Boolean isAlive() このメソッドは、呼び出し対象のスレッドがまだ実行中で終了していない場合にtrueを返し、それ以外の場合はfalseを返します。 tru

  2. Java 9のdestroyForcibly()メソッドとは?プロセスを強制終了する方法を解説

    destroyForcibly() メソッドは、実行中のプロセスを強制的に終了(キル)するために使用されます。このメソッドは、プロセスが正常に終了しない場合や、フリーズ(応答停止)してしまった場合に特に役立ちます。 例えば、destroyForcibly() を呼び出した後でも isAlive() メソッドが true を返すことがあります。これは、OSレベルでの終了処理が完了するまでに時間差があるためです。また、destroyForcibly() は終了要求が正常に受け付けられた場合は true を返し、そうでない場合は false を返します。 構文 boolean destroyForc