はじめに
こんにちは。このシリーズでは、Javaの「オブジェクト指向」について一緒に学んでいきます。
オブジェクト指向は、Javaを学ぶ上で避けては通れない重要な概念です。
でも、「クラス」「インスタンス」「継承」といった言葉を聞いても、最初はなかなかイメージが掴めないですよね。
実は私も、教える立場になる前は何度もつまずきました。特に「なぜクラスが必要なのか」が腑に落ちるまで時間がかかりました。
でも安心してください。このシリーズでは、RPGゲームを作りながら、一歩ずつ理解を深めていきます。ゲームなら、プレイヤーや敵キャラクター、アイテムなど、イメージしやすいですよね。
この記事で一緒に学ぶこと:
- オブジェクト指向が生まれた背景
- 手続き型プログラミングとの違い
- 「現実世界をコードで表現する」という考え方
- クラスの基本的な書き方
所要時間: 約10〜15分です。焦らず、じっくり読んでいきましょう。
前提知識: 変数、データ型、if文、for文、メソッドの基本を理解していることを前提とします。
今回の冒険:Java Questへようこそ
あなたは駆け出しの冒険者です。この世界では、強い冒険者になるためには「オブジェクト指向」という魔法をマスターしなければなりません。
今回のクエストでは、あなた自身(プレイヤーキャラクター)を作るところから始めます。
オブジェクト指向が必要な理由
まず、質問です。
大規模なプログラムを作るとき、何が一番難しいと思いますか?
答えは「コードの管理」です。
例えば、RPGゲームを作ることを想像してみてください。
プレイヤー、敵キャラクター、アイテム、マップなど、たくさんの要素があります。
これらを変数だけで管理しようとすると、どうなるでしょうか?
手続き型プログラミングの限界
/**
* 手続き型での実装例:変数だけでRPGを作ろうとすると...
*/
public class OldStyleRPG {
public static void main(String[] args) {
// プレイヤーの情報を個別の変数で管理
String playerName = "勇者";
int playerX = 100; // X座標
int playerY = 200; // Y座標
int playerHp = 100; // 体力
int playerMaxHp = 100; // 最大体力
int playerAtk = 10; // 攻撃力
// 敵キャラ1の情報
String enemy1Name = "スライム";
int enemy1X = 300;
int enemy1Y = 400;
int enemy1Hp = 30;
int enemy1MaxHp = 30;
int enemy1Atk = 5;
// 敵キャラ2の情報
String enemy2Name = "ゴブリン";
int enemy2X = 500;
int enemy2Y = 600;
int enemy2Hp = 50;
int enemy2MaxHp = 50;
int enemy2Atk = 8;
// 敵キャラ3の情報
String enemy3Name = "ドラゴン";
int enemy3X = 700;
int enemy3Y = 800;
int enemy3Hp = 200;
int enemy3MaxHp = 200;
int enemy3Atk = 30;
// ... これが10体、100体になったら?
System.out.println("変数が多すぎて管理できない...");
}
}
見ているだけで頭が痛くなりませんか?
この方法の問題点:
- 変数が膨大な数になる(敵が10体なら60個の変数!)
- どの変数がどのキャラクターのものか分かりにくい
- 新しいキャラクターを追加するたびに大量の変数を追加
- バグの温床になる(enemy2Xを更新すべき所でenemy3Xを更新してしまう、など)
もっと良い方法はないでしょうか?
オブジェクト指向の考え方:「モノ」としてまとめる
ここで、現実世界を考えてみましょう。
あなたが友達に「スマホ貸して」と言うとき、
「画面と、バッテリーと、CPUと、カメラと...」とは言いませんよね。
「スマホ」という一つのモノとして扱います。
スマホには、
- データ(属性): 画面サイズ、バッテリー残量、ストレージ容量
- 機能(振る舞い): 電話をかける、写真を撮る、アプリを起動する
が備わっています。
オブジェクト指向は、この「現実世界の考え方」をプログラミングに持ち込んだものです。
RPGで考えると
勇者というキャラクターは:
- データ: 名前、座標、HP、攻撃力
- 機能: 移動する、攻撃する、ダメージを受ける
これらを一つの「モノ」としてまとめられたら便利ですよね。
それを実現するのが「クラス」です。
クラスとは:「設計図」を作る
クラスは、オブジェクト(モノ)の設計図です。
たい焼き屋さんで例えると:
- クラス = たい焼きの型(設計図)
- オブジェクト(インスタンス) = 実際に焼いたたい焼き
型は一つでも、そこから何個でもたい焼きを作れますよね。
同じように、一つのクラスから、何個でもオブジェクトを作ることができます。
実際にクラスを作ってみよう
それでは、RPGの「プレイヤーキャラクター」のクラスを作ってみましょう。
シンプルなPlayerクラス
/**
* プレイヤーキャラクターを表現するクラス
* RPGゲームにおける冒険者の状態と振る舞いを管理します。
*/
public class Player {
// ========================================
// フィールド(プレイヤーの「データ」)
// ========================================
/** プレイヤーの名前 */
String name;
/** プレイヤーのX座標(マップ上の横位置) */
int x;
/** プレイヤーのY座標(マップ上の縦位置) */
int y;
/** プレイヤーの体力(HP: Hit Point) */
int hp;
/** プレイヤーの最大体力 */
int maxHp;
/** プレイヤーの攻撃力 */
int attack;
// ========================================
// メソッド(プレイヤーの「機能」)
// ========================================
/**
* プレイヤーの現在の状態を表示します
* デバッグや確認用に使用します。
*/
void showStatus() {
System.out.println("===== プレイヤー情報 =====");
System.out.println("名前: " + name);
System.out.println("位置: (" + x + ", " + y + ")");
System.out.println("HP: " + hp + " / " + maxHp);
System.out.println("攻撃力: " + attack);
System.out.println("========================");
}
/**
* プレイヤーを移動させます
*
* @param dx X方向の移動量(正の数で右、負の数で左)
* @param dy Y方向の移動量(正の数で下、負の数で上)
*/
void move(int dx, int dy) {
// 現在の位置を表示
System.out.println(name + "が移動します");
System.out.println("現在位置: (" + x + ", " + y + ")");
// 座標を更新
x = x + dx; // または x += dx;
y = y + dy; // または y += dy;
// 移動後の位置を表示
System.out.println("移動後: (" + x + ", " + y + ")");
}
/**
* プレイヤーがダメージを受けます
*
* @param damage 受けるダメージ量(0以上の整数)
*/
void takeDamage(int damage) {
System.out.println(name + "は " + damage + " のダメージを受けた!");
// HPを減らす
hp = hp - damage; // または hp -= damage;
// HPが0未満にならないようにする
if (hp < 0) {
hp = 0;
}
// 現在のHPを表示
System.out.println("残りHP: " + hp + " / " + maxHp);
// HPが0になったら戦闘不能メッセージ
if (hp == 0) {
System.out.println(name + "は倒れた...");
}
}
/**
* 敵を攻撃します
*
* @param target 攻撃対象の敵
*/
void attackEnemy(Player target) {
System.out.println(name + "の攻撃!");
// 相手にダメージを与える
target.takeDamage(attack);
}
}
コードの解説
このクラスには、大きく分けて2つの部分があります。
1. フィールド(データ)
String name;
int x;
int y;
int hp;
int maxHp;
int attack;
これらは「インスタンス変数」または「フィールド」と呼ばれます。
プレイヤーが持つ「データ」を表現しています。
2. メソッド(機能)
void showStatus() { ... }
void move(int dx, int dy) { ... }
void takeDamage(int damage) { ... }
void attackEnemy(Player target) { ... }
これらは「インスタンスメソッド」と呼ばれます。
プレイヤーができる「行動」を表現しています。
クラスを使ってみよう:インスタンスの作成
クラスはあくまで「設計図」です。
実際に使うには、インスタンス(実体)を作る必要があります。
メインクラスの作成
/**
* ゲームのメインクラス
* プログラムの実行開始地点です。
*/
public class GameMain {
/**
* プログラムのエントリーポイント
* ここからゲームが始まります。
*
* @param args コマンドライン引数(今回は使用しません)
*/
public static void main(String[] args) {
System.out.println("=== Java Quest スタート! ===\n");
// ========================================
// プレイヤーキャラクターを作成
// ========================================
// new演算子でインスタンスを生成
Player hero = new Player();
// フィールドに値を設定
hero.name = "勇者アレン";
hero.x = 0;
hero.y = 0;
hero.hp = 100;
hero.maxHp = 100;
hero.attack = 15;
// プレイヤーの状態を表示
hero.showStatus();
System.out.println(); // 空行
// ========================================
// 敵キャラクターを作成
// ========================================
// 同じPlayerクラスから別のインスタンスを作成
Player slime = new Player();
slime.name = "スライム";
slime.x = 10;
slime.y = 5;
slime.hp = 30;
slime.maxHp = 30;
slime.attack = 5;
slime.showStatus();
System.out.println(); // 空行
// ========================================
// 冒険開始:戦闘シーン
// ========================================
System.out.println("=== 戦闘開始! ===\n");
// 勇者が移動
hero.move(10, 5);
System.out.println(); // 空行
// 勇者がスライムを攻撃
hero.attackEnemy(slime);
System.out.println(); // 空行
// スライムの反撃
slime.attackEnemy(hero);
System.out.println(); // 空行
// もう一度攻撃
hero.attackEnemy(slime);
System.out.println(); // 空行
// 戦闘後の状態確認
System.out.println("=== 戦闘終了 ===\n");
hero.showStatus();
System.out.println();
slime.showStatus();
}
}
実行結果
=== Java Quest スタート! ===
===== プレイヤー情報 =====
名前: 勇者アレン
位置: (0, 0)
HP: 100 / 100
攻撃力: 15
========================
===== プレイヤー情報 =====
名前: スライム
位置: (10, 5)
HP: 30 / 30
攻撃力: 5
========================
=== 戦闘開始! ===
勇者アレンが移動します
現在位置: (0, 0)
移動後: (10, 5)
勇者アレンの攻撃!
スライムは 15 のダメージを受けた!
残りHP: 15 / 30
スライムの攻撃!
勇者アレンは 5 のダメージを受けた!
残りHP: 95 / 100
勇者アレンの攻撃!
スライムは 15 のダメージを受けた!
残りHP: 0 / 30
スライムは倒れた...
=== 戦闘終了 ===
===== プレイヤー情報 =====
名前: 勇者アレン
位置: (10, 5)
HP: 95 / 100
攻撃力: 15
========================
===== プレイヤー情報 =====
名前: スライム
位置: (10, 5)
HP: 0 / 30
攻撃力: 5
========================
重要ポイントの整理
1. クラスとインスタンスの関係
Player hero = new Player();
- Player: クラス名(設計図の名前)
- hero: 変数名(このインスタンスに付ける名前)
- new Player(): インスタンスを新しく作る命令
たい焼きで例えると:
- クラス = たい焼きの型
-
new Player()= 型を使ってたい焼きを焼く - hero = 焼きあがったたい焼きに「hero」という名札を付ける
2. 同じクラスから複数のインスタンスを作れる
Player hero = new Player(); // 1つ目
Player slime = new Player(); // 2つ目
同じPlayerクラスから、heroとslimeという2つの別々のインスタンスを作りました。
それぞれ独立したデータを持っています。
3. ドット(.)でアクセスする
hero.name = "勇者アレン"; // フィールドへのアクセス
hero.showStatus(); // メソッドの呼び出し
インスタンスのフィールドやメソッドにアクセスするには、「インスタンス名.フィールド名」「インスタンス名.メソッド名()」のように書きます。
メモリ上ではどうなっている?
プログラムを実行すると、メモリ上では以下のようなことが起きています:
変数領域(スタック):
-
heroという変数が作られる -
slimeという変数が作られる - これらは「参照」(どこにインスタンスがあるかを示す情報)を保持
インスタンス領域(ヒープ):
-
new Player()で実際のインスタンスが作られる - 各インスタンスは独立したフィールドを持つ
- heroのインスタンスとslimeのインスタンスは別々の場所に存在
この仕組みは「参照型」と呼ばれ、第6回で詳しく学びます。
今は「変数とインスタンスは別の場所にある」ということだけ覚えておいてください。
手続き型とオブジェクト指向の比較
手続き型(Before)
// プレイヤーの変数
String playerName = "勇者";
int playerX = 0;
int playerY = 0;
int playerHp = 100;
// 敵の変数
String enemyName = "スライム";
int enemyX = 10;
int enemyY = 5;
int enemyHp = 30;
// 関数で処理(どのキャラクターの処理か分かりにくい)
movePlayer(playerX, playerY, 5, 3);
moveEnemy(enemyX, enemyY, -2, 1);
問題点:
- 変数とそれを処理する関数がバラバラ
- どの変数がどのキャラクターのものか分かりにくい
- キャラクターが増えるとコードが爆発的に複雑になる
オブジェクト指向(After)
// キャラクターをインスタンスとして作成
Player hero = new Player();
hero.name = "勇者";
hero.x = 0;
hero.y = 0;
hero.hp = 100;
Player slime = new Player();
slime.name = "スライム";
slime.x = 10;
slime.y = 5;
slime.hp = 30;
// それぞれのインスタンスが自分のメソッドを持つ
hero.move(5, 3); // 勇者が移動
slime.move(-2, 1); // スライムが移動
メリット:
- データ(フィールド)と機能(メソッド)が一つにまとまっている
- それぞれのキャラクターが独立して管理される
- キャラクターを追加しても、インスタンスを増やすだけ
- コードが読みやすく、保守しやすい
初学者がよくハマるポイント
❌ 間違い1:newを忘れる
Player hero; // インスタンスを作っていない!
hero.name = "勇者"; // エラー!NullPointerException
なぜエラーになる?
Player hero;だけでは、「Playerインスタンスを入れる箱」を用意しただけです。
中身(インスタンス)はまだ存在しません。
正しい書き方:
Player hero = new Player(); // newでインスタンスを作る
hero.name = "勇者"; // OK!
❌ 間違い2:クラス名とファイル名が一致していない
// ファイル名:Player.java
public class Plyer { // スペルミス!
// ...
}
エラーメッセージ:
エラー: クラスPlyerはpublic であり、ファイルPlyer.java で宣言する必要があります
ルール:
publicクラスの名前とファイル名は完全に一致させる必要があります(大文字小文字も含めて)。
❌ 間違い3:mainメソッドの中にクラスを書いてしまう
public class GameMain {
public static void main(String[] args) {
// mainメソッドの中にクラスは書けない!
public class Player { // エラー!
String name;
}
}
}
正しい書き方:
クラスは独立したファイルとして作成します。
- Player.java → Playerクラス
- GameMain.java → GameMainクラス
デバッグのヒント
NullPointerExceptionが出たら
Exception in thread "main" java.lang.NullPointerException
at GameMain.main(GameMain.java:10)
確認すること:
-
newでインスタンスを作成したか? - インスタンスにnullが入っていないか?
デバッグ方法:
Player hero = new Player();
// インスタンスがちゃんと作られているか確認
if (hero == null) {
System.out.println("heroがnullです!");
} else {
System.out.println("heroは正常に作成されました");
}
フィールドが初期値のままになっている
Player hero = new Player();
hero.showStatus(); // 名前がnull、HPが0と表示される
原因:
フィールドに値を設定し忘れています。
解決方法:
Player hero = new Player();
// フィールドに値を設定する
hero.name = "勇者";
hero.hp = 100;
hero.maxHp = 100;
// ... 他のフィールドも設定
hero.showStatus(); // 正しく表示される
注意:
次回以降で学ぶ「コンストラクタ」を使えば、この問題を解決できます。今は「フィールドに値を設定し忘れないこと」を意識しましょう。
実際に手を動かして、理解を深めましょう!
基本問題
問題1: Enemyクラスを作成しよう
Playerクラスを参考に、敵キャラクター用のEnemyクラスを作成してください。
要件:
- フィールド:name, x, y, hp, maxHp, attack
- メソッド:showStatus(), move(int dx, int dy), takeDamage(int damage)
解答例を見る
/**
* プレイヤーキャラクターを敵を表現するクラス
* RPGゲームにおける冒険者の状態と振る舞いを管理します。
*/
public class Enemy {
// ========================================
// フィールド(プレイヤーの「データ」)
// ========================================
/** プレイヤーの名前 */
String name;
/** プレイヤーのX座標(マップ上の横位置) */
int x;
/** プレイヤーのY座標(マップ上の縦位置) */
int y;
/** プレイヤーの体力(HP: Hit Point) */
int hp;
/** プレイヤーの最大体力 */
int maxHp;
/** プレイヤーの攻撃力 */
int attack;
// ========================================
// メソッド(プレイヤーの「機能」)
// ========================================
/**
* プレイヤーの現在の状態を表示します
* デバッグや確認用に使用します。
*/
void showStatus() {
System.out.println("===== プレイヤー情報 =====");
System.out.println("名前: " + name);
System.out.println("位置: (" + x + ", " + y + ")");
System.out.println("HP: " + hp + " / " + maxHp);
System.out.println("攻撃力: " + attack);
System.out.println("========================");
}
/**
* プレイヤーを移動させます
*
* @param dx X方向の移動量(正の数で右、負の数で左)
* @param dy Y方向の移動量(正の数で下、負の数で上)
*/
void move(int dx, int dy) {
// 現在の位置を表示
System.out.println(name + "が移動します");
System.out.println("現在位置: (" + x + ", " + y + ")");
// 座標を更新
x = x + dx; // または x += dx;
y = y + dy; // または y += dy;
// 移動後の位置を表示
System.out.println("移動後: (" + x + ", " + y + ")");
}
/**
* プレイヤーがダメージを受けます
*
* @param damage 受けるダメージ量(0以上の整数)
*/
void takeDamage(int damage) {
System.out.println(name + "は " + damage + " のダメージを受けた!");
// HPを減らす
hp = hp - damage; // または hp -= damage;
// HPが0未満にならないようにする
if (hp < 0) {
hp = 0;
}
// 現在のHPを表示
System.out.println("残りHP: " + hp + " / " + maxHp);
// HPが0になったら戦闘不能メッセージ
if (hp == 0) {
System.out.println(name + "は倒れた...");
}
}
}
問題2: 複数の敵を作成しよう
Enemyクラスを使って、以下の敵キャラクターを3体作成してください。
- スライム(HP: 30, 攻撃力: 5)
- ゴブリン(HP: 50, 攻撃力: 8)
- ドラゴン(HP: 200, 攻撃力: 30)
それぞれの状態を表示してください。
解答例を見る
/**
* 敵キャラクター確認用クラス
*/
public class EnemyDemo {
public static void main(String[] args) {
// スライム
Enemy slime = new Enemy();
slime.name = "スライム";
slime.hp = 30;
slime.maxHp = 30;
slime.attack = 5;
slime.showStatus();
// ゴブリン
Enemy goblin = new Enemy();
goblin.name = "ゴブリン";
goblin.hp = 50;
goblin.maxHp = 50;
goblin.attack = 8;
goblin.showStatus();
// ドラゴン
Enemy dragon = new Enemy();
dragon.name = "ドラゴン";
dragon.hp = 200;
dragon.maxHp = 200;
dragon.attack = 30;
dragon.showStatus();
}
}
実行結果
===== プレイヤー情報 =====
名前: スライム
位置: (0, 0)
HP: 30 / 30
攻撃力: 5
==================
===== プレイヤー情報 =====
名前: ゴブリン
位置: (0, 0)
HP: 50 / 50
攻撃力: 8
==================
===== プレイヤー情報 =====
名前: ドラゴン
位置: (0, 0)
HP: 200 / 200
攻撃力: 30
==================
応用問題
問題3: healメソッドを追加しよう
Playerクラスに、HPを回復するheal(int amount)メソッドを追加してください。
仕様:
- 指定された量だけHPを回復する
- 最大HPを超えないようにする
- 回復量と回復後のHPを表示する
ヒント:
void heal(int amount) {
// ここにコードを書く
// 最大HPを超えないようにするには、if文を使います
}
解答例を見る
public class Player {
//----↑↑既存のコード↑↑----
//----↓↓追加のコード↓↓----
/**
* HPを回復する処理
* 最大HPを超えないように制限する
*
* @param amount 回復するHP量
*/
public void heal(int amount) {
this.hp += amount;
if (this.hp > this.maxHp) {
this.hp = this.maxHp;
}
System.out.println(this.name + "は " + amount + " 回復した!");
}
}
問題4: isAliveメソッドを追加しよう
キャラクターが生きているかどうかを判定するisAlive()メソッドを追加してください。
仕様:
- 戻り値の型:boolean
- HPが1以上ならtrue、0ならfalseを返す
使用例:
if (hero.isAlive()) {
System.out.println(hero.name + "はまだ戦える!");
} else {
System.out.println(hero.name + "は倒れている...");
}
解答例を見る
public class Player {
//----↑↑既存のコード↑↑----
//----↓↓追加のコード↓↓----
/**
* キャラクターが生きているかどうかを判定
*
* @return 生きている場合true、戦闘不能の場合false
*/
public boolean isAlive() {
return this.hp > 0;
}
}
お疲れ様でした!第1回では、オブジェクト指向の基礎を学びました。
重要ポイント:
1.クラスは設計図、インスタンスは実体
- クラスは「型」を定義するもの
- インスタンスは
newで作成する実際のオブジェクト
2.クラスには「データ」と「機能」がある
- フィールド:オブジェクトが持つデータ
- メソッド:オブジェクトができる行動
3.一つのクラスから複数のインスタンスを作れる
- それぞれ独立したデータを持つ
- 同じメソッドを使える
4.オブジェクト指向のメリット
- コードの整理整頓ができる
- 保守性が向上する
- 現実世界をモデル化しやすい
まだ完璧じゃなくても大丈夫です。
次回以降、少しずつ理解が深まっていきます。
第2回:クラスとインスタンス - 設計図と実物の関係
次回は、今回学んだ内容をさらに深掘りします。
- インスタンスとは何か(メモリ上の動き)
- 参照型の基本
- 複数のインスタンスを配列で管理する方法
冒険はまだ始まったばかりです。一緒に頑張りましょう!
- Oracle Java Tutorials: Classes and Objects
- Java API仕様書(日本語版)
- 第1回:オブジェクト指向って何?(本記事)
- 第2回:配列とループ(次回)
- 第3回:フィールドとメソッド
- 第4回:コンストラクタ
- 第5回:カプセル化
- 第6回:参照型を理解する
- 第7回:継承
- 第8回:ポリモーフィズム
- 第9回:抽象クラスとインターフェース
- 第10回:総合演習 - RPGバトルシステム構築
© 2025 Java Quest - オブジェクト指向って何?RPGで理解する超入門
All Rights Reserved.
本記事は教育目的で作成されています。RPGの例を通じてプログラミング概念を楽しく学習できることを目指しています。










