Javaのクラスとは?5つの基本構造とインスタンス化を徹底解説

Mirai

Javaのクラスって、なんとなくわかるけど、ちゃんと説明できない…。

Zetto

クラスは一言でいうとオブジェクトの「設計図」だね。

この記事では、Javaのクラスの基本からインスタンス化の手順まで解説します。

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

本記事の専門性
現役フリーランスエンジニアのZettoです。Java SilverとJava Goldの資格を持ち、Javaを使った業務系Webアプリの案件を複数経験しています。

クラスの概念があいまいなままだと、オブジェクト指向の理解が進まず、Java Silver試験でも点数が取りにくい状態が続きます。

この記事を読めば、Javaのクラスの構造・インスタンス化の手順・よくあるエラーの対処法まで一通り理解できます。

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

目次

Javaのクラスとは何か

Javaクラスとは

Javaのクラスの概念を、具体的な比喩と用語の関係から解説します。

  • 「設計図」という比喩が示す本当の意味
  • クラスとオブジェクト(インスタンス)の関係
  • クラスを使う3つのメリット

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

「設計図」という比喩が示す本当の意味

Javaのクラスとは、オブジェクトを作るための設計図です。

家の設計図を思い浮かべてください。設計図自体は「もの」ではありません。でも、その設計図があれば、同じ構造の家を何棟でも建てられますよね。

Javaのクラスも同じです。クラスには「どんなデータを持つか」「どんな処理をするか」を定義します。実際に動くのは、そのクラスをもとに作られた「オブジェクト(インスタンス)」の方です。

// これがクラス(設計図)
public class Car {
    String color;  // 色というデータ
    int speed;     // 速さというデータ

    void run() {  // 走るという処理
        System.out.println("走っています");
    }
}

クラスを定義するだけではプログラムは動きません。クラスから実体(インスタンス)を作ってはじめて、データを持って動く「もの」になります。

クラスとオブジェクト(インスタンス)の関係

クラスとオブジェクト(インスタンス)の関係は、次のように整理できます。

  • クラス:設計図。プログラムファイルに1回書くだけ
  • オブジェクト(インスタンス):クラスから作られた実体。何個でも作れる
  • インスタンス化:クラスからオブジェクトを生成する操作のこと

Javaでは「オブジェクト」と「インスタンス」はほぼ同じ意味で使われます。「クラスをインスタンス化してオブジェクトを作る」という言い方が一般的ですね。

// Car クラスから2つのインスタンスを作る例
Car myCar = new Car();    // 1台目
Car yourCar = new Car();  // 2台目

この2つのインスタンスは、同じ設計図から作られていても、それぞれ独立したデータを持ちます。myCarの色を変えても、yourCarの色は変わりません。

クラスを使う3つのメリット

クラスを使うことで、コードが整理され、再利用しやすくなります。具体的なメリットは以下の通りです。

  • 再利用できる:一度クラスを定義すれば、何度でもインスタンスを作れる。同じコードを書き直す手間がなくなる
  • 管理しやすい:データと処理を1つのクラスにまとめられる。どこに何が書いてあるかわかりやすい
  • 拡張しやすい:継承(後述)を使うと、既存のクラスを元に機能を追加できる。修正の影響範囲を小さく保てる

Javaのクラスの基本的な概念がつかめましたね。次のセクションからは、実際にコードを書きながら構造を見ていきましょう。

Mirai

設計図という例えがわかりやすい!インスタンスって、設計図から作った「本物」ってこと?

Zetto

まさにそれ!設計図(クラス)は1枚でも、そこから家(インスタンス)は何棟でも建てられるんだよね。

クラスの基本構造とインスタンス化の手順

クラスの構造とインスタンス化

Javaのクラスを実際に書くために必要な要素を順番に解説します。

  • フィールド(属性)の定義と役割
  • コンストラクタの書き方と使いどころ
  • メソッドの定義と呼び出し方
  • newキーワードでインスタンスを生成する手順

それぞれ見ていきましょう。

フィールド(属性)の定義と役割

フィールドとは、クラスが持つデータ(状態)のことです。

人間で言えば「名前」「年齢」「身長」のような情報です。Javaでは、クラスのブロック内でフィールドを定義します。

public class Person {
    String name;   // 名前
    int age;       // 年齢
    double height; // 身長
}

フィールドにはデータ型(String、int、doubleなど)を指定します。インスタンスごとに異なる値を持てるのがフィールドの特徴です。

コンストラクタの書き方と使いどころ

コンストラクタとは、インスタンスを生成するときに自動的に呼ばれる特別なメソッドです。

主な役割は、インスタンスを作ると同時にフィールドに初期値をセットすることです。書き方のルールは次の通りです。

  • クラス名と同じ名前にする
  • 戻り値(voidなど)は書かない
public class Person {
    String name;
    int age;

    // コンストラクタ
    public Person(String name, int age) {
        this.name = name; // thisはこのインスタンス自身を指す
        this.age = age;
    }
}

thisは「このインスタンスのフィールド」を指します。引数の変数名とフィールド名が同じときに区別するために使います。

コンストラクタを定義しない場合、Javaが自動で「引数なしのコンストラクタ(デフォルトコンストラクタ)」を用意してくれます。ただし、自分でコンストラクタを1つでも書くと、デフォルトコンストラクタは消えるので注意してください。

メソッドの定義と呼び出し方

メソッドとは、クラスが持つ処理(動作)のことです。

フィールドが「データ」なら、メソッドは「そのデータに対してやれること」です。定義の基本形は次の通りです。

public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // メソッドの定義
    public void introduce() {
        System.out.println("名前は" + name + "、年齢は" + age + "歳です。");
    }
}

メソッドを呼び出すには、インスタンスを作ってから「インスタンス名.メソッド名()」の形で書きます。

Person taro = new Person("太郎", 25);
taro.introduce(); // 「名前は太郎、年齢は25歳です。」と出力される

newキーワードでインスタンスを生成する手順

基本的なインスタンス生成の書き方

インスタンスを生成するにはnewキーワードを使います。書き方は以下の形が基本です。

クラス名 変数名 = new クラス名(引数);

先ほどのPersonクラスを使った例を見てみましょう。

Person taro = new Person("太郎", 25);
System.out.println(taro.name); // 太郎
System.out.println(taro.age);  // 25
taro.introduce();              // 名前は太郎、年齢は25歳です。

new Person(“太郎”, 25)の部分で、コンストラクタが呼ばれてフィールドに値がセットされます。

複数インスタンスを生成したときの挙動

同じクラスから複数のインスタンスを作ると、それぞれが独立したデータを持ちます。

Person taro = new Person("太郎", 25);
Person hanako = new Person("花子", 22);

taro.introduce();   // 名前は太郎、年齢は25歳です。
hanako.introduce(); // 名前は花子、年齢は22歳です。

// taroのデータを変えても、hanakoには影響なし
taro.age = 26;
System.out.println(taro.age);   // 26
System.out.println(hanako.age); // 22(変わっていない)

この「インスタンスごとにデータが独立している」という挙動は、Javaを学ぶ上でとても重要な概念です。

クラスの構造とインスタンス化の基本がつかめたかなと思います。フィールド・コンストラクタ・メソッド・newの4要素がクラスの骨格です。

Mirai

フィールド・コンストラクタ・メソッド・newの4つがクラスの骨格ですね。

Zetto

最初はこの4つだけ押さえれば十分ですよ。

クラスの種類と使い分け

クラスの種類と使い分け

クラスの基本を押さえたら、次はクラスの種類と活用方法を整理します。

  • アクセス修飾子とカプセル化の基本
  • 継承で親クラスの機能を引き継ぐ方法
  • 抽象クラスとインターフェースの違い
  • 内部クラス・ラッパークラスの概要

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

アクセス修飾子とカプセル化の基本

アクセス修飾子とは、フィールドやメソッドへのアクセス範囲を制御するキーワードです。

主なアクセス修飾子は以下の通りです。

  • publicどこからでもアクセスできる
  • private:同じクラス内からしかアクセスできない
  • protected:同じパッケージ(クラスのグループ)と、継承したクラスからアクセスできる

カプセル化(データの隠蔽)とは、フィールドをprivateにして外部から直接変更できなくし、ゲッター(getter)・セッター(setter)と呼ばれるメソッド経由で操作させる設計のことです。

public class Person {
    private String name; // privateにして外から直接変えられないようにする
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getter:値を取得する
    public String getName() {
        return name;
    }

    // setter:値を変更する(バリデーションも入れられる)
    public void setAge(int age) {
        if (age >= 0) { // 0以上の値しか受け付けない
            this.age = age;
        }
    }
}

カプセル化をすることで、予期しない方法でデータが書き換えられるリスクを減らせます。実務でもカプセル化は基本的なルールとして扱われますね。

継承で親クラスの機能を引き継ぐ方法

継承とは、既存のクラス(親クラス)の機能を引き継いで、新しいクラス(子クラス)を作る仕組みです。

extendsキーワードを使います。

// 親クラス
public class Animal {
    String name;

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

    public void eat() {
        System.out.println(name + "が食事しています。");
    }
}

// 子クラス(Animalを継承)
public class Dog extends Animal {

    public Dog(String name) {
        super(name); // 親クラスのコンストラクタを呼ぶ
    }

    // 独自のメソッドを追加
    public void bark() {
        System.out.println(name + "が吠えています。");
    }
}
Dog dog = new Dog("ポチ");
dog.eat();  // 親クラスのメソッドも使える。「ポチが食事しています。」
dog.bark(); // 子クラス独自のメソッド。「ポチが吠えています。」

superは親クラスを指すキーワードです。親クラスのコンストラクタを呼ぶときに使います。

抽象クラスとインターフェースの違い

抽象クラスとインターフェースは、どちらも「ひな形」を作るための仕組みです。ただし、用途が異なります。

  • 抽象クラス(abstract class):共通の処理と、子クラスで実装させたいメソッドを定義する。extendsで継承する。1つのクラスしか継承できない
  • インターフェース(interface):処理の取り決め(仕様)だけを定義する。implementsで実装する。複数適用できる
// 抽象クラスの例
public abstract class Shape {
    abstract double area(); // 子クラスで実装させる(抽象メソッド)

    public void display() { // 共通の処理は書ける
        System.out.println("面積:" + area());
    }
}

// インターフェースの例
public interface Printable {
    void print(); // 実装は書かない
}

「抽象クラス=部分的なひな形」「インターフェース=取り決めだけ」と覚えておくと整理しやすいかなと。

内部クラス・ラッパークラスの概要

Javaにはさらに以下のようなクラスの種類があります。

  • 内部クラス(インナークラス):クラスの中に定義されたクラス。外側のクラスのフィールドに直接アクセスできる
  • ラッパークラス:intなどのプリミティブ型(基本データ型)をオブジェクトとして扱うためのクラス。Integer・Double・Booleanなどがある
int num = 42;
Integer wrapped = Integer.valueOf(num); // int → Integer(ラッピング)
int unwrapped = wrapped.intValue();     // Integer → int(アンラッピング)

// オートボクシング(自動変換)
Integer auto = 42; // Javaが自動でInteger.valueOf(42)に変換してくれる

ラッパークラスはコレクション(ArrayListなど)にプリミティブ型の値を入れるときに必要になります。実務でもよく使うクラスですね。

クラスの種類を整理できたので、次は実務や試験で役立つ設計の考え方を見ていきましょう。

Mirai

抽象クラスとインターフェースの違いって、ずっとぼんやりしてたんだよね。

Zetto

「部分的なひな形か、取り決めだけか」って覚えると区別しやすいよ!

実務で使えるJavaクラス設計の考え方

実務的なクラス設計

ここではクラス設計の実践的な知識を整理します。

  • クラス設計のNG例とOK例(命名・単一責任の原則)
  • 初心者がはまりやすいエラーと対処法
  • Java Silver試験でのクラス出題傾向

順に解説していきます。

クラス設計のNG例とOK例(命名・単一責任の原則)

クラスを設計するときに意識したいのが「単一責任の原則(SRP)」です。

単一責任の原則とは、「1つのクラスは1つの責任だけを持つべき」という考え方です。複数の役割を1つのクラスに詰め込むと、修正が難しくなります。

NG例とOK例を比べてみましょう。

// NG:1つのクラスにユーザー管理とメール送信を詰め込んでいる
public class UserManager {
    public void registerUser(String name) { /* ユーザー登録 */ }
    public void sendWelcomeMail(String email) { /* ウェルカムメール送信 */ }
    public void deleteUser(int userId) { /* ユーザー削除 */ }
}
// OK:ユーザー管理とメール送信をクラスに分ける
public class UserManager {
    public void registerUser(String name) { /* ユーザー登録 */ }
    public void deleteUser(int userId) { /* ユーザー削除 */ }
}

public class MailService {
    public void sendWelcomeMail(String email) { /* ウェルカムメール送信 */ }
}

クラス名は「何をするクラスか」がひと目でわかる名前にします。Manager・Service・Repositoryのように役割を示す言葉を末尾につけるのが一般的です。

設計の段階でクラスの責任を絞っておくと、後からのメンテナンスがずっと楽になります。

初心者がはまりやすいエラーと対処法

NullPointerExceptionの原因と回避策

NullPointerException(ヌルポインター例外)は、Javaで最も頻繁に遭遇するエラーの一つです。

null(値が何もない状態)のオブジェクトに対して操作しようとすると発生します。

Person person = null; // インスタンスを作らずnullのまま
System.out.println(person.name); // NullPointerException が発生!

回避策はシンプルです。使う前にnullチェックを入れることです。

if (person != null) {
    System.out.println(person.name); // nullでない場合だけ実行
}

Java 8以降ではOptionalクラスを使う方法もあります。nullを扱う処理が増えてきたときに覚えておくと便利ですね。

参照渡しの誤解によるバグ

Javaでオブジェクトを変数に代入すると、「オブジェクトそのもの」ではなく「参照(場所の情報)」がコピーされます。

Person taro = new Person("太郎", 25);
Person copy = taro; // taroの参照がcopyにコピーされる

copy.age = 30;              // copyを通じてageを変更
System.out.println(taro.age); // 30(taroも変わってしまう!)

taroとcopyは同じオブジェクトを指しているため、片方を変えるともう片方にも影響します。これは「参照渡し」の特性です。

別々のオブジェクトとして独立させたい場合は、新しいインスタンスを作るか、コピー用のメソッドを用意する必要があります。

Mirai

NullPointerExceptionは、僕も最初の案件でかなり悩まされましたね。

Zetto

「使う前にnullチェック」を習慣にするだけで、かなりバグが減りますよ。

Javaクラスの演習問題

Javaクラス演習問題

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

  • 演習問題1:基本的なクラスをゼロから書いてみよう
  • 演習問題2:コンストラクタとメソッドを組み合わせる
  • 演習問題3:継承を使ったクラス設計に挑戦

実際に手を動かしながら確認していきましょう。

演習問題1:基本的なクラスをゼロから書いてみよう

問題

Bookクラスを定義してください。条件は以下の通りです。

  • フィールド:title(タイトル、String型)、price(価格、int型)
  • コンストラクタ:titleとpriceを受け取って初期化する
  • メソッド:showInfo()という名前で「タイトル:〇〇、価格:〇〇円」と出力する
  • mainメソッドでBookのインスタンスを1つ作り、showInfo()を呼ぶ

解答例

public class Book {
    String title;
    int price;

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

    public void showInfo() {
        System.out.println("タイトル:" + title + "、価格:" + price + "円");
    }

    public static void main(String[] args) {
        Book book = new Book("Javaプログラミング入門", 2800);
        book.showInfo();
        // 出力:タイトル:Javaプログラミング入門、価格:2800円
    }
}

解説

フィールド・コンストラクタ・メソッドの3点セットを使うクラスの基本形です。this.title = titleのthisは「このインスタンスのフィールド」を指します。引数名とフィールド名が同じ場合はthisで区別します。

演習問題2:コンストラクタとメソッドを組み合わせる

問題

BankAccount(銀行口座)クラスを定義してください。条件は以下の通りです。

  • フィールド:owner(口座名義、String型)、balance(残高、int型)
  • コンストラクタ:ownerと初期残高(balance)を受け取る
  • メソッド:deposit(int amount)で残高に金額を加算する
  • メソッド:showBalance()で「〇〇の残高:〇〇円」と出力する
  • mainメソッドで1000円から始め、500円入金して残高を確認する

解答例

public class BankAccount {
    private String owner;
    private int balance;

    public BankAccount(String owner, int balance) {
        this.owner = owner;
        this.balance = balance;
    }

    public void deposit(int amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public void showBalance() {
        System.out.println(owner + "の残高:" + balance + "円");
    }

    public static void main(String[] args) {
        BankAccount account = new BankAccount("田中", 1000);
        account.showBalance(); // 田中の残高:1000円
        account.deposit(500);
        account.showBalance(); // 田中の残高:1500円
    }
}

解説

フィールドをprivateにしてカプセル化しています。depositメソッド内でamount > 0のチェックを入れることで、マイナスの金額が入金されるバグを防いでいます。実務でも入力値のバリデーションはメソッド内に書くのが基本ですね。

演習問題3:継承を使ったクラス設計に挑戦

問題

Vehicle(乗り物)クラスを親クラスとし、Bicycle(自転車)クラスを子クラスとして継承してください。条件は以下の通りです。

  • 親クラスVehicle:フィールドname(名前)、メソッドmove()で「〇〇が移動しています。」と出力
  • 子クラスBicycle:Vehicleを継承し、フィールドgears(ギア数、int型)を追加
  • 子クラスにshowGears()メソッドを追加して「ギア数:〇〇」と出力する
  • mainメソッドでBicycleのインスタンスを作り、両方のメソッドを呼ぶ

解答例

public class Vehicle {
    String name;

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

    public void move() {
        System.out.println(name + "が移動しています。");
    }
}
public class Bicycle extends Vehicle {
    int gears;

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

    public void showGears() {
        System.out.println("ギア数:" + gears);
    }

    public static void main(String[] args) {
        Bicycle bike = new Bicycle("クロスバイク", 21);
        bike.move();       // クロスバイクが移動しています。
        bike.showGears();  // ギア数:21
    }
}

解説

子クラスのコンストラクタではsuper(name)で親クラスの初期化を行います。superがないとコンパイルエラーになるので注意してください。

継承を使うと、Vehicleのmove()メソッドをBicycleでも使えるようになります。この「コードの再利用」が継承の最大のメリットです。

演習問題を通じて、クラスの基本的な使い方が体感できたかなと思います。

Mirai

演習問題3、最初はsuper()の書き方で詰まったけど、意味がわかったらスッキリした!

Zetto

superはよく忘れるんだよね。「子クラスのコンストラクタでは必ずsuper()で親を初期化する」って体で覚えると良いよ!

クラスの基礎が固まれば、Javaの次のステップが見えてくる

クラス基礎の次のステップ

この記事では、Javaのクラスについて以下の内容を解説しました。

  • クラスの概念:オブジェクトを作るための設計図であること
  • 基本構造:フィールド・コンストラクタ・メソッド・newキーワードの4要素
  • クラスの種類:アクセス修飾子・継承・抽象クラス・インターフェースの使い分け
  • 実務での注意点:単一責任の原則・NullPointerException・参照渡しの落とし穴
  • Java Silver対策:コンストラクタ・アクセス修飾子・継承が頻出

クラスはJavaのオブジェクト指向(オブジェクトを中心にプログラムを組む考え方)の核心です。ここが理解できると、Spring BootなどのJavaフレームワークの書き方も自然と読めるようになります。

Javaのクラスの書き方をより体系的に学びたい場合は、Oracleの公式チュートリアル(The Java™ Tutorials – Classes)も合わせて参照してみてください。実際のコード例が豊富で参考になりますよ。

Mirai

クラスってこんなに奥が深いとは思わなかった。でもこれで全体像がつかめた気がする!

Zetto

クラスを理解したら、次は継承やポリモーフィズムを深掘りしてみてね。そこからJavaのオブジェクト指向が本当に面白くなってくるよ!

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