SJ blog
beginner
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

コンストラクタはなぜ存在するのか — newしたときに何が起きているか

コンストラクタの存在理由、なぜクラス名と同じ名前か、なぜ戻り値がないのか、デフォルトコンストラクタはなぜ自動生成されるのかを根本から解説。

一言結論

コンストラクタはオブジェクトを「使える状態」にするための初期化専用メソッド。クラス名と同じ・戻り値なし、という見た目の特殊さにはそれぞれ明確な理由がある。

「なぜコンストラクタが必要なのか」を考える前に

Javaでオブジェクトを作るとき、こう書く:

Person p = new Person("田中", 25);

この new Person("田中", 25) の部分がコンストラクタの呼び出しだ。でも「コンストラクタって何?」と聞かれると、「クラスと同じ名前のメソッドみたいなもの」と答える人が多い。

それは正しいが、なぜ存在するのかが抜けている。

newしたときに何が起きているのか

new Person("田中", 25) を実行すると、JVMの中では:

1. Personオブジェクトのためにメモリを確保する
2. フィールドをデフォルト値で初期化する(int → 0、String → null など)
3. コンストラクタを呼び出す
4. コンストラクタの中のコードが実行される
5. 完成したオブジェクトの参照を返す

ステップ2で「デフォルト値で初期化」される。つまりnewした直後のオブジェクトはフィールドが全部null0だ。これでは使い物にならない場合がほとんど

public class Person {
    String name;
    int age;
    // コンストラクタを書かなかったら?
}

Person p = new Person();
System.out.println(p.name);  // null
System.out.println(p.age);   // 0

namenullのPersonオブジェクトで名前を表示しようとするとクラッシュする。

コンストラクタの役割は「オブジェクトを使える状態にする」ことだ。

public class Person {
    String name;
    int age;

    // コンストラクタ: オブジェクトを使える状態にする
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Person p = new Person("田中", 25);
System.out.println(p.name);  // 田中(ちゃんと入っている)
System.out.println(p.age);   // 25

newしてすぐに使える状態を保証するのがコンストラクタの仕事だ。

なぜクラス名と同じ名前なのか

コンストラクタの見た目は変わっている:

public class Person {
    public Person(String name, int age) {  // ← クラス名と同じ
        this.name = name;
        this.age = age;
    }
}

通常のメソッドとの違い:

// 通常のメソッド
public void greet() { ... }

// コンストラクタ
public Person() { ... }

なぜクラス名と同じにするのか?

答えはシンプルで、コンパイラが「これはコンストラクタだ」と識別できるようにするため だ。

コンパイラはクラス定義の中を見て、「クラス名と同じ名前のメソッド」を見つけたらコンストラクタとして扱う。戻り値の型も書かれていない。これがコンストラクタの目印だ。

もし普通のメソッド名を使ったらどうか。たとえばinit()という名前にしたとする。コンパイラはそれがコンストラクタなのか、ただの初期化メソッドなのかを判断できない。new時に自動で呼び出す処理と区別できなくなる。

クラス名を使うという規則は「分かりやすさ」と「コンパイラの識別のしやすさ」の両方を解決している。

なぜ戻り値の型がないのか

コンストラクタにはvoidも型名も書かない。なぜか?

答えは「返す型が決まっているから」だ。

new Person(...) を呼び出したら、必ずPerson型のオブジェクトが返される。これ以外はありえない。型が自明なので、わざわざ書く必要がない。

もし戻り値を書いてしまうと:

// これはコンストラクタではなく、ただのメソッドになる
public Person Person(String name) {  // Person型を返すメソッド
    return new Person(name, 0);
}

public Person Person(...) と書いた時点で、コンパイラはこれをコンストラクタではなく「Person型を返す通常のメソッド」として解釈する。new時に自動で呼ばれることはない。

「戻り値の型なし」と「クラス名と同じ」の2つが揃って初めてコンストラクタとして認識される。

デフォルトコンストラクタはなぜ自動生成されるのか

コンストラクタを1つも書かなかった場合、Javaは自動的に「引数なしのコンストラクタ」を追加する:

public class Dog {
    String name;
    // コンストラクタを何も書かない
}

// でもこれが動く
Dog d = new Dog();  // なぜ?

これは「デフォルトコンストラクタ」と呼ばれ、コンパイル時に自動生成される:

// コンパイラが自動で追加する(実際にはソースには見えない)
public Dog() {
    super();  // 親クラス(Object)のコンストラクタを呼ぶ
}

なぜ自動生成するのか?

「コンストラクタを書かなかったらnewできない」のは不便すぎるからだ。シンプルなデータクラスを作るたびにコンストラクタを書かされたら手間だ。デフォルトコンストラクタがあれば、コンストラクタが不要な場合は書かなくていい。

ただし、引数ありのコンストラクタを1つでも書いたら、デフォルトコンストラクタは自動生成されなくなる:

public class Cat {
    String name;

    public Cat(String name) {
        this.name = name;
    }
}

Cat c1 = new Cat("ミケ");  // OK
Cat c2 = new Cat();        // エラー! 引数なしコンストラクタは存在しない

これは「引数ありを定義したということは、引数なしで作らせたくないという意思表示だ」と解釈するから。必要なら明示的に書く:

public class Cat {
    String name;

    public Cat(String name) { this.name = name; }
    public Cat() { this.name = "名無し"; }  // 明示的に書けば使える
}

コンストラクタのオーバーロード

コンストラクタは複数定義できる。引数の型や数が違えばいい:

public class Rectangle {
    int width;
    int height;

    // 幅と高さを別々に指定
    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    // 正方形(幅=高さ)
    public Rectangle(int size) {
        this(size, size);  // ← this()で別のコンストラクタを呼べる
    }
}

this(size, size) は「同じクラスの別のコンストラクタを呼ぶ」という構文だ。コードの重複を避けるのに使う。必ずコンストラクタの1行目に書かなければならないというルールがある。

まとめ

コンストラクタの役割  = オブジェクトを「使える状態」にする初期化処理
クラス名と同じ名前   = コンパイラが「コンストラクタだ」と識別できるように
戻り値の型なし      = 返すのは常にそのクラスのオブジェクト(自明なので書かない)
デフォルトコンストラクタ = コンストラクタ未定義時にコンパイラが自動生成(引数ありを定義すると消える)
this()            = 同クラスの別コンストラクタへの委譲(1行目限定)

newした瞬間にオブジェクトが「半完成品」の状態で渡されてくるより、コンストラクタで完成状態にして渡してもらう方が安全だ。コンストラクタはその保証の仕組みだ。