Skip to content
Playground

1.クラスとインスタンス

学習目標

  • クラスとインスタンスの関係を説明できる
  • フィールド・コンストラクター・メソッドを持つクラスを定義できる
  • new でインスタンスを生成し、フィールドの読み書きとメソッド呼び出しができる

書籍ならタイトル・価格・在庫、ユーザーなら名前・メールアドレス・年齢、注文なら商品・数量・日時。プログラムはこのような複数の属性を持つデータを扱います。Java には intdoubleString といった型が組み込まれていますが、これらが表すのは単一の値です。「タイトルと価格と在庫を持つ書籍」のように複数の属性を 1 つに束ねた型は、組み込み型には存在しません。

クラス は、こうした新しい型を自分で定義する仕組みです。データ(フィールド)と処理(メソッド)をまとめて 1 つの型として扱えます。

クラスは class キーワードで定義し、クラスが持つデータを フィールド として データ型 フィールド名; の形で並べます。

class クラス名 {
データ型 フィールド名;
...
}
Person.java
public class Person {
String name;
int age;
}

これで Person という新しい型を定義しました。Personname(文字列)と age(整数)の 2 つのフィールドを持ちます。public を付けると、このクラスは別のパッケージからも参照できます。

新しく作った型は、intString と同じように扱えます。Person 型の変数の宣言や、Person[] のような配列の宣言が可能です。

クラスを定義しただけでは値を扱えません。Person 型の値を使うには、Person クラスから具体的な実体を生成する必要があります。

2-1. クラスとインスタンスの関係

Section titled “2-1. クラスとインスタンスの関係”

クラスから生成された実体を インスタンス と呼びます。オブジェクト も同義の用語です。

クラスは型の設計図、インスタンスはその設計図から生成される実体です。

Person クラスとインスタンスの関係

設計図は 1 つでも、そこから生成するインスタンスはいくつでも作れます。同じ Person クラスから生成された 2 つのインスタンスは、それぞれが独立した nameage の値を持ちます。

クラスからインスタンスを生成するには new 演算子を使います。

new クラス名()

Person を利用するコードは別のクラスに記述します。

Main.java
public class Main {
public static void main(String[] args) {
Person p = new Person(); // ① インスタンスを生成し、参照を p に代入
p.name = "田中"; // ② インスタンスの name フィールドに代入
p.age = 25; // ③ インスタンスの age フィールドに代入
System.out.println(p.name);
System.out.println(p.age);
}
}
田中
25

new Person() で、Person 型のインスタンスが 1 つ生成されます。

以降のコード例では、Main.javamain メソッドの中身だけを示します。

同じクラスから複数のインスタンスを生成でき、それぞれが独立したフィールドの値を持ちます。

Main.java
Person p1 = new Person();
p1.name = "田中";
p1.age = 25;
Person p2 = new Person();
p2.name = "鈴木";
p2.age = 30;
p1.name = "中村";
System.out.println(p1.name); // 中村
System.out.println(p2.name); // 鈴木

new Person() を 2 回実行すると、別々のオブジェクトがメモリ上に配置されます。p1p2 はそれぞれ別のオブジェクトを参照するため、p1.name を変更しても p2.name には影響しません。

ここまでの例では、インスタンスを生成してから 1 つずつフィールドに代入していました。

Main.java
Person p = new Person();
p.name = "田中";
p.age = 25;

この書き方では、new Person() の実行から各フィールドへの代入が完了するまでの間、name = nullage = 0 のフィールドを持つ不完全な Person インスタンスが存在します。インスタンスを完成させる責任は呼び出し側のコードにあります。

コンストラクター は、インスタンスの生成と初期化を 1 つの操作にまとめる仕組みです。

コンストラクターはクラス名と同じ名前で定義します。戻り値の型は書きません。

class クラス名 {
クラス名(引数) {
初期化の処理
}
}
Person.java
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
Main.java
Person p = new Person("田中", 25);
System.out.println(p.name); // 田中
System.out.println(p.age); // 25

new Person("田中", 25) を実行したときの流れを追います。

  1. Person 型のオブジェクトがメモリ上に生成される
  2. コンストラクターが呼び出され、引数 name"田中"age25 が渡る
  3. コンストラクターの中で this.name = namethis.age = age が実行され、インスタンスのフィールドが初期化される
  4. 完成したオブジェクトへの参照が返り、変数 p に代入される

コンストラクターの中の this.name = name は、インスタンス自身のフィールドに引数の値を代入する文です。

引数名とフィールド名が同じ name なので、どちらを指すかを区別する必要があります。this は、コンストラクターやメソッドが呼び出されている対象のインスタンスを指すキーワードです。new Person("田中", 25) の場合、コンストラクター内の this はこの new で生成された Person のインスタンスを指します。

表記意味
this.nameインスタンスのフィールド name
nameコンストラクターの引数 name

this. を書いた場合と書き忘れた場合の挙動を比較します。

Person.java
public Person(String name, int age) {
this.name = name;
this.age = age;
}
Main.java
Person p = new Person("田中", 25);
System.out.println(p.name); // 田中
System.out.println(p.age); // 25

左辺の this.name はインスタンスのフィールド、右辺の name は引数を指すため、引数の値がフィールドに代入されます。

3-3. デフォルトコンストラクター

Section titled “3-3. デフォルトコンストラクター”

インスタンスの生成 では、コンストラクターを定義していない Person に対して new Person() のように引数なしで生成できました。これは Java が デフォルトコンストラクター(引数なしで何もしないコンストラクター)を暗黙に提供していたためです。コンストラクターを 1 つでも自分で定義すると、このデフォルトは無効になります。

public Person(String name, int age) を定義した Person に対して、new Person() を書くとコンパイルエラーになります。

Main.java
Person p = new Person(); // コンパイルエラー:引数の数が合わない

これにより、nameage を渡さずに Person を生成する方法はなくなります。フィールドが初期値のまま残ることを、Java の仕組みが防ぎます。

クラスには、データに加えて メソッド(処理)も定義できます。

メソッドはクラスの中で次の形で定義します。

class クラス名 {
戻り値の型 メソッド名(引数) {
処理
return 戻り値;
}
}

戻り値がない場合は型として void を指定し、return 文は不要です。

例として、自己紹介を表示する introduce メソッドを Person に追加します。

Person.java
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
void introduce() {
System.out.println("私は" + name + "" + age + "歳です");
}
}

メソッド内ではフィールドに直接アクセスできます。引数と名前が衝突しない限り、this. を付けないのが Java の慣習です。

メソッドはインスタンスに対して呼び出します。

Main.java
Person p1 = new Person("田中", 25);
Person p2 = new Person("鈴木", 30);
p1.introduce();
p2.introduce();
私は田中、25歳です
私は鈴木、30歳です

p1.introduce() は「p1 のインスタンスに対して introduce メソッドを呼び出す」という意味です。メソッド内で参照される nameagep1 のフィールドの値であるため、出力には 田中25 が現れます。p2.introduce() も同様に、p2 のフィールドが参照されます。