Stringの不変性のコードトレース

基礎教材で学んだ「Stringの内容は変わらない」を使い、代入・戻り値の破棄・別名参照が混在するコードを一行ずつ追います。まず出力を予想し、その後で参照先と値の対応を確認してください。ここではJava SE 17の構文とAPIを使います。

この単元で学ぶこと

  • 各行の実行後にa・b・cの参照先と文字列の値を記録する
  • 戻り値を捨てた操作と変数への再代入を区別する
  • 途中の出力と最後の出力を状態表から説明する

本文解説

演習の準備 基礎教材「Stringの不変性を理解する」を読んだ後の演習です。参照先には説明用の記号S0・S1・S2を付けます。これはメモリアドレスやJVM内部の割当個数を表すものではありません。各行で「戻り値」「代入先」「他の変数への影響」を書き出してください。

演習コード:空欄を自分で埋める public class StringTrace { public static void main(String[] args) { String a = "cat"; // 1 String b = a; // 2 a.concat("s"); // 3 String c = a.substring(1); // 4 a = a.concat("s"); // 5 b = c; // 6 c += "!"; // 7 System.out.println(a + "/" + b + "/" + c); // 8 a = b; // 9 System.out.println(a + "/" + b + "/" + c); // 10 } } 問:3・5・7・9行目直後のa、b、cの値と、8・10行目の出力を求めてください。

1〜3行目:共有と戻り値の破棄 1行目:a→S0「cat」。2行目:b→S0「cat」、aとbは同じ参照先です。3行目:concatの戻り値は「cats」ですが、受け取る代入がありません。直後もa→S0、b→S0で両方の値はcatです。cはまだ宣言されていません。呼出しだけでaの参照先が書き換わると考えるのが誤りです。

4〜5行目:部分文字列と再代入 4行目:substring(1)は添字1から末尾までの「at」を返し、c→S1「at」とします。aとbはcatのままです。5行目:aだけをconcatの戻り値S2「cats」へ向けます。直後はa=cats、b=cat、c=atです。bはaという変数を参照しているのではなく、代入時のStringを参照しているので追従しません。

6〜8行目:別名参照をさらに分岐させる 6行目:b=cによりb→S1「at」、c→S1「at」となります。7行目:c += "!"は、この変数についてc = c + "!"と同じ結果です。c→S3「at!」になり、b→S1「at」は変わりません。直後はa=cats、b=at、c=at!。8行目の出力はcats/at/at!です。

9〜10行目:参照の再共有 9行目:a=bによってaとbはS1「at」を共有します。S2「cats」の内容を書き換えたのではありません。直後はa=at、b=at、c=at!。10行目の出力はat/at/at!です。最終出力だけを見ず、5行目で作ったaの状態が9行目で置き換わったことまで説明します。

解答をまとめる 3行目後:a=cat / b=cat / c=未宣言 5行目後:a=cats / b=cat / c=at 7行目後:a=cats / b=at / c=at! 9行目後:a=at / b=at / c=at! 出力(2行): cats/at/at! at/at/at! 誤答cats/cats/at!は、aへの代入がbにも伝わると考えたものです。誤答cats/at!/at!は、cへの再代入を共有オブジェクトの変更と取り違えています。

条件を変えて確認する 元コードの3行目だけをa = a.concat("s");へ変更すると、4行目のcは「ats」、5行目のaは「catss」です。その後の出力はcatss/ats/ats!とats/ats/ats!になります。代入を一つ足すと、後続のsubstringが読む値まで変わります。Stringの不変性と、変数が指す値が変わることは両立します。

図解

Stringの行別トレース:参照先の変化
a・b・cは変数、S0〜S3は説明用の参照先記号。代入先だけ矢印が変わり、元のStringの内容は変わらない。

重要ポイント

  • 各行の代入先だけ参照を書き換え、他の変数はそのまま残す。
  • 戻り値を捨てたconcatは、呼出し元の値を変えない。
  • b=cの後でcに再代入しても、bは以前の参照先を保持する。
  • 出力はその行の時点で評価する。最後の値で過去の出力を上書きしない。

間違えやすい点

  • S0などの説明用記号を、実際のアドレスや割当回数と混同しない。
  • Stringの内容が不変でも、finalでない変数への再代入はできる。
  • +=をStringBuilderのような同一オブジェクトの編集だと考えない。

用語解説

不変
生成後にオブジェクトの内容を変更できない性質。
再代入
変数に別の値を保存すること。
参照共有
複数の変数が同じオブジェクトを指す状態。

関連教材

参考資料

教材データに登録された参考リンクです。リンク先の内容や更新日は提供元でご確認ください。

確認問題の出題・採点や学習履歴の保存にはログインが必要です。