第7章までは、社員の名前と年齢を別々の変数や配列に入れていました。この章では、「社員」という1つのまとまりを表す クラス を自分で定義し、そこから社員1人1人の オブジェクト を作って使います。Java のプログラムの中心になる考え方です。
この章で学ぶこと
- クラスの定義とフィールド
-
newでオブジェクトを作り、.でフィールドやメソッドを使う -
staticの付かないメソッド - コンストラクタと
this - 変数に入っているのはオブジェクトの「参照」であること
-
nullとNullPointerException
8.1 この章で作るもの
この章では、ch08 パッケージに、社員を表すクラス Employee と、それを使うクラス Main を作ります。章の最後には、Main を実行するとコンソールに次のように表示されるようになります。
雷禅 海皇(26歳)
院照 心愛(27歳)
雷禅 海皇(27歳)
e1 と e3 は同じオブジェクトか:true
e1 と e2 は同じオブジェクトか:false
| ファイル | 作業 | 内容 |
|---|---|---|
ch08/Employee.java |
新規 | 社員を表すクラス(main なし) |
ch08/Main.java |
新規 |
Employee を使うクラス(main あり) |
8.2 クラスを定義する
【便利な使い方】補完で入力する
補完を使っていますか? この章からは、自分で作ったクラスを使います。名前の一部を入力して Ctrl + Space キーを押すと、続きの候補が表示されます。自分で作ったクラスの名前や、そのクラスのフィールド・メソッドも補完できるので、打ち間違いが減り、入力も速くなります。
| 入力して Ctrl + Space | 入るもの |
|---|---|
Emp |
Employee(この章で作るクラス)。↑ ↓ キーで選んで Enter キー |
e1. |
name、age、print() など、e1 で使えるフィールドやメソッドの候補 |
sysout |
System.out.println(); |
くわしくは、第3章の「補完で入力する」で説明しています。
-
srcを右クリックし、新規 → パッケージ でch08パッケージを作ります。 -
ch08パッケージを右クリックし、新規 → クラス で、名前Employeeと入力し、「public static void main(String[] args)」の チェックを入れずに クラスを作ります。 -
緑の行を入力します。
ch08/Employee.javapackage ch08; /** 社員を表す */ public class Employee { String name; int age; /** 一覧に表示するために、名前と年齢を1行にして表示する */ void print() { System.out.println(name + "(" + age + "歳)"); } } -
保存します。
-
もう一度
ch08パッケージを右クリックし、新規 → クラス で、名前Main、「public static void main(String[] args)」に チェックを入れて クラスを作ります。 -
// TODOの行を消し、緑の行を入力します。ch08/Main.javapackage ch08; public class Main { public static void main(String[] args) { Employee e1 = new Employee(); e1.name = "雷禅 海皇"; e1.age = 26; Employee e2 = new Employee(); e2.name = "院照 心愛"; e2.age = 27; e1.print(); e2.print(); } } -
保存して、
Main.javaを実行します。コンソール雷禅 海皇(26歳) 院照 心愛(27歳)
8.3 クラスとオブジェクト
Employee クラスは、「社員は名前(name)と年齢(age)を持ち、表示する処理(print)ができる」ということを定義しています。クラスは、どんなデータと処理を持つかを決めたもの で、クラスを定義しただけでは、社員は1人もいません。
new Employee() と書くと、Employee クラスの定義どおりのデータの入れ物が、メモリの上に1つ作られます。これを オブジェクト(またはインスタンス)といいます。Main では、new を2回書いたので、オブジェクトが2つ作られ、e1 と e2 という変数に入りました。2つのオブジェクトは、それぞれ別の name と age を持っています。
| コード | 意味 |
|---|---|
Employee e1 = new Employee(); |
Employee のオブジェクトを新しく作り、Employee 型の変数 e1 に入れる。クラス名は、そのまま型の名前になる |
e1.name = "雷禅 海皇"; |
e1 のオブジェクトの name に値を入れる。. は「〜の」 |
e1.print(); |
e1 のオブジェクトの print メソッドを呼ぶ。e1 の name と age が表示される |
第4章で学んだ String も、こうしたクラスの1つです。"雷禅 海皇" と書くと String のオブジェクトが作られ、name.length() はそのオブジェクトのメソッドを呼んでいます。
8.4 フィールドとメソッド
フィールド
クラスの { } の直下(メソッドの外)に宣言した変数を フィールド といいます。name と age がフィールドです。
- フィールドはオブジェクトごとに持つ。
e1.ageとe2.ageは別の値 - フィールドは、そのクラスのどのメソッドからも使える(
printメソッドの中で、引数で受け取っていないname・ageを使えている) - 値を入れていないフィールドには、型ごとの初期値が入っている。
intは0、booleanはfalse、Stringなどのクラスの型はnull
static の付かないメソッド
print メソッドには static を付けていません。static の付かないメソッドは、e1.print() のように オブジェクトに対して呼び出し、そのオブジェクトのフィールドを使って動きます。第7章の static なメソッドは、AgeChecker.isValid(age) のようにクラスに対して呼び出し、オブジェクトを作らなくても使えるものでした。
クラスの中で「そのオブジェクト自身のデータ」を使う処理は、static を付けずに書きます。この教材でこの後に作るメソッドは、Main クラスの中のもの以外は static を付けません。
【ご注意】main メソッドからフィールドを直接使えない
Employee クラスに main メソッドを書いて、その中で name と書くと、「非 static フィールド name を static 参照できません」というエラーになります。main は static なので、どのオブジェクトの name なのかが決まらないためです。new でオブジェクトを作り、e1.name のようにオブジェクトを指定して使います。
8.5 コンストラクタと this
new Employee() でオブジェクトを作ってから、name と age を1つずつ入れるのは手間です。オブジェクトを作るときに、同時に値を入れられるようにする仕組みが コンストラクタ です。
-
Employee.javaに、緑の行を書き足します。ch08/Employee.javapackage ch08; /** 社員を表す */ public class Employee { String name; int age; /** 名前と年齢を決めて社員を作る * @param name 名前 * @param age 年齢 */ Employee(String name, int age) { this.name = name; this.age = age; } /** 一覧に表示するために、名前と年齢を1行にして表示する */ void print() { System.out.println(name + "(" + age + "歳)"); } } -
保存します。
Main.javaのnew Employee()の行に赤い「×」が付きます。「コンストラクター Employee() は未定義です」という意味です(理由は後で説明します)。 -
Main.javaのmainメソッドを、緑の行のように書き換えます。ch08/Main.javapackage ch08; public class Main { public static void main(String[] args) { Employee e1 = new Employee("雷禅 海皇", 26); Employee e2 = new Employee("院照 心愛", 27); e1.print(); e2.print(); } } -
保存して実行します。表示は前と同じです。
コンソール雷禅 海皇(26歳) 院照 心愛(27歳)
コンストラクタの形
コンストラクタは、クラス名と同じ名前 で、戻り値の型を書かない メソッドのようなものです。new Employee("雷禅 海皇", 26) と書くと、オブジェクトが作られた直後にコンストラクタが呼ばれ、引数の値がフィールドに入ります。
クラス名(引数) {
フィールドに値を入れる処理
}
this
コンストラクタの引数の名前(name)と、フィールドの名前(name)が同じなので、コンストラクタの中で name と書くと引数のほうを指します。フィールドのほうを指すには、this.name と書きます。this は、「このオブジェクト自身」という意味です。this.name = name; は、「このオブジェクトのフィールド name に、引数の name を入れる」と読みます。
引数の無いコンストラクタ
コンストラクタを1つも書いていないクラスには、引数の無いコンストラクタ(Employee())が自動で用意されます。8.2 で new Employee() と書けたのはこのためです。コンストラクタを1つでも書くと、自動の用意はされなくなるので、new Employee() はエラーになりました。
8.6 参照
Employee e1 = new Employee(…); を実行すると、変数 e1 にはオブジェクトそのものではなく、そのオブジェクトを指す 参照 が入ります。一部の書籍や記事で説明している、参照がメモリ上の番地(アドレス)であるとは、Java の仕様では決められていません(Java 仮想マシン仕様 2.7「Representation of Objects」、英語)。参照の中身をプログラムから見たり、数として計算したりすることもできません。
変数に入っているのがオブジェクトではなく参照であることは、変数を別の変数に代入したときの動きを見るとよく分かります。
-
Main.javaのmainメソッドに、緑の行を書き足します。ch08/Main.javapackage ch08; public class Main { public static void main(String[] args) { Employee e1 = new Employee("雷禅 海皇", 26); Employee e2 = new Employee("院照 心愛", 27); e1.print(); e2.print(); Employee e3 = e1; e3.age = 27; e1.print(); System.out.println("e1 と e3 は同じオブジェクトか:" + (e1 == e3)); System.out.println("e1 と e2 は同じオブジェクトか:" + (e1 == e2)); } } -
保存して実行します。
コンソール雷禅 海皇(26歳) 院照 心愛(27歳) 雷禅 海皇(27歳) e1 と e3 は同じオブジェクトか:true e1 と e2 は同じオブジェクトか:false
e3.age = 27; と書いただけなのに、e1.print() の表示が 27 歳に変わりました。Employee e3 = e1; は、オブジェクトを複製したのではなく、同じオブジェクトの参照を e3 にも入れた からです。e1 と e3 は同じ1つのオブジェクトを指しているので、e1 == e3 は true です。
一方、e1 と e2 は、new を別々に行って作った別のオブジェクトです。たとえ名前と年齢が同じ値だったとしても、e1 == e2 は false です。== は「同じオブジェクトを指しているか」を比べるもので、「中身が同じか」は比べません。第5章で、文字列の比較に == を使ってはいけないと説明したのは、このためです。String もクラスなので、== は中身ではなく参照を比べてしまいます。
【知識メモ】ハッシュコードで同じオブジェクトかを見てみる
参照の中身はプログラムから見られません。ただ、オブジェクトの ハッシュコード という整数を表示すると、2つの変数が同じオブジェクトを指しているかの目安になります。
ハッシュ(ハッシュ値)とは、データから決まった計算で求めた、そのデータの目印になる数のことです。たくさんのデータの中から目的のものを速く探したり、2つのデータが同じかを手早く比べたりするのに使われます。Java では、すべてのオブジェクトが hashCode() というメソッドを持っていて、これを呼ぶとそのオブジェクトのハッシュコードが返ります。Employee のように hashCode() を書き直していないクラスでは、オブジェクトごとに Java が決めた数が返ります。
Main.java の main メソッドの最後に、次の3行を書き足して実行してみましょう。
System.out.println("e1 のハッシュコード:" + e1.hashCode());
System.out.println("e2 のハッシュコード:" + e2.hashCode());
System.out.println("e3 のハッシュコード:" + e3.hashCode());
e1 のハッシュコード:925858445
e2 のハッシュコード:1927950199
e3 のハッシュコード:925858445
Employee e3 = e1; で参照を代入したので、e1 と e3 は同じオブジェクトを指していて、ハッシュコードも同じです。e2 は別のオブジェクトなので、違う値になります。
ただし、ハッシュコードは参照そのものでも、メモリ上の番地でもありません。別のオブジェクトでも、まれに同じ値になることがあります。
このハッシュコードは、第10章 10.3 でもう一度出てきます。println でオブジェクトを表示したときの「クラス名@…」の @ の後ろが、ハッシュコードを16進数で表したものです。
【知識メモ】基本型の変数は値そのものを持つ
int a = 26; int b = a; b = 27; と書いても、a は 26 のままです。基本型(int など)の変数は値そのものを持ち、代入すると値が複製されるからです。参照が入るのは、クラスの型(String、Employee)と配列の変数だけです。
8.7 null と NullPointerException
クラスの型の変数には、「どのオブジェクトも指していない」ことを表す null を入れられます(第5章 5.8)。null の変数に対してフィールドやメソッドを使うと、実行したときに NullPointerException が起きて、プログラムはそこで止まります。実際に起こしてみましょう。
-
Main.javaのmainメソッドの最後に、緑の行を書き足します。ch08/Main.java(main メソッドの最後)System.out.println("e1 と e2 は同じオブジェクトか:" + (e1 == e2)); Employee e4 = null; e4.print(); } } -
保存して実行します。これまでの表示の後に、赤い文字で次のように表示されます。
コンソール(最後の部分)Exception in thread "main" java.lang.NullPointerException: Cannot invoke "ch08.Employee.print()" because "e4" is null at ch08.Main.main(Main.java:18) -
確かめたら、書き足した2行を消して保存します。
この赤い表示は スタックトレース といい、「何のエラーが」「どこで」起きたかを表しています。
| 部分 | 意味 |
|---|---|
java.lang.NullPointerException |
起きたエラーの種類。null に対してメソッドやフィールドを使った |
Cannot invoke "ch08.Employee.print()" because "e4" is null |
エラーの説明。「e4 が null なので、Employee の print() を呼べない」 |
at ch08.Main.main(Main.java:18) |
起きた場所。Main.java の 18 行目(Main クラスの main メソッドの中)。Eclipse ではこの部分がリンクになっていて、クリックするとその行に飛べる |
NullPointerException は、Java で最もよく出会うエラーです。出たときは、スタックトレースの「どの変数が null か」を読み、その変数に値を入れている場所を確かめます。エラーの扱いは、第12章で詳しく学びます。
【知識メモ】Spring入門で出会う NullPointerException
Spring入門では、フィールドに値を入れる処理を Spring に任せる仕組み(DI)を使います。そのための印(@Autowired)を書き忘れると、フィールドが null のままになり、この NullPointerException が起きます。スタックトレースの読み方を知っていれば、原因の場所がすぐに分かります。
8.8 うまくいかないときは
| 症状 | 考えられる原因と対処 |
|---|---|
| 「コンストラクター Employee() は未定義です」 | 引数付きのコンストラクタを書いたので、引数の無い new Employee() が使えなくなっています。new Employee("雷禅 海皇", 26) のように引数を渡してください。 |
| 「コンストラクター Employee(String, int) は未定義です」 | コンストラクタをまだ書いていないか、引数の型や数が違います。Employee.java のコンストラクタと見比べてください。 |
| 「非 static フィールド name を static 参照できません」「型 Employee の非 static メソッド print() を static 参照することはできません」 |
main メソッドの中で、オブジェクトを指定せずにフィールドやメソッドを使っています。new で作ったオブジェクトの変数を付けて、e1.name・e1.print() のように書いてください。 |
| 「Employee を型に解決できません」 |
Employee クラスがまだ無いか、別のパッケージに作っています。Main と同じ ch08 パッケージにあるか確かめてください。 |
| コンストラクタに「メソッドの戻りの型がありません」 | コンストラクタの名前がクラス名と違います(employee のように小文字になっているなど)。クラス名と同じ Employee にしてください。 |
NullPointerException が出る |
null の変数のメソッドやフィールドを使っています。スタックトレースの because "〇〇" is null の変数に、値を入れているかを確かめてください。詳しくは 付録A「よくある実行時のエラー」 を見てください。 |
ほかのエラーは、付録A「よくあるコンパイルエラー」・付録A「よくある実行時のエラー」 も参考にしてください。
8.9 まとめと確認問題
まとめ
-
クラス は、どんなデータ(フィールド)と処理(メソッド)を持つかを定義したもの。
new クラス名(…)で オブジェクト を作る。1つのクラスから、いくつでもオブジェクトを作れる - フィールドとメソッドは
変数.名前で使う。staticの付かないメソッドは、オブジェクトに対して呼び出し、そのオブジェクトのフィールドを使う -
コンストラクタ は、クラス名と同じ名前で戻り値の型を書かない。
newのときに呼ばれ、フィールドに値を入れる。this.nameは「このオブジェクトのフィールドname」 - コンストラクタを1つも書かなければ、引数の無いコンストラクタが自動で用意される。1つでも書くと、用意されなくなる
- クラスの型の変数に入っているのは 参照。代入すると同じオブジェクトを指す。
==は同じオブジェクトかどうかを比べる -
nullの変数に対してメソッドやフィールドを使うと NullPointerException。スタックトレースで、何が・どこで起きたかを読む
この章で使った書き方は、付録B「文法早見表」の「メソッドとクラス」 にまとめています。
確認問題
問1 部署を表すクラス Department を、部署名のフィールド String deptName だけを持つクラスとして定義してください。
解答を見る
public class Department { String deptName; }
問2 コンストラクタが、ふつうのメソッドと違う点を2つ答えてください。
解答を見る
名前がクラス名と同じであることと、戻り値の型を書かないことです。new でオブジェクトを作るときに呼ばれます。
問3 this.name = name; の this.name と name は、それぞれ何を指していますか。
解答を見る
this.name はこのオブジェクトのフィールド name、name はコンストラクタ(メソッド)の引数の name です。
問4 次のコードを実行した後、a.age はいくつですか。Employee a = new Employee("林檎 星飛", 24);Employee b = a;b.age = 25;
解答を見る
25 です。a と b は同じオブジェクトを指しているので、b.age を変えると a.age も変わります。
問5 Employee e = null; e.print(); を実行するとどうなりますか。
解答を見る
NullPointerException が起きて、プログラムが止まります。e がどのオブジェクトも指していないのに、メソッドを呼んだためです。

