
Java道|パッケージを階層に分ける
パッケージを階層で整理すれば、クラスの住所がもっと分かりやすくなる。
サブパッケージを理解すると、大きなJavaプログラムでも、役割ごとにすっきり管理できるようになります。
Javaでプログラムを作っていると、最初のうちはクラスの数が少ないため、1つか2つのパッケージだけでも十分に整理できます。
たとえば、pa パッケージに学習用のクラスをまとめたり、pb パッケージに実行用のクラスを置いたりするだけでも、基本的な構成は理解しやすくなります。
しかし、学習が進んでプログラムの規模が大きくなると、クラスの数が増え、役割も細かく分かれていきます。
鬼滅の刃風にたとえると、最初は鬼殺隊士の名簿だけを管理していればよかったものが、だんだん次のような巻物が増えていくイメージです。
| 巻物の種類 | Javaでのクラスのイメージ |
|---|---|
| 鬼殺隊士の名簿 | DemonSlayer クラス |
| 呼吸の型の記録 | BreathingStyle クラス |
| 任務の指令書 | Mission クラス |
| 日輪刀の台帳 | NichirinSword クラス |
| 戦闘記録 | BattleManager クラス |
| 補助処理の巻物 | SupportTool クラス |
これらをすべて1つのパッケージに入れてしまうと、どこに何があるのか探しにくくなります。
そこで役立つのが、パッケージを階層に分ける という考え方です。
Javaでは、パッケージの中にさらに細かい分類を作れます。
たとえば、pa という大きなパッケージの下に sub という分類を作ると、パッケージ名は次のようになります。
package pa.sub;これは、pa の中をさらに sub という単位で細かく整理している形です。
鬼滅の刃風に言えば、pa という鬼殺隊本部の大きな棚の中に、sub という補助巻物専用の小さな棚を作るようなものです。
このように階層化すると、クラスの役割が見えやすくなり、大きなプログラムでも整理しやすくなります。
パッケージを階層に分けるとは何か
Javaのパッケージは、1段だけの分類ではありません。
必要に応じて、パッケージ名をピリオドでつなぎ、階層構造として表せます。
たとえば、次のような形です。
| パッケージ名 | 意味 |
|---|---|
| pa | 大きな分類 |
| pa.sub | pa の下にある sub という細かい分類 |
pa.sub と書くことで、pa の中をさらに sub で分類していることを表します。
ただし、ここで大切なのは、pa と pa.sub は別々のパッケージとして扱われる という点です。
名前だけ見ると、pa.sub は pa の一部のように感じます。
しかし、Javaのコード上では、pa と pa.sub は独立した別のパッケージです。
| 書き方 | Javaでの扱い |
|---|---|
| pa | pa パッケージ |
| pa.sub | pa.sub パッケージ |
| pa.Fighter | pa パッケージの Fighter クラス |
| pa.sub.Fighter | pa.sub パッケージの Fighter クラス |
鬼滅の刃風にたとえると、pa は本部の大きな棚です。
pa.sub は、その中にある補助任務専用の棚です。
見た目には親子関係があるように見えますが、Javaのパッケージとしては、それぞれ別の棚として扱います。
サブパッケージとは何か
パッケージの下の階層に作るパッケージを、サブパッケージ と呼びます。
たとえば、pa.sub の場合、sub が pa の下にあるサブパッケージです。
| 用語 | 例 | 意味 |
|---|---|---|
| パッケージ | pa | 大きな分類 |
| サブパッケージ | pa.sub | pa の下にある細かい分類 |
| ピリオド | . | 階層を表す区切り |
鬼滅の刃風にたとえると、鬼殺隊本部の棚をさらに細かく分けるようなものです。
| Javaのパッケージ | 鬼滅の刃風のイメージ |
|---|---|
| pa | 鬼殺隊本部の基本棚 |
| pa.sub | pa の中にある補助任務用の小棚 |
| pa.character | 隊士情報用の小棚 |
| pa.battle | 戦闘処理用の小棚 |
| pa.training | 修行記録用の小棚 |
サブパッケージを使うと、似た役割のクラスをさらに細かく整理できます。
クラスが少ないうちは、pa だけでも十分です。
しかし、クラスが増えてくると、pa の中にすべてを入れるよりも、pa.sub や pa.battle のように分けたほうが見通しがよくなります。
なぜパッケージを階層化するのか
小さなプログラムなら、1つのパッケージにすべてのクラスを入れても、それほど困らないことがあります。
しかし、プログラムが大きくなると、クラスの数が増え、役割も細かくなります。
鬼滅の刃風の学習プログラムを考えると、次のようなクラスが出てきそうです。
| クラス | 役割 |
|---|---|
| DemonSlayer | 鬼殺隊士を表す |
| BreathingStyle | 呼吸の型を表す |
| Mission | 任務を表す |
| BattleManager | 戦闘の進行を管理する |
| TrainingMenu | 修行内容を管理する |
| SupportTool | 補助処理を行う |
これらをすべて同じ pa パッケージに入れると、クラスが増えたときに探しにくくなります。
そこで、役割ごとにサブパッケージへ分けます。
| パッケージ | 入れるクラスの例 |
|---|---|
| pa.character | 鬼殺隊士、柱、支援隊士など |
| pa.battle | 戦闘処理、技の処理など |
| pa.training | 修行メニュー、訓練記録など |
| pa.item | 日輪刀、防具、道具など |
| pa.sub | 補助的なクラス、細かい機能のクラス |
このように分けると、どこに何があるのかが見えやすくなります。
| 状況 | 階層化しない場合 | 階層化した場合 |
|---|---|---|
| クラス数が増えたとき | 1つの場所に集まりすぎる | 役割ごとに整理できる |
| 修正するとき | 探すのに時間がかかる | 関係する場所を見つけやすい |
| 複数人で開発するとき | 担当範囲が分かりにくい | 機能ごとに分担しやすい |
| 同名クラスがあるとき | 衝突しやすい | パッケージ名で区別できる |
パッケージの階層化は、大きなプログラムを見やすく保つための大切な工夫です。
package文の書き方
サブパッケージを使うときは、package 文に階層をそのまま書きます。
たとえば、pa の下に sub というサブパッケージを作る場合は、次のように書きます。
package pa.sub;この1行によって、そのファイル内のクラスは pa.sub パッケージに属します。
| package 文 | 所属するパッケージ |
|---|---|
| package pa; | pa パッケージ |
| package pa.sub; | pa.sub パッケージ |
| package pa.battle; | pa.battle パッケージ |
ここで大切なのは、package 文はソースファイルの先頭に書くということです。
package pa.sub;
class Sample7
{
}package 文の前にクラス定義を書くことはできません。
また、クラス定義の途中に package 文を書くこともできません。
鬼滅の刃風にたとえると、巻物の一番上に「この巻物は pa.sub の棚に置く」と札を付けるようなものです。
package文とフォルダ構成は対応する
パッケージを階層に分けた場合、フォルダ構成もそれに対応させます。
package pa.sub; と書いた場合、ファイルは pa フォルダの下にある sub フォルダに保存します。
作業中フォルダを C:\Java\13 とすると、構成は次のようになります。
C:\Java\13
└─ pa
└─ sub
└─ Sample7.java| package 文 | 保存場所 |
|---|---|
| package pa; | pa フォルダ |
| package pa.sub; | pa\sub フォルダ |
pa.sub というパッケージ名は、フォルダ構成では pa\sub に対応します。
| パッケージ名の部分 | フォルダ |
|---|---|
| pa | pa フォルダ |
| sub | pa の下の sub フォルダ |
この対応がずれると、Javaがクラスの場所を正しく見つけにくくなります。
鬼滅の刃風に言えば、巻物の札には pa.sub と書いてあるのに、実際には pa の棚だけに置いてしまうようなものです。
sub の棚まで用意して、そこに置く必要があります。
↓クリックすると拡大表示されます。

今回のサンプルで使う構成
今回は、pa.sub パッケージの中に Sample7.java を置きます。
Sample7.java の中には、鬼滅の刃風にアレンジした次の2つのクラスを書きます。
| クラス名 | 役割 |
|---|---|
| DemonSlayer | 鬼殺隊士の名前と階級を管理する |
| Sample7 | main メソッドを持ち、処理を開始する |
DemonSlayer は、鬼殺隊士の情報を表すクラスです。
| DemonSlayer の要素 | 内容 |
|---|---|
| name | 鬼殺隊士の名前 |
| rank | 階級 |
| setSlayer() | 名前と階級を設定する |
| show() | 現在の情報を表示する |
Sample7 は、DemonSlayer オブジェクトを作成して、show() を呼び出す実行用のクラスです。
| Sample7 の役割 | 内容 |
|---|---|
| main メソッドを持つ | プログラムの開始地点 |
| DemonSlayer を作る | 鬼殺隊士オブジェクトを作成する |
| show() を呼ぶ | 鬼殺隊士の情報を表示する |
どちらのクラスも、同じ Sample7.java の中にあり、先頭に package pa.sub; があるため、pa.sub パッケージに属します。
パッケージの階層を確認する
ファイル名:Sample7.java
package pa.sub;
// 鬼殺隊士クラス
class DemonSlayer
{
private String name;
private String rank;
public DemonSlayer()
{
name = "未登録";
rank = "階級未設定";
System.out.println("鬼殺隊士を準備しました。");
}
public void setSlayer(String n, String r)
{
name = n;
rank = r;
System.out.println("隊士名を" + name + "、階級を" + rank + "にしました。");
}
public void show()
{
System.out.println("隊士名は" + name + "です。");
System.out.println("階級は" + rank + "です。");
}
}
class Sample7
{
public static void main(String[] args)
{
DemonSlayer slayer1 = new DemonSlayer();
slayer1.show();
}
}実行結果
鬼殺隊士を準備しました。
隊士名は未登録です。
階級は階級未設定です。このプログラムでは、DemonSlayer オブジェクトを作成し、show() を呼び出しています。
コンストラクタでは、name に 未登録、rank に 階級未設定 を設定しています。
public DemonSlayer()
{
name = "未登録";
rank = "階級未設定";
System.out.println("鬼殺隊士を準備しました。");
}そのため、show() を呼び出すと、初期状態の隊士情報が表示されます。
public void show()
{
System.out.println("隊士名は" + name + "です。");
System.out.println("階級は" + rank + "です。");
}package pa.sub; が表していること
Sample7.java の先頭には、次の1行があります。
package pa.sub;この1行によって、Sample7.java に書かれている DemonSlayer クラスと Sample7 クラスは、どちらも pa.sub パッケージに属します。
| クラス | 所属パッケージ | 役割 |
|---|---|---|
| DemonSlayer | pa.sub | 鬼殺隊士の情報を管理する |
| Sample7 | pa.sub | main メソッドで処理を開始する |
同じ pa.sub パッケージ内にあるため、Sample7 から DemonSlayer をそのまま使えます。
DemonSlayer slayer1 = new DemonSlayer();ここでは、pa.sub.DemonSlayer と書いていません。
同じパッケージ内にあるため、クラス名だけで自然に使えます。
鬼滅の刃風にたとえると、Sample7 の任務指令書と DemonSlayer の隊士名簿は、どちらも pa.sub の棚にあるため、同じ棚の巻物として扱いやすいということです。
コンパイルの方法
サブパッケージを使う場合も、コンパイルは作業中フォルダから行います。
今回のフォルダ構成は次のとおりです。
C:\Java\13
└─ pa
└─ sub
└─ Sample7.java作業中フォルダが C:\Java\13 の場合、次のように入力します。
PS C:\Java\13>javac pa\sub\Sample7.java| コマンド | 意味 |
|---|---|
| javac pa\sub\Sample7.java | pa\sub フォルダ内の Sample7.java をコンパイルする |
パッケージが pa.sub なので、ファイルの場所は pa\sub\Sample7.java になります。
コンパイルが成功すると、Sample7.java の中にあるクラスに対応して、class ファイルが作成されます。
C:\Java\13
└─ pa
└─ sub
├─ Sample7.java
├─ DemonSlayer.class
└─ Sample7.class| ソース内のクラス | 作成される class ファイル |
|---|---|
| DemonSlayer | DemonSlayer.class |
| Sample7 | Sample7.class |
1つのソースファイルの中に複数のクラスがある場合、コンパイル後はクラスごとに .class ファイルが作成されます。
↓クリックすると拡大表示されます。

実行の方法
実行するときは、main メソッドを持つクラスを指定します。
今回、main メソッドを持っているのは Sample7 クラスです。
ただし、Sample7 は pa.sub パッケージに属しています。
そのため、実行時にはサブパッケージまで含めて指定します。
PS C:\Java\13>java pa.sub.Sample7| 実行コマンド | 意味 |
|---|---|
| java pa.sub.Sample7 | pa.sub パッケージの Sample7 クラスを実行する |
ここで java pa.Sample7 と書いてはいけません。
Sample7 は pa パッケージではなく、pa.sub パッケージに属しているからです。
| 間違いやすい実行 | 正しい実行 |
|---|---|
| java Sample7 | パッケージ名がない |
| java pa.Sample7 | sub が抜けている |
| java pa.sub.Sample7 | 正しい |
鬼滅の刃風にたとえると、Sample7 の巻物は pa の棚ではなく、pa の中の sub の棚に置かれています。
そのため、実行時にも pa.sub.Sample7 と、正しい住所で指定する必要があります。
通常のパッケージとの違い
サブパッケージを使っても、考え方そのものは通常のパッケージと大きく変わりません。
違うのは、階層が1段深くなることです。
| 項目 | 通常のパッケージ | サブパッケージ |
|---|---|---|
| package 文 | package pa; | package pa.sub; |
| 保存先 | pa フォルダ | pa\sub フォルダ |
| コンパイル | javac pa\SampleX.java | javac pa\sub\Sample7.java |
| 実行 | java pa.SampleX | java pa.sub.Sample7 |
| クラスの完全名 | pa.SampleX | pa.sub.Sample7 |
つまり、指定する名前やフォルダが少し長くなるだけです。
その代わり、クラスの役割を細かく整理できるようになります。
paとpa.subは別パッケージ
ここは特に大切です。
pa と pa.sub は、名前が似ていても別々のパッケージです。
| パッケージ | Javaでの扱い |
|---|---|
| pa | pa パッケージ |
| pa.sub | pa.sub パッケージ |
たとえば、次の2つのクラスがあったとします。
| クラスの完全名 | 意味 |
|---|---|
| pa.DemonSlayer | pa パッケージの DemonSlayer クラス |
| pa.sub.DemonSlayer | pa.sub パッケージの DemonSlayer クラス |
どちらも DemonSlayer というクラス名ですが、所属するパッケージが違うため、Javaでは別のクラスとして扱います。
鬼滅の刃風にたとえると、同じ DemonSlayer という名前の巻物でも、pa の棚にあるものと、pa.sub の棚にあるものは別の巻物です。
また、pa と pa.sub が別パッケージであることは、アクセス修飾子を学ぶときにも重要になります。
たとえば、無指定のクラスやメンバは、基本的に同じパッケージ内から使えます。
しかし、pa と pa.sub は別パッケージなので、名前が似ていても同じパッケージ扱いにはなりません。
サブパッケージを使うメリット
サブパッケージを使うと、プログラム全体の構造が整理しやすくなります。
| メリット | 内容 |
|---|---|
| 機能ごとに整理しやすい | 似た役割のクラスをまとめられる |
| 見通しがよくなる | どこに何があるか把握しやすい |
| クラス数が増えても管理しやすい | 大規模開発に向いている |
| 名前の衝突を避けやすい | 同名クラスでもパッケージ名で区別できる |
| 担当範囲を分けやすい | チーム開発で作業しやすい |
鬼滅の刃風のプログラムなら、次のような分け方もできます。
| パッケージ | 役割 |
|---|---|
| pa.character | 鬼殺隊士や柱のクラス |
| pa.battle | 戦闘処理のクラス |
| pa.training | 修行メニューのクラス |
| pa.item | 日輪刀や道具のクラス |
| pa.support | 補助処理のクラス |
このように整理すると、クラスが増えても全体の構造が見えやすくなります。
図:package pa.sub; とフォルダ構成の関係
↓クリックすると拡大表示されます。

この図が示していること
この図では、package pa.sub; とフォルダ構成の対応を表しています。
左側では、C:\Java\13 の下に pa フォルダがあり、その下に sub フォルダがあります。
Sample7.java は、その sub フォルダの中に保存されています。
中央には package pa.sub; があり、右側には pa.sub パッケージに属する DemonSlayer クラスと Sample7 クラスがあります。
| 要素 | 対応 |
|---|---|
| package pa.sub; | pa.sub パッケージに属する |
| pa フォルダ | pa に対応する |
| sub フォルダ | sub に対応する |
| Sample7.java | pa\sub の中に保存する |
この図から分かることは、パッケージ名のピリオドが、フォルダ構成では階層として表れるということです。
図:コンパイルと実行の流れ
↓クリックすると拡大表示されます。

この図が示していること
この図では、pa.sub パッケージを使ったときのコンパイルと実行の流れを表しています。
左側には、pa\sub フォルダの中に Sample7.java が保存されています。
Sample7.java の先頭には package pa.sub; が書かれています。
中央では、次のコマンドでコンパイルしています。
javac pa\sub\Sample7.javaコンパイル後には、pa\sub フォルダの中に DemonSlayer.class と Sample7.class が作成されます。
| クラス | class ファイル |
|---|---|
| DemonSlayer | DemonSlayer.class |
| Sample7 | Sample7.class |
実行するときは、main メソッドを持つ Sample7 を、サブパッケージまで含めて指定します。
java pa.sub.Sample7この図から分かることは、サブパッケージを使う場合、コンパイルではフォルダ階層を指定し、実行ではパッケージ名をピリオドでつないで指定するということです。
つまずきやすいポイント
サブパッケージを使うときは、次のようなところで混乱しやすいです。
| つまずきやすい点 | 気をつけたいこと |
|---|---|
| pa.sub は pa と同じパッケージだと思う | Javaでは pa と pa.sub は別パッケージ |
| pa フォルダだけ作ってしまう | package pa.sub; なら pa\sub が必要 |
| 実行時に java pa.Sample7 と書いてしまう | 正しくは java pa.sub.Sample7 |
| package 文と保存場所がずれる | package pa.sub; なら pa\sub に保存する |
| pa.sub.Sample7 と書く意味が分からない | pa.sub パッケージの Sample7 クラスという意味 |
特に大切なのは、package 文、保存場所、実行時の指定を一致させることです。
| 項目 | 今回の例 |
|---|---|
| package 文 | package pa.sub; |
| 保存場所 | pa\sub\Sample7.java |
| コンパイル | javac pa\sub\Sample7.java |
| 実行 | java pa.sub.Sample7 |
この4つがつながると、サブパッケージの基本がかなり分かりやすくなります。
アクセス修飾子の学習にもつながる
サブパッケージを理解すると、あとで学ぶアクセス修飾子の意味も見えやすくなります。
たとえば、Javaでは、無指定のクラスやメンバは、基本的に同じパッケージ内から利用できます。
ここで、pa と pa.sub を同じパッケージだと思っていると、アクセス範囲の理解で混乱しやすくなります。
| 関係 | 同じパッケージか |
|---|---|
| pa 内のクラス同士 | 同じパッケージ |
| pa.sub 内のクラス同士 | 同じパッケージ |
| pa と pa.sub | 別パッケージ |
pa と pa.sub は名前がつながっています。
しかし、Javaでは別パッケージです。
鬼滅の刃風にたとえると、同じ本部内に見えても、pa の棚と pa.sub の棚は管理区画が違うということです。
そのため、アクセスできる範囲を考えるときにも、別の棚として扱う必要があります。
パッケージを階層に分けるときの大事な感覚
パッケージを階層に分けるときは、次の感覚を持つと分かりやすくなります。
| 考え方 | 内容 |
|---|---|
| ピリオドは階層を表す | pa.sub のように書く |
| フォルダも階層にする | pa\sub のように配置する |
| サブパッケージは別パッケージ | pa と pa.sub は同じではない |
| 実行時は完全な名前を書く | java pa.sub.Sample7 |
| 役割ごとに整理できる | クラス数が増えても見通しがよくなる |
鬼滅の刃風に言えば、pa は大きな本部の棚、pa.sub はその中の補助任務専用の棚です。
Sample7.java の先頭に package pa.sub; と書くなら、実際の保存場所も pa\sub にします。
そして実行するときは、pa.sub.Sample7 と住所を最後まで指定します。
この流れが分かると、Javaのパッケージ階層はかなり自然に理解できます。
