Javaの「クラス」と「オブジェクト」は、オブジェクト指向プログラミングの基本概念です。本記事では、クラスとオブジェクトの違いや関係性を解説し、コンストラクタやメソッドを活用した実践的なサンプルコードも紹介します。初心者でも理解しやすい内容になっているので、ぜひ参考にしてください。
クラスの基本構成とは?

クラスは、オブジェクト指向プログラミングの中心となる概念であり、以下の要素で構成されます。
クラスの主要な構成要素
クラスは、オブジェクトの設計図として機能し、データ(状態)と処理(動作)を統合した構造を持ちます。クラスを適切に設計することで、オブジェクト指向プログラミングの特性を活かし、再利用性や保守性の高いコードを実現できます。ここでは、クラスを構成する主要な要素について詳しく解説します。
項目 | 説明 |
---|---|
フィールド(変数) | クラスが持つデータ(状態) |
メソッド | クラスが持つ処理(動作) |
コンストラクタ | オブジェクトの初期化処理 |
クラスの基本構造(例)
以下のコードは、 Car クラスを定義し、フィールドとメソッドを持たせた基本的な構造です。
public class Car {
String color;
int speed;
void accelerate() {
speed += 10;
}
}
クラスの分け方の考え方
クラスを適切に分けることは、プログラムの可読性や保守性を向上させるために重要です。クラスが適切に設計されていないと、修正や拡張が困難になり、コードの再利用性も低下します。本章では、「クラスは1つの責務に集中させる」「データと振る舞いを1つのまとまりにする」といった設計の基本原則を解説し、適切なクラス分けの考え方について詳しく説明します。
main メソッドの役割
Java プログラムでは、 main メソッドがエントリーポイント(起動点)として動作します。
main メソッドはプログラムのスタート地点となりますが、業務ロジックを全て記述するのは適切ではありません。
適切なクラス・メソッドに分けることで、コードの可読性や保守性が向上します。
【基本的な main メソッドの構造】
public class MainExample {
public static void main(String[] args) {
System.out.println("プログラムを開始します");
}
}
【設計のポイント】
- main メソッドには業務ロジックを直接記述せず、他のクラスやメソッドを呼び出すようにする
- 処理を適切なメソッドに分けることで、コードの再利用性が高まる
クラスは「責務(役割)」で分ける
クラスを設計する際に重要なのは「1つのクラスは1つの責務(役割)を持つ」という考え方です。
この原則を 単一責任の原則(SRP: Single Responsibility Principle) と呼びます。
1つのクラスが複数の責務を持つと、修正や拡張が困難になり、保守性が低下します。そのため、クラスを適切に分割することが重要です。
【悪い例】1つのクラスに複数の責務を持たせてしまう
以下のコードは、車(Car)の管理とエンジン(Engine)の管理を1つのクラスで行ってしまっているため、責務が混在しています。
public class Car {
private String color;
private int speed;
private boolean engineRunning;
public void startCar() {
engineRunning = true;
System.out.println("エンジンが始動しました");
}
}
【良い例】責務ごとにクラスを分ける
責務ごとに「Carクラス」と「Engineクラス」を分けることで、クラスの役割を明確にし、拡張性や保守性を向上させます。
public class Car {
private String color;
private int speed;
private Engine engine;
public Car(String color) {
this.color = color;
this.engine = new Engine();
}
public void startCar() {
engine.start();
}
}
public class Engine {
private boolean isRunning;
public void start() {
isRunning = true;
System.out.println("エンジンが始動しました");
}
}
クラスは「データ」と「振る舞い」のまとまりで設計する
クラスは単にデータを保持するだけでなく、データを操作するメソッドを持つべきです。
「データと振る舞いが1つのまとまり」 になっていることが望ましいとされています。
【悪い例】データと振る舞いが分離している
データと処理を完全に分けてしまうと、オブジェクト指向の利点が失われます。
public class BankAccountData {
double balance;
}
public class BankAccountOperations {
public void deposit(BankAccountData account, double amount) {
account.balance += amount;
}
}
【良い例】データと振る舞いを統合
データと処理を1つのクラスに統合することで、直感的に扱いやすい設計になります。
public class BankAccount {
private double balance;
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
} else {
System.out.println("残高不足です");
}
}
}
ポイント
- クラスは「1つの責務」に集中させ、複数の責務を持たせない
- データと処理を適切に統合し、直感的に扱いやすくする
- 適切に分けることで、保守性・可読性・再利用性が向上する
メソッドの分け方の考え方
メソッドの設計は、プログラムの可読性や保守性に大きく影響します。適切にメソッドを分けることで、コードの再利用性が向上し、バグの発生を抑えることができます。本章では、メソッドを分ける際の基本的な考え方として、「1つのメソッドは1つの役割に集中させる」「分かりやすい命名を行う」「共通処理はユーティリティクラスに分ける」などのポイントを詳しく解説します。
main メソッドとは?
Java プログラムを動かすには、必ず main メソッドが必要です。
このメソッドがプログラムのスタート地点(エントリーポイント)になります。
【基本的な main メソッドの形】
public class MainExample {
public static void main(String[] args) {
System.out.println("プログラムを開始します!");
}
}
【重要ポイント】
- Java のプログラムは main メソッドから始まる
- System.out.println() で画面にメッセージを表示できる
- メインの処理は、できるだけ他のメソッドに分ける
【main メソッドが使われるケースと使われないケース】
Java では、プログラムの種類によってエントリーポイントの考え方が異なります。
プログラムの種類 | エントリーポイント | 補足 |
---|---|---|
コンソールアプリケーション | main メソッド | Java の基本的なプログラム |
GUI アプリケーション(JavaFX / Swing) | main メソッド | GUI の起動処理を `main` で行う |
Web アプリケーション(Servlet / JSP) | Servlet コンテナ(Tomcat など) | Web サーバーがエントリーポイントを管理 |
Spring Boot アプリケーション | main メソッド | Spring の `SpringApplication.run()` を呼び出す |
基本的な Java プログラムでは `main` メソッドがエントリーポイントになりますが、Web アプリケーションのようにフレームワークが制御する場合は `main` を直接使わないこともあります。
main メソッドをシンプルにする理由
大きなプログラムでは、 main メソッドに全ての処理を書くと読みにくくなります。
代わりに、処理ごとにメソッドを作り、それを main から呼び出すのが良い設計です。
【悪い例】
すべての処理を main に書いてしまうと、コードが読みにくくなります。
public static void main(String[] args) {
System.out.println("計算を開始します");
int a = 10;
int b = 5;
int result = a + b;
System.out.println("合計: " + result);
}
【良い例】
計算の処理を別のメソッドに分けると、コードがスッキリします。
public static void main(String[] args) {
System.out.println("計算を開始します");
int result = add(10, 5);
System.out.println("合計: " + result);
}
public static int add(int a, int b) {
return a + b;
}
【ポイント】
- main には「プログラムの流れ」だけを書く
- 具体的な処理は、別のメソッドに分ける
- こうすると、コードが整理されて読みやすくなる
メソッドとは?
メソッドは、特定の処理をまとめたコードのブロックであり、オブジェクト指向プログラミングにおいて重要な役割を持ちます。
適切にメソッドを設計することで、コードの可読性や再利用性が向上し、保守性が高まります。
【メソッドの基本構造】
Java のメソッドは以下のような構造を持ちます。
[アクセス修飾子] [戻り値の型] [メソッド名] ([引数リスト]) {
処理内容;
}
例えば、以下のコードは、整数を加算して結果を返すメソッドです。
public int add(int a, int b) {
return a + b;
}
メソッドの戻り値と引数
メソッドは、入力(引数)を受け取り、処理を行い、結果(戻り値)を返すことができます。
メソッドの種類 | 説明 | 例 |
---|---|---|
戻り値なし | メソッド内の処理のみ実行し、値を返さない | public void printMessage() { System.out.println("Hello"); } |
戻り値あり | 処理の結果を返す | public int add(int a, int b) { return a + b; } |
引数なし | メソッドを呼び出すだけで実行可能 | public void sayHello() { System.out.println("Hello"); } |
引数あり | 入力データを受け取って処理を行う | public void greet(String name) { System.out.println("Hello, " + name); } |
メソッドのオーバーロード(Overload)
同じ名前のメソッドを、異なる引数リストで定義することをオーバーロードと言います。
オーバーロードを利用すると、同じ処理を異なる引数の組み合わせで実行でき、コードの可読性と柔軟性が向上します。
【オーバーロードの例】
public class Calculator {
// 整数の加算
public int add(int a, int b) {
return a + b;
}
// 小数の加算
public double add(double a, double b) {
return a + b;
}
}
静的メソッド(Static Method)
クラスに属し、オブジェクトを生成せずに直接呼び出せるメソッドを静的メソッド(static メソッド)といいます。
静的メソッドは、 Math クラスのようなユーティリティメソッドに多く利用されます。
【静的メソッドの例】
public class MathUtils {
public static int square(int num) {
return num * num;
}
}
呼び出し方:
int result = MathUtils.square(5);
メソッドのオーバーライド(Override)
オーバーライドとは、親クラスのメソッドを子クラスで再定義することを指します。
オーバーライドを利用すると、継承を活かした柔軟な設計が可能になります。
【オーバーライドの例】
class Animal {
public void makeSound() {
System.out.println("動物の鳴き声");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("ワンワン");
}
}
この場合、 Dog クラスは Animal クラスの makeSound() メソッドをオーバーライドし、異なる動作を実装しています。
メソッドは「1つの役割」に集中させる
メソッドは「1つのメソッドは1つの機能だけを担当する」ことが重要です。
1つのメソッドに複数の役割を持たせると、修正が難しくなり、可読性も低下します。
【悪い例】複数の処理を1つのメソッドに詰め込む
以下のコードでは、注文処理のメソッドが「合計金額の計算」「割引適用」「メール送信」をすべて含んでおり、責務が混在しています。
public void processOrder() {
calculateTotal();
applyDiscount();
sendEmail();
}
【良い例】処理を細かく分ける
以下のコードでは、「合計金額の計算」「割引適用」「メール送信」をそれぞれ別のメソッドに分けています。
public void processOrder() {
calculateTotal();
applyDiscount();
notifyCustomer();
}
private void calculateTotal() { ... }
private void applyDiscount() { ... }
private void notifyCustomer() { ... }
メソッドの命名を分かりやすくする
メソッド名は 「何をするのかが一目でわかるように」 するべきです。
【良い命名】
- calculateTotalPrice() → 「合計金額を計算する」
- sendEmailToUser() → 「ユーザーにメールを送信する」
- getCustomerDetails() → 「顧客情報を取得する」
【悪い命名】
- calc() → 何の計算をするのか不明
- doEmail() → メールを「送信」するのか「受信」するのか不明
- info() → 何の情報を扱うのか不明
共通処理はユーティリティメソッドに切り出す
プログラムの中で何度も使う処理は、別のクラスに分けてユーティリティメソッドとして定義するのが良い設計です。
【悪い例】同じ処理を何度も書いてしまう
以下のコードでは、クラスの中で税率計算を毎回手書きで実装しています。
public class Order {
public double applyTax(double price) {
return price * 1.1;
}
}
【良い例】共通メソッドをユーティリティクラスに分ける
以下のコードでは、「税率計算」などの共通処理を TaxCalculator クラスにまとめています。
public class TaxCalculator {
public static double applyTax(double price) {
return price * 1.1;
}
}
ポイント
- メソッドは「1つの役割」に集中させる
- メソッド名は直感的に理解できるようにする
- 共通処理はユーティリティクラスに分けて管理する
- 適切に分けることで、保守性・可読性・再利用性が向上する
メソッドの可視性とアクセス制御
メソッドを適切に分ける際には、可視性(アクセス修飾子)を適切に設定することが重要です。
Javaのメソッドには以下の4種類のアクセス修飾子があります。
修飾子 | クラス内 | パッケージ内 | サブクラス | 全てのクラス |
---|---|---|---|---|
public | ★ | ★ | ★ | ★ |
protected | ★ | ★ | ★ | ✕ |
(指定なし / デフォルト) | ★ | ★ | ✕ | ✕ |
private | ★ | ✕ | ✕ | ✕ |
【可視性の適用例】
public class Example {
private int count; // クラス内のみアクセス可能
public void increment() { // どこからでも呼び出し可能
count++;
}
protected void reset() { // サブクラスからアクセス可能
count = 0;
}
}
【設計のポイント】
- クラスの外部からアクセスする必要がないメソッドは private にする
- パブリックAPIとして提供するメソッドは public を使用
- サブクラスで利用するメソッドは protected に設定
コンストラクタの活用
コンストラクタは、オブジェクトの生成時に自動的に呼び出される特別なメソッドであり、フィールドの初期化などを行う重要な役割を持ちます。適切にコンストラクタを活用することで、オブジェクトの状態を一貫したものにし、コードの可読性や保守性を向上させることができます。本章では、コンストラクタの基本的な仕組みから、デフォルトコンストラクタやオーバーロードの活用方法まで、具体的なコード例を交えて詳しく解説します。
コンストラクタとは?
コンストラクタは、オブジェクト生成時に呼び出される特別なメソッドで、オブジェクトの初期化を行います。
クラス内でコンストラクタを定義することで、オブジェクトが適切な状態で作成されるように制御できます。
【基本的なコンストラクタの構造】
以下のコードは、 Car クラスのコンストラクタを定義する例です。
public class Car {
String color;
int speed;
// コンストラクタ
public Car(String color, int speed) {
this.color = color;
this.speed = speed;
}
}
コンストラクタの活用方法
クラスの初期状態を決めるために、コンストラクタを活用することで、オブジェクトのフィールドに適切な値を設定できます。
【悪い例】コンストラクタを使用せず、手動で値を設定
以下のコードでは、フィールドを手動で設定する必要があり、コードの可読性が低下します。
Car myCar = new Car();
myCar.color = "Red";
myCar.speed = 0;
【良い例】コンストラクタを利用して初期値を設定
以下のコードでは、コンストラクタを利用することで、オブジェクト生成時に初期値を設定できます。
Car myCar = new Car("Red", 0);
【引数なしのデフォルトコンストラクタ】
引数を受け取らないデフォルトコンストラクタを定義することで、デフォルト値を設定できます。
public class Car {
String color;
int speed;
// デフォルトコンストラクタ
public Car() {
color = "White";
speed = 0;
}
}
【複数のコンストラクタ(オーバーロード)】
コンストラクタのオーバーロードを利用することで、異なるパターンのオブジェクト生成をサポートできます。
public class Car {
String color;
int speed;
// デフォルトコンストラクタ
public Car() {
this("White", 0);
}
// 引数ありのコンストラクタ
public Car(String color, int speed) {
this.color = color;
this.speed = speed;
}
}
ポイント
- コンストラクタはオブジェクトの初期化を行う
- コンストラクタを使用することで、フィールドの初期値を適切に設定できる
- 引数なしのデフォルトコンストラクタを定義すると、初期値を自動設定できる
- コンストラクタのオーバーロードを活用すると、柔軟なオブジェクト生成が可能になる
コンストラクタ(constructor)という名前の由来は、英語の 「construct(構築する)」 から来ています。 プログラミングにおいて 「コンストラクタ(constructor)」 は、オブジェクトを構築(作成)するための特別なメソッドであり、オブジェクトの初期化処理を行う役割を持ちます。そのため、「オブジェクトを構築するメソッド」 という意味から 「コンストラクタ」 と名付けられました。Javaに限らず、C++やPythonなどのオブジェクト指向プログラミング言語でも、オブジェクトの初期化を行うための「コンストラクタ」という概念が存在します。
コンストラクタの役割
- クラスのオブジェクト(インスタンス)が作られるときに最初に呼び出されるメソッド
- フィールドの初期化や、オブジェクトの設定を行う
- 戻り値はなく、クラス名と同じ名前を持つ
基本的なコンストラクタの書き方
public class Car {
String color;
int speed;
// コンストラクタ
public Car(String color, int speed) {
this.color = color;
this.speed = speed;
}
void displayInfo() {
System.out.println("色: " + color + ", 速度: " + speed);
}
}
// インスタンスの生成
public class Main {
public static void main(String[] args) {
Car myCar = new Car("赤", 100); // コンストラクタが自動で呼ばれる
myCar.displayInfo();
}
}
コンストラクタの特徴
- クラス名と同じ名前を持つ
- 戻り値がない( void すら書かない)
- インスタンス生成時に new を使うと自動で呼ばれる
- デフォルトコンストラクタ(引数なし)は、何も定義しない場合でも自動で作られる
- オーバーロード(複数のコンストラクタを定義)が可能
コンストラクタがない場合
Javaでは「コンストラクタが明示的に定義されていない場合、デフォルトコンストラクタ(引数なし)が自動で作られる」仕様になっています。
例えば、以下のように何もコンストラクタを定義していない場合でも、デフォルトのコンストラクタが作られるため、 new Car() でインスタンスを作成できます。
public class Car {
String color;
int speed;
}
しかし、この場合はフィールドの初期値を設定できないため、 color は null、 speed は になります。
クラスとメソッドを適切に分けることで得られるメリット
クラスとメソッドを適切に分けることで、コードの可読性や保守性が向上し、再利用性の高い設計が可能になります。クラスの責務を明確にし、メソッドを適切な単位で分けることで、プログラム全体の構造が整理され、修正や拡張が容易になります。本章では、クラスとメソッドを適切に分けることで得られる具体的なメリットについて詳しく解説します。
コードの可読性が向上する
クラスやメソッドが明確に分けられていると、コードが整理され、可読性が向上します。
以下のようなポイントを意識すると、より読みやすいコードになります。
- クラスの責務を1つに絞ることで、何をするクラスなのかが明確になる
- メソッドを適切に分けることで、処理の流れが分かりやすくなる
- 適切な命名を行うことで、コードを見ただけで処理内容が推測できる
保守性が向上する
クラスやメソッドの役割が明確に分かれていれば、修正や拡張時の影響範囲を限定でき、保守性が向上します。
以下のようなメリットがあります。
- 1つの機能を修正しても、他の部分に影響を与えにくくなる
- バグの発見と修正が容易になる
- 機能追加の際に、新しいクラスやメソッドを追加するだけで対応できる
コードの再利用性が高まる
共通処理をユーティリティメソッドとして分けることで、コードの重複を減らし、再利用性を向上させることができます。
【悪い例】コードの重複が発生する
以下のコードでは、各クラスで税率計算を個別に実装しており、コードの重複が発生しています。
public class Order {
public double applyTax(double price) {
return price * 1.1;
}
}
public class Invoice {
public double applyTax(double price) {
return price * 1.1;
}
}
【良い例】共通メソッドをユーティリティクラスに切り出す
以下のように、共通処理を TaxCalculator クラスにまとめることで、コードの重複を防ぐことができます。
public class TaxCalculator {
public static double applyTax(double price) {
return price * 1.1;
}
}
public class Order {
public double calculateTotal(double price) {
return TaxCalculator.applyTax(price);
}
}
public class Invoice {
public double calculateTotal(double price) {
return TaxCalculator.applyTax(price);
}
}
ポイント
- 適切にクラスとメソッドを分けることで、コードの可読性が向上する
- 修正や拡張時の影響範囲を限定でき、保守性が向上する
- 共通処理をユーティリティクラスに分けることで、コードの再利用性が高まる
まとめ
クラスとメソッドを適切に設計することで、コードの可読性・保守性・再利用性が向上します。以下のポイントを押さえて設計を行いましょう。
- クラスは1つの責務に集中させる(単一責任の原則)
- 1つのクラスが複数の責務を持つと、修正や拡張が困難になります。クラスごとに明確な役割を持たせ、責務を分けることが重要です。
- データと振る舞いを1つのまとまりにする
- データ(フィールド)とそのデータを操作するメソッドは、同じクラスにまとめることで直感的に扱いやすくなります。
- メソッドは1つの役割に集中させ、適切な長さにする
- 1つのメソッドに複数の処理を詰め込むと可読性が低下します。機能ごとにメソッドを分けることで、コードが理解しやすくなります。
- メソッド名は直感的に理解できるようにする
- メソッドの命名は、そのメソッドが何をするのか一目で分かるようにすることが大切です。具体的で分かりやすい名前をつけましょう。
- 共通処理はユーティリティクラスに切り出す
- プログラム内で何度も使う処理は、ユーティリティクラスとして別に定義することで、コードの重複を防ぎ、再利用性を高めることができます。
- 適切に分けることで、可読性・保守性・再利用性が向上する
- 適切なクラス設計を行うことで、コードが整理され、修正・拡張が容易になります。また、再利用可能なコードが増え、開発効率が向上します。
これらのポイントを意識しながらクラスとメソッドを設計することで、よりメンテナブルで拡張性の高いコードを実現できます。