Java道|オブジェクト生成とコンストラクタの関係

隊士は生まれた瞬間に、初期状態が整えられる。
コンストラクタを理解すると、オブジェクト生成の流れがはっきり見えてくる。

これまで、クラスの中にはフィールドやメソッドを用意できることを学んできました。

フィールドは、オブジェクトが持つ情報です。
メソッドは、オブジェクトが行う処理です。

鬼滅の刃でたとえると、鬼殺隊士という設計図の中に、体力や呼吸の力といった情報を持たせ、状態を表示する動きも用意しておくようなものです。

ただ、実際にオブジェクトを作るときには、ひとつ大切なことがあります。

それは、作られた直後のオブジェクトがどんな状態になっているのか、という点です。

何も初期設定されていないままだと、その隊士がどんな状態で生まれたのかが分かりにくくなります。

そこで使うのが、コンストラクタです。

コンストラクタは、オブジェクトが作られた瞬間に自動で動く特別なしくみです。

鬼滅の刃でたとえると、新しい隊士が登場した瞬間に、体力や呼吸の力の初期値を整え、登場の合図を出すような処理です。

コンストラクタはオブジェクト作成時に自動で動く

コンストラクタは、クラスの中に書く特別な処理です。

見た目はメソッドに似ています。
しかし、役割はメソッドとは違います。

コンストラクタの一番大きな特徴は、オブジェクトが作成されたときに自動で実行されることです。

普通のメソッドは、必要なときに自分で呼び出します。

たとえば、状態を表示したいときは、次のように show() を呼び出します。

mizuki.show();

これは、自分で呼び出している処理です。

一方で、コンストラクタは次のように new を使ってオブジェクトを作ったときに、自動で実行されます。

DemonSlayer mizuki = new DemonSlayer();

この1行で、DemonSlayer オブジェクトが作られます。
そして、その瞬間に DemonSlayer クラスのコンストラクタが動きます。

鬼滅の刃でたとえると、新しい隊士が任務地に現れた瞬間、最初の体力や呼吸の力を整える準備処理が自動で行われるようなものです。

鬼滅の刃でたとえると、登場時の初期設定

コンストラクタは、隊士が生まれた瞬間の初期設定だと考えると分かりやすいです。

たとえば、新しい隊士を1人作ったとします。

そのとき、何も決まっていない状態では困ります。

体力は最初に 0 にしておく。
呼吸の力は最初に 0.0 にしておく。
隊士を作成しました、と表示して登場を知らせる。

こうした処理を、オブジェクト作成時に自動で行えるのがコンストラクタです。

Javaのしくみ鬼滅の刃でたとえると
クラス鬼殺隊士の設計図
オブジェクト生成新しい隊士が登場する
コンストラクタ登場時に初期状態を整える処理
初期化体力や呼吸の力を最初の値にする

コンストラクタは、あとから任意に使う技というより、隊士が登場した瞬間に必ず発動する準備処理です。

この感覚を持つと、メソッドとの違いも見えやすくなります。

↓クリックすると拡大表示されます。

コンストラクタはメソッドに似ているけれど別物

コンストラクタは、見た目がメソッドに少し似ています。

しかし、次の2つが大きく違います。

1つ目は、名前が必ずクラス名と同じであることです。
2つ目は、戻り値の型を書かないことです。

たとえば、DemonSlayer クラスのコンストラクタなら、名前も DemonSlayer になります。

public DemonSlayer()
{
    stamina = 0;
    breathingEnergy = 0.0;
    System.out.println("隊士を作成しました。");
}

ここで注目するところは、次の2つです。

注目点内容
名前クラス名と同じ DemonSlayer
戻り値void も int も String も書かない

普通のメソッドなら、戻り値がない場合は void を書きます。

public void show()

しかし、コンストラクタには void を書きません。

ここを間違えて public void DemonSlayer() のように書くと、それはコンストラクタではなく、DemonSlayer という名前の普通のメソッドとして扱われてしまいます。

項目メソッドコンストラクタ
名前自由につけられるクラス名と同じ
戻り値void や int などを書く書かない
実行タイミング呼び出したときnew で作成したときに自動
主な役割必要な処理を行う作成直後の初期設定を行う

この違いを押さえておくと、コンストラクタをメソッドと混同しにくくなります。

コンストラクタの役割は初期化

コンストラクタの主な役割は、初期化です。

初期化とは、オブジェクトが作られた直後に、最初の状態を整えることです。

たとえば、DemonSlayer クラスなら、次のような初期設定が考えられます。

初期化するもの初期値意味
stamina0体力の初期値
breathingEnergy0.0呼吸の力の初期値

鬼滅の刃でたとえると、隊士が登場した直後に、まだ任務へ出る前の基本状態を整えておくようなものです。

何も決まっていない隊士ではなく、最低限の状態を持った隊士として生まれる。
これがコンストラクタの大切な役割です。

さらに、初期化だけでなく、登場メッセージのような処理も書けます。

System.out.println("隊士を作成しました。");

このように、コンストラクタには「オブジェクトを作った瞬間に行いたい処理」をまとめて書けます。

コンストラクタの動きの確認

実際のプログラムで、コンストラクタの動きを確認していきましょう。

この記事では、具体的な例示プログラムとして Sample4.java だけを使います。

ファイル名:Sample4.java

class DemonSlayer
{
    private int stamina;
    private double breathingEnergy;

    public DemonSlayer()
    {
        stamina = 0;
        breathingEnergy = 0.0;
        System.out.println("隊士を作成しました。");
    }

    public void show()
    {
        System.out.println("体力は" + stamina + "です。");
        System.out.println("呼吸の力は" + breathingEnergy + "です。");
    }
}

class Sample4
{
    public static void main(String[] args)
    {
        DemonSlayer mizuki = new DemonSlayer();
        mizuki.show();
    }
}

このプログラムでは、DemonSlayer クラスの中にコンストラクタがあります。

public DemonSlayer()
{
    stamina = 0;
    breathingEnergy = 0.0;
    System.out.println("隊士を作成しました。");
}

この部分がコンストラクタです。

名前は DemonSlayer です。
クラス名と同じです。
戻り値の型は書かれていません。

new と一緒にコンストラクタが動く

main メソッドの中では、次の1行でオブジェクトを作っています。

DemonSlayer mizuki = new DemonSlayer();

この1行では、いくつかのことが連続して起きています。

順番起きていること
1DemonSlayer クラスをもとにオブジェクトを作る
2new DemonSlayer() によってコンストラクタが自動で実行される
3stamina に 0 が入る
4breathingEnergy に 0.0 が入る
5隊士を作成しました。と表示される
6作られたオブジェクトを mizuki で扱えるようになる

ここで大切なのは、コンストラクタを別の行で呼び出していないことです。

たとえば、次のように書いているわけではありません。

mizuki.DemonSlayer();

コンストラクタは、new DemonSlayer() と書いた時点で自動的に動きます。

鬼滅の刃でたとえると、隊士が登場する瞬間に、初期状態を整える儀式が自動で発動するようなものです。

show() を呼ぶと初期化された値が見える

オブジェクトを作ったあと、次のように show() を呼び出しています。

mizuki.show();

show() は普通のメソッドです。

public void show()
{
    System.out.println("体力は" + stamina + "です。");
    System.out.println("呼吸の力は" + breathingEnergy + "です。");
}

このメソッドは、現在の stamina と breathingEnergy を表示します。

コンストラクタで次のように初期化しているため、

stamina = 0;
breathingEnergy = 0.0;

show() を呼ぶと、初期化された値が表示されます。

実行結果は次のようになります。

隊士を作成しました。
体力は0です。
呼吸の力は0.0です。

この結果から、次の流れが分かります。

表示どこで起きたか
隊士を作成しました。コンストラクタ
体力は0です。show() メソッド
呼吸の力は0.0です。show() メソッド

つまり、オブジェクトを作ったときにコンストラクタが先に動き、そのあとで show() が呼ばれています。

図:new でオブジェクトを作るとコンストラクタが動く

↓クリックすると拡大表示されます。

この図が示していること

この図では、new DemonSlayer() によってオブジェクトが作られる瞬間に、コンストラクタ public DemonSlayer() が自動で実行される流れを表しています。

コンストラクタの中で stamina は 0、breathingEnergy は 0.0 に設定されます。
その結果、右側の mizuki オブジェクトは、初期値を持った状態で完成します。

ここから分かるのは、コンストラクタはあとから手動で呼ぶ処理ではなく、オブジェクト生成とセットで動く初期化処理だということです。

コンストラクタは自分で自由に呼び出すものではない

メソッドは、自分で好きなタイミングに呼び出せます。

たとえば、show() は次のように呼び出します。

mizuki.show();

必要なときに何度でも呼び出せます。

しかし、コンストラクタはそのように使うものではありません。

コンストラクタは、new によってオブジェクトを作るときに自動で動きます。

種類呼び出し方タイミング
コンストラクタnew DemonSlayer() と一緒に動くオブジェクト作成時
メソッドmizuki.show() のように呼ぶ必要なとき

そのため、コンストラクタの中には、オブジェクトが最初に持つべき状態を書きます。

今回なら、体力を 0 にする、呼吸の力を 0.0 にする、という処理が自然です。

鬼滅の刃でたとえると、隊士が登場した瞬間にだけ行う初期準備です。
任務中に何度も自由に使う通常の技とは、役割が違います。

フィールドにはもともと初期値がある

ここで、フィールドの初期値についても整理しておきましょう。

Javaでは、フィールドには型に応じた初期値が自動で入ります。

初期値
booleanfalse
char\u0000
整数型0
浮動小数点型0.0
参照型null

今回の DemonSlayer クラスでは、フィールドに int と double を使っています。

private int stamina;
private double breathingEnergy;

int 型の stamina は、何もしなくても初期値として 0 を持ちます。
double 型の breathingEnergy は、何もしなくても初期値として 0.0 を持ちます。

では、コンストラクタでわざわざ次のように書く意味がないのかというと、そうではありません。

stamina = 0;
breathingEnergy = 0.0;

コンストラクタに書くことで、初期化の意図がはっきりします。

このクラスでは、作成時に体力を 0、呼吸の力を 0.0 にするのだと、コードを読む人に伝わります。

また、今回のようにメッセージを表示する処理も一緒に書けます。

System.out.println("隊士を作成しました。");

つまり、自動初期値があることと、コンストラクタで初期化を書くことは、少し役割が違います。

自動初期値コンストラクタでの初期化
Javaが型に応じて自動で入れる作成者が意図を持って初期状態を決める
最低限の初期状態明示的な初期設定
メッセージなどの処理はできない初期化以外の処理も書ける

コンストラクタは作られた直後の状態を保証しやすい

コンストラクタの大きな価値は、オブジェクトが作られた直後の状態を整えやすいことです。

コンストラクタがなければ、オブジェクトを作ったあとに必要な設定を手動で行う必要があります。

すると、次のような問題が起きるかもしれません。

起こりうる問題内容
設定し忘れ体力や呼吸の力を設定し忘れる
状態があいまい作った直後の値が分かりにくい
処理が散らばる初期設定がいろいろな場所に分かれる
読みにくいどこで初期状態が決まるのか追いにくい

コンストラクタを使えば、オブジェクトを作った瞬間に初期化処理が必ず実行されます。

鬼滅の刃でたとえると、新しい隊士が登場するときに、最低限の身支度や状態確認が必ず行われるようなものです。

この「作られた直後の安心感」を作れることが、コンストラクタの強みです。

メソッドとの役割の違いを整理する

ここで、コンストラクタとメソッドの違いをもう一度整理しておきます。

項目コンストラクタメソッド
主な役割作成時の初期化必要な処理の実行
名前クラス名と同じ自由につけられる
戻り値書かないvoid や int などを書く
実行タイミングnew と一緒に自動呼び出したとき
DemonSlayer()show()

Sample4.java では、次のように考えます。

コード種類役割
public DemonSlayer()コンストラクタ作成時に体力と呼吸の力を初期化する
public void show()メソッド現在の状態を表示する

DemonSlayer() は、オブジェクトを作るときに自動で動きます。
show() は、オブジェクトを作ったあと、必要なタイミングで呼び出します。

この違いが分かると、クラスの中で何をコンストラクタに書き、何をメソッドに書くべきかが見えやすくなります。

図:コンストラクタとメソッドの違い

↓クリックすると拡大表示されます。

この図が示していること

この図では、コンストラクタとメソッドの違いを比較しています。

コンストラクタはクラス名と同じ名前で、戻り値を書きません。
そして、new でオブジェクトを作ったときに自動で実行されます。

一方、メソッドは自由な名前をつけられ、void などの戻り値の型を書きます。
そして、必要なタイミングで自分で呼び出します。

ここから分かるのは、コンストラクタは作成時の初期化、メソッドは必要な処理の実行というように、役割が分かれていることです。

コンストラクタを学ぶと次の内容が理解しやすくなる

コンストラクタの基本が分かると、この先に出てくる内容も理解しやすくなります。

特に、次のような内容につながります。

次に学ぶ内容コンストラクタとのつながり
引数つきコンストラクタ作成時に値を渡して初期化する
thisフィールドと引数を区別するときに使う
カプセル化初期状態を安全に整える設計につながる
オブジェクトの状態管理作成直後の状態をはっきりさせる

今回の Sample4.java では、引数のないコンストラクタを使いました。

public DemonSlayer()

この形では、いつも同じ初期値でオブジェクトが作られます。

次の段階では、オブジェクトを作るときに名前や体力を渡して、最初から具体的な値を持たせる形も学べます。

鬼滅の刃でたとえると、今回は「隊士を作ったら体力0、呼吸の力0.0で登場する」という基本形です。
次は「隊士を作るときに、名前や初期体力も一緒に登録する」という形へ進んでいくイメージです。

いちばん大事な感覚

オブジェクト生成とコンストラクタの関係で大切なのは、次の感覚です。

ポイント内容
コンストラクタクラスの中に書く特別な処理
名前クラス名と同じ
戻り値書かない
実行タイミングnew でオブジェクトを作ったときに自動実行
主な役割フィールドの初期化など、作成直後の準備
メソッドとの違いメソッドは必要なときに自分で呼び出す

鬼滅の刃でたとえると、コンストラクタは隊士が登場した瞬間に発動する初期準備です。

体力を整える。
呼吸の力を整える。
登場したことを知らせる。

こうした処理を、オブジェクト生成時に自動で行ってくれます。

この感覚がつかめると、コンストラクタはただの特別な文法ではなく、オブジェクトを正しい状態で生み出すための大切な仕組みとして見えてきます。