Javaコンストラクタとは?基本構文から実務活用まで徹底解説

Mirai

Javaのコンストラクタって、メソッドと何が違うの?どうやって使えばいいのかよくわからない…。

Zetto

コンストラクタはオブジェクトを作るときに自動で呼ばれる特別な仕組みだね。メソッドとの違いをちゃんと押さえると、Javaのクラス設計がぐっとクリアになるよ!

この記事では、Javaのコンストラクタの基本構文から実務での活用パターンまで解説します。

Zettoのライタープロフィール

本記事の専門性
現役エンジニアのZettoです。Java GoldとJava Silverの資格を保有しており、Javaを使ったWebアプリのフリーランス案件(インフラ設備・在庫管理・医療機器関連など)を複数経験しています。

コンストラクタの仕組みをあいまいなままにすると、オブジェクトの初期化が場当たり的になり、バグや設計の崩れにつながります。

この記事を読めば、コンストラクタの役割・種類・this()やsuper()の使い方・実務での活用パターンまで一通り理解できます。

ぜひ参考にしてみてください。

目次

コンストラクタとは何か?メソッドとの違いから理解する

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

この章では、Javaのコンストラクタの基本的な役割とメソッドとの違いを解説します。

  • コンストラクタの役割と仕組み
  • コンストラクタとメソッドの3つの違い

順番に見ていきましょう。

コンストラクタの役割と仕組み

コンストラクタとは、オブジェクトが生成されるタイミングで自動的に呼ばれる特別な処理のことです。

newキーワードを使ってオブジェクトを作ると、コンストラクタが実行されます。主な役割は「フィールド(クラスが持つ変数)の初期値を設定すること」です。

たとえば、Userクラスにnameageというフィールドがあるとします。コンストラクタを使うと、オブジェクトを作る瞬間に値をセットできます。

public class User {
    String name;
    int age;

    // コンストラクタ
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

このクラスを使うときはこうなります。

User user = new User("Zetto", 30);

new User("Zetto", 30)を実行した瞬間にコンストラクタが呼ばれて、nameに「Zetto」、ageに30がセットされます。

コンストラクタがない場合、フィールドの初期化をあちこちのメソッドで行うことになります。初期化漏れやバグが起きやすくなるため、コンストラクタで一元管理するのが基本的な考え方です。

コンストラクタとメソッドの3つの違い

コンストラクタとメソッドは見た目が似ていますが、仕組みは別物です。

違いを整理すると、以下の通りです。

比較項目コンストラクタメソッド
名前クラス名と完全に同じ任意の名前をつけられる
戻り値なし(voidも書かない)必ず指定する(void含む)
呼び出しタイミングnewで自動実行される明示的に呼び出す

この3点がコンストラクタとメソッドの決定的な違いです。

特に注意したいのが「戻り値の型」です。コンストラクタにはvoidすら書きません。誤ってvoidと書いてしまうと、Javaはそれをメソッドとして判断します。

// NG:これはコンストラクタではなくメソッドとして扱われる
public void User() {
    // 処理
}

僕も過去に現場でこのミスに出くわしたとき、「なんでコンストラクタが呼ばれないんだろう」と30分以上悩んだ記憶があります。知っていれば一瞬で解決できる話ですが、知らないとかなり詰まるんですよね。

Zetto

コンストラクタとメソッドの違い、特に「戻り値を書かない」という点は最初に混乱しやすいポイントです。この違いを押さえておくと、コンパイルエラーで詰まる時間がぐっと減ります。

Javaコンストラクタの書き方と種類

Javaコンストラクタの書き方

この章では、コンストラクタの具体的な書き方と、よく使われる種類を解説します。

  • デフォルトコンストラクタの仕組みと自動生成のルール
  • 引数ありコンストラクタの書き方
  • コンストラクタのオーバーロードで柔軟な初期化を実現する
  • アクセス修飾子をコンストラクタに設定する方法

1つずつ見ていきましょう。

デフォルトコンストラクタの仕組みと自動生成のルール

デフォルトコンストラクタとは、引数も処理も何も書いていない空のコンストラクタです。

実は、コンストラクタを1つも書かないクラスには、Javaコンパイラが自動でデフォルトコンストラクタを追加します。

public class Sample {
    // コンストラクタを何も書かなくてもOK
}

Sample s = new Sample(); // 自動生成されたデフォルトコンストラクタのおかげで動く

ただし、注意点があります。引数ありのコンストラクタを1つでも定義した場合、デフォルトコンストラクタは自動生成されなくなります。

public class User {
    String name;

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

// コンパイルエラー:デフォルトコンストラクタが存在しないため
User u = new User();

「コンストラクタを定義したのに引数なしで呼ぶとエラーが出る」という混乱は、このルールを知らないと起きがちです。必要に応じて引数なしコンストラクタも明示的に定義しましょう。

引数ありコンストラクタの書き方

引数ありコンストラクタは、オブジェクト生成時に値を渡して初期化できる書き方です。

public class Product {
    String name;
    int price;

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }
}

ここで重要なのがthis(ディス)です。this.nameはフィールドのnameを、引数のnameだけはコンストラクタに渡された値を指します。引数名とフィールド名が同じ場合はthisを使って区別します。

引数名をnnmのように略す書き方もできますが、Javaの現場ではthisを使うスタイルが一般的です。コードを読む人に意図が伝わりやすくなります。

コンストラクタのオーバーロードで柔軟な初期化を実現する

コンストラクタは、引数の数や型が違えば複数定義できます。これを「オーバーロード(多重定義)」と呼びます。

public class User {
    String name;
    int age;

    // 引数なし:デフォルト値をセット
    public User() {
        this.name = "名無し";
        this.age = 0;
    }

    // nameだけ受け取る
    public User(String name) {
        this.name = name;
        this.age = 0;
    }

    // nameとage両方受け取る
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

これにより、使う側は状況に合わせてコンストラクタを選べます。

オーバーロードの基本ルールは以下の通りです。

  • 引数の数が違えばOKUser()User(String name)は共存できる
  • 引数の型が違えばOKUser(int id)User(String name)は共存できる
  • 引数の数・型が同じはNG:コンパイルエラーになる

アクセス修飾子をコンストラクタに設定する方法

コンストラクタにもpublicprotectedprivate・なし(パッケージプライベート)の修飾子を設定できます。

使い分けはこのようになります。

  • public:どのクラスからでもオブジェクトを生成できる(最もよく使う)
  • protected:同じパッケージまたはサブクラスからのみ生成できる
  • なし:同じパッケージ内からのみ生成できる
  • private:外部からのオブジェクト生成を禁止する

特にprivateコンストラクタは、シングルトン(そのクラスのインスタンスを1つしか作れない設計パターン)を実装するときに使います。

public class Config {
    private static Config instance;

    // 外から new Config() できないようにする
    private Config() {}

    public static Config getInstance() {
        if (instance == null) {
            instance = new Config();
        }
        return instance;
    }
}

何も考えずに全部publicにするより、「このクラスはどこから生成されるべきか」を意識して設定すると、設計の意図が伝わりやすくなります。

Zetto

コンストラクタの種類ごとの使いどころを押さえておくと、クラス設計の選択肢がぐっと広がります。引数の有無・オーバーロード・アクセス修飾子を組み合わせて、柔軟な初期化を実現できます。

アクセス修飾子に関しては、以下記事で詳しく解説しているので、ぜひチェックしてみてください。

this()とsuper()の使い方|継承時の動きを理解する

this()とsuper()の使い方

この章では、コンストラクタ内で使うthis()super()の仕組みを解説します。

  • this()でコンストラクタ同士を連携させる方法
  • super()で親クラスのコンストラクタを呼び出す方法
  • 継承時にsuper()が必要になるケースと注意点

順番に見ていきましょう。

this()でコンストラクタ同士を連携させる方法

this()は、同じクラス内の別のコンストラクタを呼び出す構文です。

オーバーロードしたコンストラクタが複数あると、初期化ロジックが重複しがちです。そこでthis()を使って処理をまとめます。

public class User {
    String name;
    int age;

    // 引数なし → 引数ありコンストラクタに委譲
    public User() {
        this("名無し", 0);
    }

    // nameだけ → 引数2つのコンストラクタに委譲
    public User(String name) {
        this(name, 0);
    }

    // 実際の初期化処理はここだけ
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

this()には守るべきルールがあります。

  • コンストラクタの1行目に書かなければならない
  • this()super()を同じコンストラクタ内に両方書くことは禁止

重複するロジックを1か所にまとめられるので、修正時の変更箇所が減ります。メンテナンス性が大きく向上するのが特徴です。

super()で親クラスのコンストラクタを呼び出す方法

super()は、子クラスのコンストラクタから親クラスのコンストラクタを呼ぶ構文です。

public class Animal {
    String name;

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

public class Dog extends Animal {
    String breed;

    public Dog(String name, String breed) {
        super(name); // 親クラス(Animal)のコンストラクタを呼ぶ
        this.breed = breed;
    }
}

super(name)を実行することで、Animalクラスのthis.name = nameが先に処理されます。子クラスで親クラスのフィールドを再設定する必要がなくなり、コードがすっきりします。

継承時にsuper()が必要になるケースと注意点

親クラスに引数ありコンストラクタしかない場合、子クラスでsuper()を明示的に書かないとコンパイルエラーになります。

public class Animal {
    String name;

    public Animal(String name) { // 引数ありのみ定義(デフォルトは自動生成されない)
        this.name = name;
    }
}

public class Dog extends Animal {
    // super()を書かないとコンパイルエラー
    public Dog(String name) {
        super(name); // これがないとエラーになる
    }
}

逆に、親クラスにデフォルトコンストラクタがある(コンストラクタを何も定義していない)場合は、super()を書かなくてもJavaが暗黙的に呼んでくれます。

継承が絡む設計では「親クラスのコンストラクタがどうなっているか」を必ず確認する習慣をつけておきましょう。これが徹底できるだけでも、詰まる時間が大幅に減ります。

this()super()はどちらもコンストラクタを連携させる仕組みです。使いこなせると継承設計がかなりすっきりします。

Mirai

this()super()って似てるけど、使い分けが難しそう…。

Zetto

this()は同じクラス内・super()は親クラスへ、って覚えると整理しやすいよ。どちらもコンストラクタの1行目にしか書けないというルールは絶対に覚えておいてね!

実務で差がつくコンストラクタの活用パターン

実務的なコンストラクタの活用

実務でよく見るコンストラクタの使い方と、避けるべき書き方をまとめます。

  • Spring Bootで使われるコンストラクタインジェクション
  • コンストラクタでやってはいけない処理
  • 初心者がよく見るコンパイルエラーと解決のポイント
  • Java Silver試験で押さえておくべき出題ポイント

1つずつ解説していきます。

Spring Bootで使われるコンストラクタインジェクション

Spring Bootを使ったWebアプリ開発では、コンストラクタを使ったDI(Dependency Injection:依存性注入)がよく使われます。

DIとは、クラスが必要とする別のオブジェクトを外部から注入する仕組みのことです。Spring Bootでは、これをコンストラクタで実現するのが推奨スタイルです。

@Service
public class UserService {
    private final UserRepository userRepository;

    // コンストラクタインジェクション
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

コンストラクタインジェクションが推奨される理由は以下の通りです。

  • finalフィールドにできる:オブジェクト生成後に変更できないため、安全性が高まる
  • テストがしやすい:モック(テスト用の仮オブジェクト)を注入しやすくなる
  • 依存関係が明示的:コンストラクタを見ればそのクラスが何に依存しているかわかる

僕が実務でJavaのWebアプリ案件を担当していたとき、コンストラクタインジェクションは当たり前のように使われていました。フィールドに@Autowiredを直接書くスタイルより、こちらが好まれていましたね。

コンストラクタの基礎を理解してからSpring Bootに進むと、DIのコードが自然に読めるようになります。

コンストラクタでやってはいけない処理

コンストラクタは「初期化のための場所」です。次のような処理は避けるのが基本です。

  • 重い処理(DB接続・ファイル読み込みなど):オブジェクト生成のたびに実行されるため、パフォーマンスに悪影響が出る
  • 例外が発生しやすい処理:コンストラクタ内の例外処理は複雑になりやすく、呼び出し側が扱いにくくなる
  • ビジネスロジック(業務処理):コンストラクタはあくまで初期化専用。処理はメソッドに書く

特にDBアクセスをコンストラクタに書いてしまうと、テストのたびにDBを用意しなければならなくなります。コンストラクタはシンプルに保つのが賢明です。

初心者がよく見るコンパイルエラーと解決のポイント

コンストラクタ絡みのコンパイルエラーは、パターンが決まっています。

よくあるエラーと原因をまとめます。

  • constructor User() is not applicable:引数ありコンストラクタだけ定義されているのに引数なしで呼んでいる。デフォルトコンストラクタを追加するか、正しい引数を渡す
  • call to super must be first statementsuper()がコンストラクタの1行目にない。必ず1行目に移動する
  • constructor already defined:同じシグネチャ(引数の数と型の組み合わせ)のコンストラクタが2つある。引数の型か数を変えて区別する
  • cannot find symbolthis.フィールド名のスペルが間違っている。フィールド名を再確認する

エラーメッセージをそのままコピーして検索するか、Oracle公式の日本語リファレンスを参照するのが解決の近道です。

Javaのコンストラクタの演習問題

コンストラクタの練習問題

理解を深めるために、演習問題を3つ用意しました。

演習問題1:引数ありコンストラクタを定義する

問題

以下の条件でBookクラスを作成してください。

  • フィールド:String titleString authorint price
  • 引数ありコンストラクタで3つのフィールドを初期化する
  • mainメソッドでBookオブジェクトを1つ生成し、各フィールドの値を出力する

解答例

public class Book {
    String title;
    String author;
    int price;

    public Book(String title, String author, int price) {
        this.title = title;
        this.author = author;
        this.price = price;
    }

    public static void main(String[] args) {
        Book book = new Book("Javaプログラミング入門", "Zetto", 2800);
        System.out.println(book.title);
        System.out.println(book.author);
        System.out.println(book.price);
    }
}

解説

this.title = titleのようにthisを使ってフィールドと引数を区別しています。new Book(...)でコンストラクタが呼ばれ、3つのフィールドに値がセットされます。

thisの使い方に慣れていないうちは、引数名を少し変えて確認してみると理解が深まります。

演習問題2:コンストラクタをオーバーロードする

問題

Productクラスを作成してください。

  • フィールド:String nameint price
  • コンストラクタを2つ定義する(引数なし:name = "未設定"price = 0/引数あり:String nameint price
  • mainメソッドで両方のコンストラクタを使ってオブジェクトを生成し、フィールドを出力する

解答例

public class Product {
    String name;
    int price;

    public Product() {
        this("未設定", 0); // 引数ありコンストラクタに委譲
    }

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public static void main(String[] args) {
        Product p1 = new Product();
        System.out.println(p1.name + " : " + p1.price); // 未設定 : 0

        Product p2 = new Product("コーヒー", 300);
        System.out.println(p2.name + " : " + p2.price); // コーヒー : 300
    }
}

解説

引数なしコンストラクタ内でthis("未設定", 0)を使い、初期化ロジックを1か所にまとめています。this()を使わずに直接フィールドに代入する書き方も動きますが、this()を使うと修正箇所が1か所にまとまって保守しやすくなります。

演習問題3:super()で親クラスのコンストラクタを呼ぶ

問題

以下の2つのクラスを完成させてください。

  • Animalクラス:フィールドString name、引数ありコンストラクタを定義する
  • Catクラス:Animalを継承し、フィールドString colorを追加。super()を使ってAnimalのコンストラクタを呼ぶ
  • mainメソッドでCatオブジェクトを生成し、namecolorを出力する

解答例

public class Animal {
    String name;

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

public class Cat extends Animal {
    String color;

    public Cat(String name, String color) {
        super(name); // 親クラスのコンストラクタを1行目で呼ぶ
        this.color = color;
    }

    public static void main(String[] args) {
        Cat cat = new Cat("ミケ", "三毛");
        System.out.println(cat.name);  // ミケ
        System.out.println(cat.color); // 三毛
    }
}

解説

super(name)を1行目に書くことで、Animalクラスのthis.name = nameが先に実行されます。super()を書かないと、Animalに引数なしコンストラクタが存在しないためコンパイルエラーになります。

super()はコンストラクタの1行目」というルールをここで改めて確認しておきましょう。これが徹底できるだけでも、エラーで詰まる時間が大幅に減ります。

Zetto

演習問題は手元で実際に動かしてみるのが一番です。「動いた」で終わらず、「なぜこう書くのか」を言語化する習慣をつけると、理解の定着がぐっと変わります。

Javaコンストラクタに関するよくある質問

コンストラクタのFAQ

よくある質問と回答をまとめました。

  • コンストラクタは複数定義できますか?
  • 戻り値の型を書いてしまうとどうなりますか?
  • 親クラスにコンストラクタを定義したのに子クラスでエラーが出ます

コンストラクタは複数定義できますか?

はい、定義できます。これがコンストラクタのオーバーロードです。ただし、「引数の数と型の組み合わせ(シグネチャ)」が違う場合に限ります。

User(String name)User(String email)はどちらも引数がString1つなのでシグネチャが同じです。これは共存できず、コンパイルエラーになります。

一方、User(String name)User(String name, int age)は引数の数が違うため、共存できます。

戻り値の型を書いてしまうとどうなりますか?

コンストラクタではなく、同名のメソッドとして扱われます。コンパイルエラーにはなりませんが、オブジェクト生成時に呼ばれなくなります。

public class Sample {
    // 「void」があるため、これはメソッドとして扱われる
    public void Sample() {
        System.out.println("コンストラクタのつもりが呼ばれない");
    }
}

「コンストラクタを定義したのに初期化が実行されない」という形で発覚することが多いパターンです。コンストラクタには戻り値の型を書かないようにしてください。

親クラスにコンストラクタを定義したのに子クラスでエラーが出ます

親クラスに引数ありコンストラクタだけがある場合によく起きるエラーです。

引数ありコンストラクタを定義すると、親クラスのデフォルトコンストラクタが自動生成されなくなります。子クラスのコンストラクタは何も書かなければ暗黙的にsuper()(引数なし)を呼ぼうとします。

しかし親クラスに引数なしコンストラクタがないためエラーになります。

解決策は2つあります。

  • 子クラスでsuper(引数)を明示的に書く:親クラスの引数ありコンストラクタに合わせた値を渡す
  • 親クラスにデフォルトコンストラクタを追加する:どうしても引数なしで生成したい場合
Zetto

コンストラクタのエラーはパターンが決まっています。「デフォルトコンストラクタが消えるタイミング」と「super()が必要なタイミング」さえ押さえれば、ほとんどのエラーはすぐに解決できます。

コンストラクタをマスターしてJavaの設計力を高めよう

コンストラクタをマスターしよう

この記事では、Javaのコンストラクタについて基本から実務活用まで解説しました。

改めて、この記事のポイントを整理します。

  • コンストラクタの役割newでオブジェクトを生成するときに自動で呼ばれる初期化用の処理
  • メソッドとの3つの違い:クラス名と同じ名前・戻り値なし・newで自動実行
  • デフォルトコンストラクタのルール:引数ありを1つでも定義すると自動生成されなくなる
  • this()とsuper():どちらもコンストラクタの1行目のみ。同時使用は禁止
  • 実務での活用:Spring Bootのコンストラクタインジェクションは現場の標準スタイル

コンストラクタは地味に見えて、実はJavaのクラス設計の土台になる仕組みです。ここを理解しておくと、継承・インターフェース・DIといった応用的な概念も自然に腹落ちしてきます。

次のステップとして、オブジェクト指向の概念(継承・ポリモーフィズム・インターフェース)を学ぶと、コンストラクタの設計意図がさらに深く理解できます。コンストラクタを起点に、Javaのクラス設計力をどんどん高めていきましょう。

Mirai

コンストラクタってただの初期化だと思ってたけど、設計に深く関わってるんだね!

Zetto

コンストラクタを意識して書けるようになると、クラス設計全体が整ってくる感覚があります。まずは今回の演習問題を試してみてくださいね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次