Webサイト制作コースのお申し込みはこちら Webサイト制作コースのお申し込みはこちら

Javaでオブジェクトをコピーする際、コピー方法によって挙動が異なるため注意が必要です。

本記事では、Javaにおけるオブジェクトのコピー方法と処理内容の違いについてサンプルコードを掲載しながらご紹介していきます。

Javaオブジェクトを=(イコール)でコピー


まずは作成したオブジェクトを「=(イコール)」を使ってコピーした場合の挙動を確認してみます。

サンプル

public class Main {

	public static void main(String[] args) {
		Human human1 = new Human();
		human1.name = "山田 太郎";
		Human human2 = human1;
		System.out.println(human1.name);
		System.out.println(human2.name);
		
		human2.name = "佐藤いちろう";
		System.out.println(human1.name);
		System.out.println(human2.name);
	}
}

class Human {
	String name;
}

実行結果が下記です。

山田 太郎
山田 太郎
佐藤いちろう
佐藤いちろう

予想していた結果と異なる方もいらっしゃるのではないでしょうか?

変数に格納するようにイコールでコピーを実施すると、human2のnameフィールドだけ値を変更したつもりがhuman1のnamdeフィールドまで変更されてしまっています。

解説

6行目でhuman1のインスタンスを新たに作成したhuman2のインスタンスに格納しており、値がコピーされていることを確認出来ます。

しかしこのコピー方法ではオブジェクトへの参照をコピーしていることとなり、新たに作成したhuman2とhuman1は同じオブジェクトを参照している状態です。

つまりサンプルのようにhuman2のnameフィールドを変更した場合でも、同じオブジェクトを参照しているhuman1のnameフィールドにも影響を与える結果となります。

Javaのcloneメソッドでオブジェクトをコピー


ではどのようにして参照先の異なるオブジェクトとしてコピーするのかというと、Cloneableインターフェースの「clone」メソッドを使用します。

サンプル

public class Main {

	public static void main(String[] args) {
		Human human1 = new Human();
		human1.name = "山田 太郎";
		Human human2 = human1.clone();
		System.out.println(human1.name);
		System.out.println(human2.name);
		
		System.out.println("---human2の値だけ変更---");
		human2.name = "佐藤いちろう";
		System.out.println(human1.name);
		System.out.println(human2.name);
	}
}

class Human implements Cloneable{
	String name;
	@Override
	public Human clone() {
		Human human = new Human();
		human.name = this.name;
		return human;
	}
}

実行した結果が下記です。

山田 太郎
山田 太郎
---human2の値だけ変更---
山田 太郎
佐藤いちろう

cloneメソッドを利用することで、指定したインスタンスの値限定で変更されていることをご確認頂けます。

解説

17行目の「implements Cloneable」がインターフェースを実装する形でHumanクラスを作成している部分です。

Objectクラスに定義されているcloneメソッドを、20行目に記述したcloneメソッドによりオーバーライドして実装しています。

このようにcloneメソッドを利用したコピーのような方法を「ディープコピー」と呼びます。

newしたJavaオブジェクトへコピー


Javaでは、全く異なるインスタンスオブジェクトとして作成し値をコピーすると、コピー元の値まで書き換えられることはありません。

サンプル

public class Main {

	public static void main(String[] args) {
		Human human1 = new Human();
		human1.name = "山田 太郎";
		Human human2 = new Human();
		human2.name = human1.name;
		System.out.println(human1.name);
		System.out.println(human2.name);
		
		human2.name = "佐藤いちろう";
		System.out.println(human1.name);
		System.out.println(human2.name);
	}
}

class Human{
	String name;
}

実行結果は下記です。

山田 太郎
山田 太郎
山田 太郎
佐藤いちろう

解説

4行目と6行目でそれぞれnew演算子を使用してインスタンス化しているため、全く別のメモリ領域が確保されていることになります。

ただしこの方法の場合、フィールドを1つ1つコピーする必要があるため、大量のフィールドが定義されている場合には大変な作業となってしまいます。

ポテパンダの一言メモ

7行目の記述を「human2 = human1;」のように変更してしまうと、最初にご紹介したイコールでの参照コピーとなってしまい、human1とhuman2の参照先が同じになります。

シャローコピーとディープコピーの違い


Javaでのオブジェクトコピー方法を3つご紹介してきましたが、1つ目のイコールを使ったコピー方法を「シャローコピー」、2つ目のcloneメソッドを使ったコピー方法を「ディープコピー」と呼びます。

シャローコピー

シャローコピーは「浅いコピー」とも呼ばれ、実際のデータはコピーされず参照のみがコピーされる言わば見せかけのコピーです。

シャローコピーで作成されたオブジェクトは、実際のデータとしてコピー元と同じメモリの値を参照しているため、オブジェクトの値を変更すればもう片方のオブジェクトの値も自動的に変更されているように見えます。

ディープコピー

ディープコピーは「深いコピー」とも呼ばれ、実際のデータがコピーされた完全に新しいオブジェクトを作成することが出来ます。

メモリ上でも全く別のデータとして扱われるわけですから、オブジェクトの値を変更した場合でも、もう片方のオブジェクトの値が変更されることはありません。

さいごに: Javaでオブジェクトをコピーする際は注意が必要


本記事では、Javaでオブジェクトをコピーする際に利用する3つの方法と処理内容の違いについてご紹介してきました。

シャローコピーとディープコピーの違いをしっかりと把握しておかないと、予期せぬエラーを引き起こす原因となりやすい部分です。

一見シャローコピーでもデータだけをコピーしているようにも見え、テストでも見逃しやすい部分ですので、処理結果の違いに注意して実装するよう意識しておきましょう。

エンジニアになりたい人に選ばれるプログラミングスクール「ポテパンキャンプ 」

ポテパンキャンプは卒業生の多くがWebエンジニアとして活躍している実践型プログラミングスクールです。 1000名以上が受講しており、その多くが上場企業、ベンチャー企業のWebエンジニアとして活躍しています。

基礎的な学習だけで満足せず、実際にプログラミングを覚えて実践で使えるレベルまで学習したいという方に人気です。 プログラミングを学習し実践で使うには様々な要素が必要です。

それがマルっと詰まっているポテパンキャンプでプログラミングを学習してみませんか?

卒業生の多くがWebエンジニアとして活躍

卒業生の多くがWeb企業で活躍しております。
実践的なカリキュラムをこなしているからこそ現場でも戦力となっております。
活躍する卒業生のインタビューもございますので是非御覧ください。

経験豊富なエンジニア陣が直接指導

実践的なカリキュラムと経験豊富なエンジニアが直接指導にあたります。
有名企業のエンジニアも多数在籍し品質高いWebアプリケーションを作れるようサポートします。

満足度高くコスパの高いプログラミングスクール「ポテパンキャンプ」

運営する株式会社ポテパンは10,000人以上のエンジニアのキャリアサポートを行ってきております。
そのノウハウを活かして実践的なカリキュラムを随時アップデートしております。

代表の宮崎もプログラミングを覚えサイトを作りポテパンを創業しました。
本気でプログラミングを身につけたいという方にコスパ良く受講していただきたいと思っておりますので、気になる方はぜひスクール詳細をのぞいてくださいませ。