わかりずらい Java の参照渡し (1/3)

Java の引数はプリミティブ型であれば値渡し、オブジェクトであれば参照渡しというのは周知の事実です。
その参照渡しは "オブジェクトの参照" を渡すのですが、広く知られている "変数の参照" を渡す方法と区別されていないためか、しばしば混乱する場合があります。

例を挙げるとわかりやすいと思います。下のソースを見てください。

サンプルA
public class SampleA
{
 public static void main(String[] args)
 {
  String s1 = "main メソッド実行"; ・・・1

  SampleA sampleA = new SampleA();
  sampleA.test1(s1);        ・・・2

  System.out.println(s1);     ・・・4
 }

 public void test1(String s2)
 {
  s2 = "test1 メソッド実行";    ・・・3
 }
}

このサンプルAを実行した時、の部分でコンソールに何が出力されるでしょうか?
答えは
> java SampleA
main メソッド実行
となります。

勘違いしやすいのは、
で test1 メソッドを実行するので、変数:s1 は "test1 メソッド実行" に書き変わり、出力は "test1 メソッド実行" になる」
と考えるパターンです。
これは Java の参照渡しが、"オブジェクトの参照" を渡す方法なのに、"変数の参照" を渡す方法と勘違いしているためです。

メモリをイメージした図にすると、 の段階では以下の様になります。
左の変数名は変数のメモリ空間を表すアドレスで数字はオブジェクトのメモリ空間を表すアドレス、右の罫線内が格納されている値と思ってください。

の段階
  ├───────────
s1│50
  ├───────────
  │   ・
      ・
  │   ・
  ├───────────
50│main メソッド実行
  ├───────────
変数:s1 にはアドレス 50 への参照が入り、アドレス 50 に実際の値である "main メソッド実行" が入っています。
そしてで test1 メソッドに引数を渡した時が以下の状態です。
の段階
  ├───────────
s1│50
  ├───────────
s2│50
  ├───────────
  │   ・
      ・
  │   ・
  ├───────────
50│main メソッド実行
  ├───────────
test1 メソッドに "オブジェクトの参照" が渡されているのが解るでしょうか?
重要なのは、変数:s1 と変数:s2 は全く別のメモリ空間ということです。
test1 メソッドに変数:s1 そのもの(変数の参照)が渡されているわけではありません。

その後、の段階では以下の様になります。

の段階
  ├───────────
s1│50
  ├───────────
s2│51
  ├───────────
  │   ・
      ・
  │   ・
  ├───────────
50│main メソッド実行
  ├───────────
51│test1 メソッド実行
  ├───────────
では変数:s2 が参照するオブジェクトを変更しただけに過ぎません。
渡されたオブジェクト自体、ましてや 変数:s1 を操作したわけではありません。

ここまでだけだと理解できると思います。
では、何故勘違いしてしまうのか?

それは、最もよく使われる String クラスにオブジェクトそのものを操作するメソッドがないからです。

2/3 へ続く

トラックバック

このエントリーのトラックバックURL:
http://www.tec-q.com/mt/mt-tb.cgi/871

コメント (6)

一番わかりやすい説明でした

あなたが神か

これ間違ってますよ。
String型が不変なだけです。

これ間違ってますよ。
String型が不変なだけです。

k、お前は間違ってますよ。

「String型不変」となんなんだよ?

S2はメソッドの中で普通の変数として使えますよ?
不変とはなんだ?

イミュータブルなオブジェクトって意味じゃないかな。
Javaにおける値渡しと参照渡しを説明するのであれば、プリミティブ型とミュータブルなオブジェクト型を比較するのが妥当で、イミュータブルなString型を引き合いに出すのは正確ではないと私は思いますよ。

コメントを投稿