参照型とnullのコードトレース
参照型の変数はオブジェクトそのものを変数へ詰め込むのではなく、オブジェクトを参照する値を持ちます。参照のコピー、内容の変更、nullへの再代入を分けると、共有と例外の原因が見えます。
この単元で学ぶこと
- 参照変数とオブジェクトを区別する
- 参照コピーと別オブジェクト生成を追跡する
- null検査・参照解除・内容比較を区別する
本文解説
参照を図にする int[] a={4,5};int[] b=a;では配列は一つです。aとbという二本の矢印が同じ配列を指す図を書きます。b[1]=8;の後にa[1]を表示すると8です。変数が二つでも実体が二つになるとは限りません。
参照の付け替え aとbが同じ配列を指していても、b=new int[]{9};はbの矢印だけを新しい配列へ向けます。aは元の配列のままです。要素への代入b[0]=9と、変数への代入b=new int[]{9}を別の操作として追います。
nullの参照比較 String a=null,b=null;でa==bはtrueです。両方とも実体を指していない状態を比較できます。一方a.equals(b)は受け手aがnullなので例外です。==はメソッド呼出しではなく、nullを受け手とするequalsとは動作が違います。
空配列との違い int[] a=new int[0];は長さ0の配列への非null参照です。a.lengthは0ですがa[0]は存在せず、ArrayIndexOutOfBoundsExceptionになります。int[] a=null;ならlengthを取得する段階でNullPointerExceptionです。長さ0と実体なしを区別します。
Stringの再代入 String a="ab";String b=a;a=a+"c";ではaは"abc"を指し、bは"ab"のままです。Stringの内容は変更できず、連結結果をaへ再代入しています。共有された可変配列の要素書換えとは結果が違います。
宣言型と明示的変換 Object x="dog";ではx.length()は呼べません。String s=(String)x;としてからs.length()を呼べば3です。キャストは実体をStringに作り替えず、参照が指す実体をその型として扱えるかを確認します。
不正なキャスト Object x=new Object();String s=(String)x;はコンパイルできても実体がStringでないためClassCastExceptionです。nullのStringへのキャスト自体は成功してnullのままです。変換成功と、その後にメソッドを安全に呼べることは別です。
安全な内容比較 "ok".equals(s)ならsがnullでもfalseです。非nullリテラルが受け手なので呼出し可能です。s!=null&&s.equals("ok")でも、左がfalseなら右を評価しません。両者の目的は同じでも、受け手と短絡のどちらで安全性を得るか説明できます。
図解
重要ポイント
- 配列とStringも参照型。
- nullはどのオブジェクトも参照しない値。
- 参照の代入はオブジェクトの複製ではない。
- ==は参照の一致、String.equalsは内容を比べる。
間違えやすい点
- 一つの変数をnullにしても他の変数までnullにはならない。
- 空のStringとnullは違う。
- 参照型のローカル変数も読む前に初期化が必要。
用語解説
- 参照
- オブジェクトを指し示す値。Javaでは数値アドレスを直接操作しない。
- null
- オブジェクトを参照しない特別な値。
- 参照解除
- 参照先のフィールド・配列要素・インスタンスメソッド等へアクセスすること。
関連教材
参考資料
教材データに登録された参考リンクです。リンク先の内容や更新日は提供元でご確認ください。
確認問題の出題・採点や学習履歴の保存にはログインが必要です。