NullPointerExceptionとprintメソッドの関係について

皆さんこんばんは、駆け出しエンジニアの脆弱性アザラシです。

今回は、Javaの資格であるJava SE11 Silverの試験対策をしていて、対策本の一つである『徹底攻略Java SE 11 Silver問題集』通称「[黒本](https://www.amazon.co.jp/%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5Java-SE-11-Silver%E5%95%8F%E9%A1%8C%E9%9B%86-1Z0-815/dp/4295007625/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=3HUSED8HBCVXE&dib=eyJ2IjoiMSJ9.O9_BcR-8LKHlrUZ00AI17ahtX1t29m23NLeH7LgkO1JD83mTVEAYGqtKDOkcCKv-Xc_ftW0DSu6kExSVoXpaqx6o7pKjoXIYJIGszbRaoT65ZfqOfGeSJnz9PJokDz1vsBJMEymLeBhSoBdiUubqNO98hmW30E3vF1UggAq5teMYQJCkYCX5Ij4XtlLnF58scO7OUB6yk6CQg7a-AJ-r0VVRBHAnZ60pBL2KLhJo804xcgXSAO-nZgJq8VwzbWzsRs8onnbZvBGKaahU87wkE0h137qpokKX-OndhUgepWE.hS5g3nIHLsq9HwX7MmkQj1eIgBsxlVFEjm82dbVIokk&dib_tag=se&keywords=Java+silver&qid=1726485178&sprefix=java+silver%2Caps%2C173&sr=8-1)」を読んでいて、NullPointerExceptionが発生するタイミングが気になった問題があり、調べてみました。

<h1>デフォルト値によるNullPointerException</h1>

今回気になったのは、第5章「配列の操作」の問5と問6の問題です。問5ではまず、下記の問題が挙げられています。

> 次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。（1つ選択）

```java
public class Item {
    String name;
    int price = 100;
}
```
```java
public class Main {
    public static void main(String[] args){
    Item[] items = new Item[3];
    int total = 0;
    for (int i = 0; i < items.length; i++) {
        total += items[i].price;
        }
        System.out.println(total);
    }
 }
```
> 志賀澄人、株式会社ソキウス・ジャパン『徹底攻略Java SE 11 Silver問題集』p139より.

この問題の答えは、実行時に例外NullPointerExceptionがスローされる、です。

このコードでなぜ例外が発生するかというと、Mainクラスの3行目では、「3つのアイテムを扱う配列インスタンス」をitemsで宣言するということしかしておらず、Itemのインスタンスを生成しているわけではないからです。ここでは<b>オブジェクト型配列のデフォルト値である「null」で初期化されています（1）。</b>

そのため、Mainクラスの6行目でpriceフィールドにアクセスする段階で、変数の参照先が存在しないことを意味するNullPointerExceptionが発生します。

<h1>printメソッドの仕様</h1>

気になったのが、この次の問題である問6です。

> 次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。（1つ選択）
```java
public class Main{
    public static void main(String[]args){
        String[]array = {"A","B","C","D"};
        array[0] = null;
        for(String str : array){
            System.out.print(str);
        }
    }
}
```
> 志賀澄人、株式会社ソキウス・ジャパン『徹底攻略Java SE 11 Silver問題集』p140より.

この問題では、Mainクラスの4行目でarray[0] = null;と、要素の値を変更しています。答えは、「nullBCD」と表示されるということなのですが、先ほどの問5を経た私は、<b>「なぜここではNullPointerExceptionが発生しないんだ…？」</b>と疑問に思いました。

要素の値が変更されているのであれば、配列の一つ目に入っているのは「null」で、そのnullを呼び出そうとしているのだから、NullPointerExceptionが発生するはずでは？と思ったのです。

気になったので、レファレンスを参照してみます。すると、NullPointerExceptionが発生するタイミングは、主に次の5種類であると書かれています。

>・nullオブジェクトのインスタンス・メソッドの呼出し。
・nullオブジェクトのフィールドに対するアクセスまたは変更。
・nullの長さを配列であるかのように取得。
・nullのスロットを配列であるかのようにアクセスまたは修正。
・nullをThrowable値であるかのようにスロー(2)。

元々レファレンスにおけるNullPointerExceptionの内容を把握していたわけではありませんが、私が問6で例外がスローされると誤認したのは、一番上の「nullオブジェクトのインスタンス・メソッドの呼出し」だと思います。print()メソッドでnullオブジェクトを呼び出しているんじゃないの？と考えたわけです。

どうにも分からなかったためChatGPTに聞いてみたところ、print()メソッドの仕様が関係している、という回答でした。

黒本の回答の解説（黒本p153）にもある通り、nullはどこも参照していないことを示す「リテラル」として、Javaでは扱われます。

GPTによれば、nullがリテラルであるためSystem.out.println(null) のように null を直接 print() メソッドに渡すことができるということのようです。print()メソッドやprintln()メソッドは、そもそもそういう仕様になっていると……うーむ？

―――

記事を書いている最中に、私と全く同じ疑問を感じて「教えて！goo」にて、で質問をしてらっしゃる方がいるのを発見しました（3）。こちらでもどうやら概ね同じ回答のようでして、そもそもprintメソッドとNullPointerExceptionがそういうものだから、ということのようです。レファレンスでprintメソッドについてさらに調べてみると、このような記述が見つかりました。

>public void print(String s)
文字列を出力します。引数がnullの場合は、文字列"null"が出力されます。それ以外の場合、文字列の文字はプラットフォームのデフォルトの文字エンコーディングに従ってバイトに変換され、これらのバイトはwrite(int)メソッドとまったく同じ方法で書き込まれます。
パラメータ:
s - 出力されるString値 (4)

printメソッドはそもそも、nullが来た場合はnullと出力されるように定義されている、だからNullPointerExceptionは発生しないという結論のようです。ちょっと知り切れトンボですが……😅

2024年9月16日.

>参考
全体.志賀澄人、株式会社ソキウス・ジャパン[『徹底攻略Java SE 11 Silver問題集』](https://www.amazon.co.jp/%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5Java-SE-11-Silver%E5%95%8F%E9%A1%8C%E9%9B%86-1Z0-815/dp/4295007625/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=3HUSED8HBCVXE&dib=eyJ2IjoiMSJ9.O9_BcR-8LKHlrUZ00AI17ahtX1t29m23NLeH7LgkO1JD83mTVEAYGqtKDOkcCKv-Xc_ftW0DSu6kExSVoXpaqx6o7pKjoXIYJIGszbRaoT65ZfqOfGeSJnz9PJokDz1vsBJMEymLeBhSoBdiUubqNO98hmW30E3vF1UggAq5teMYQJCkYCX5Ij4XtlLnF58scO7OUB6yk6CQg7a-AJ-r0VVRBHAnZ60pBL2KLhJo804xcgXSAO-nZgJq8VwzbWzsRs8onnbZvBGKaahU87wkE0h137qpokKX-OndhUgepWE.hS5g3nIHLsq9HwX7MmkQj1eIgBsxlVFEjm82dbVIokk&dib_tag=se&keywords=Java+silver&qid=1726485178&sprefix=java+silver%2Caps%2C173&sr=8-1)2019年,インプレス.
1.[やさしいデスマーチ「オブジェクト型配列」](https://shuji-w6e.hatenadiary.org/entry/20070313/1173795816)、最終閲覧日：2024年9月16日．
2.[Java（tm）Platform Standard Edition,"クラスNullPointerException
"](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/NullPointerException.html)、最終閲覧日：2024年9月16日.
3.[教えて！goo「Java配列でNullPointerExceptionになるケースとならないケースの差がわかりません」](https://oshiete.goo.ne.jp/qa/11104697.html#google_vignette)、最終閲覧日：2024年9月16日.
4.[Java（tm）Platform Standard Edition,"クラスPrintStream
"](https://docs.oracle.com/javase/jp/8/docs/api/java/io/PrintStream.html)、最終閲覧日：2024年9月16日.