Javaの「パッケージ」をざっくりまとめてみた[Java初心者]



## はじめに

こんにちは。
プログラミング初心者Wakinozaと申します。
Java勉強中に調べたことを記事にまとめています。

十分気をつけて執筆していますが、なにぶん初心者が書いた記事なので、理解が浅い点などあるかと思います。
記事を参考にされる方は、初心者の記事であることを念頭において、お読みいただけると幸いです。
間違い等あれば、指摘いただけると助かります。

## 対象読者

- Javaを勉強中の方
- Java Silver試験を勉強中の方
- Javaのパッケージの規則についてざっくり知りたい方

## 目次
1\. パッケージとは
2\. パッケージの構造
3\. 完全修飾クラス名
4\. パッケージの利点
5\. パッケージの命名方法
6\. クラスローダー
7\. Java Silver試験用の補足

## 本文

### 1\. パッケージとは

Javaには、コードを整理する仕組みがいくつか用意されています。
数十行程度のコードなら、mainメソッドにそのまま書いても問題ないでしょう。しかし、コードが数百行にもなれば、可読性が下がります。そんな時は、共通する挙動を「メソッド」にまとめて整理します。
しかしコードが長くなれば、いずれメソッド数も増え過ぎてしまいます。その場合は、関連するメソッドやフィールドを「クラス」にまとめて整理します。そしてさらにコードが長くなれば、クラスも増え過ぎて管理しずらくなります。そんな場合に利用するのが、「パッケージ」です。

ソースコードの先頭行にpackage文を記述することで、クラスを任意のパッケージに所属されることができます。

```Java
package zoo.animal;  
 //先頭行でパッケージ宣言することで、
 //Dogクラスをzoo.animalパッケージに所属させました。
public class Dog{  
  //any code
}
```

パッケージ名は、「.」(ドット)をつけて、右側にサブパッケージをつけることができます。
上のコードで言うと、「zoo」がパッケージ名、「animal」はサブパッケージ名です。

### 2\.  パッケージの構造

パッケージの理解において重要なのは、パッケージに親子関係や階層関係がないという点です。
以下の２つのパッケージがあるとします。

- zoo.animal
- zoo.staff

一見するとフォルダ構造のように、zooパッケージの中に、animalとstaffの２つの子パッケージがあるようにイメージしがちですが、そうではありません。パッケージの中にパッケージを入れることはできませんし、２つは互いに関係のない独立したパッケージです。サブパッケージは、あくまクラス情報を整理するキーワードに過ぎません。
パッケージを親子関係や階層構造で捉えないように注意が必要です。

:::note info
ちなみにパッケージ宣言をしていないクラスの状態は、「無名パッケージに属している」または「デフォルトパッケージに属している」と表現されます。デフォルトパッケージに属するクラスは、import文でインポートすることができません。
:::

### 3\.  完全修飾クラス名

パッケージに所属するクラスは、「パッケージ名.クラス名」が正式名称となります。この正式名称を、完全修飾クラス名（full qualified class name :FQCN）と言います。
上のコードで言うと、Dogクラスの完全修飾クラス名は、「zoo.animal.Dog」となります。

### 4\.  パッケージの利点

パッケージの利点は、主に３つあります。

1\. クラスの整理
大規模なソフトウェアを開発すると、クラスだけでも数百・数千となり、コードの全体像を把握するのが困難になります。クラスにはそれぞれ関連の濃いものもあれば、関連が薄いものもあります。関連が濃いクラスを１つのパッケージにまとめて整理することで、コードの構造を捉えやすくなります。

2\. 名前空間の提供
Javaには「同じソフトウェア上に、同じ"名前"を持つクラスを複数宣言することはできない」と言うルールがあります。この"名前"は、クラス名ではなく完全修飾クラス（FQCN）です。
仮に同じクラス名が複数存在しても、それぞれ別のパッケージに所属していれば、それぞれのFQCNは異なります。FQCNが異なれば別の名前と見なされるため、同じソフトウェア上に共存できます。
数百名もの開発者が作業する現場において、クラス名が衝突しないように協議するのは困難です。しかし、パッケージが独立した名前空間を提供しているおかげで、開発者はクラス名の衝突を心配せずに作業できるのです。

3\. アクセス制御
アクセス制御には、同じパッケージ内からアクセスを許可する「protected」や「package protected」などの修飾子があります。パッケージを利用することで、アクセス制御の範囲を細かく調整できます。

次に、具体的なパッケージの命名方法を見ていきます。

### 5\.パッケージの命名方法

パッケージ名は、Java識別子のルールに適合するものなら、開発者が自由につけることができます。しかし実際には、慣習として守られているルールが主に2つ存在します。

1\. クラス名との衝突を避けるルール
パッケージ名のアルファベットは小文字にすることが一般的です。その理由は、パッケージ名とクラス名が同じ名前空間に属するからです。
同じ名前空間に存在するため、パッケージ名・サブパッケージ名・クラス名に同じ名前をつけることができません。しかし、Javaでは大文字と小文字を区別します。仮に、パッケージとクラスの両方を「animal」と命名したいとします。パッケージ名を先頭小文字の「animal」、クラス名を先頭大文字の「Animal」とすれば、同じ単語でも大文字小文字の違いで別の単語を判定され、名前の衝突が起こりません。
パッケージ名のアルファベットは小文字、クラス名のアルファベットは先頭大文字と言うルールを守ることで、パッケージ名とクラス名の衝突を気にせず、自由に命名することができるのです。

2\. パッケージ同士の名前の衝突を避けるルール
同じクラス名があっても、パッケージ名が違えば区別できました。しかし、同じパッケージ名が複数存在すれば、その前提が崩れます。同じ名前のパッケージがそれぞれ同じクラス名を含んでいれば、FQCNは同一となり、区別できなくなります。こうした事態を防ぐため、同じソフトウェア上で、同じパッケージ名を複数利用しないようにする必要がありました。
複数の会社で開発を分担している場合、パッケージ名が衝突しないように協議するのは困難です。そこで、インターネットドメインをパッケージ名に利用するルールが生まれました。
インターネットドメインは、組織ごとに世界で１つしか存在しないため、世界全体でたったひとつの衝突のないパッケージ名を作ることができます。
具体的には、ドメインを逆さまに繋ぎ合わせてパッケージ名を作ります。「wakioza.com」がドメイン名だとすると、パッケージ名は「com.wakinoza」となります。サブパッケージ名に、製品名やプロジェクト名をつける場合もあります。プロジェクト名が「silver」とすると、パッケージ名は「com.wakinoza.silver」となります。
インターネットドメインを利用してパッケージを命名することで、大規模な開発でもパッケージ名の衝突を防ぐことができるのです。

:::note info
蛇足ですが、いくつかのパッケージ名は、事実上予約済みであるため利用することはできません。代表的なのが、APIなどで使われる「java」です。そのほかに「javax」「com.oracle」「com.sun」なども実質予約済みなので、利用できないと考えた方が良いでしょう。
:::

### 6\. クラスローダー
パッケージ宣言後を、フォルダ内のクラスファイルを階層構造にする必要があります。

先ほど「２ パッケージの構造」でパッケージには階層構造ではないと言ってなかった？と思った方、鋭いですね。ここ、パッケージのややこしいところです。

確かにパッケージ自体に階層構造はありません。「zoo.animal」と「zoo.staff」は同じ単語を共有するパッケージであっても、それぞれ独立したパッケージで、親子関係や階層構造はありません。
しかし、クラスファイルの配置は、別です。パッケージの記述に則った階層構造を作り、適切な位置にクラスファイルを配置する必要があります。
ソフトウェア上のパッケージの構造と、フォルダ内のクラスファイルの配置構造は、分けて考える点を注意しなければなりません。

クラスファイルを階層構造にしなければならないのは、プログラム起動時の「クラスローダー」のためです。
Javaプログラムを起動する際は、コマンドラインにmainメソッドを含むクラスのFQCNを指定します。起動コマンドが実行されると、JVM内部のクラスローダーと言うプログラムが起動し、PC内からFQCNの名前を持つクラスファイルを捜索します。起動コマンドにフォルダ名も記述していないのに、クラスローダーはどうやって目的のファイルを見つけているのでしょう。
実は、クラスローダーは「クラスパス」と呼ばれるヒント情報を参照し、捜索範囲を絞っているのです。クラスパスは、コマンドで指定することもできますが、特に指定がない場合は、起動コマンドを実行したフォルダがクラスパスとなります。
またクラスローダーは、FQCNの階層構造を辿って、目的のクラスを捜索するという性質があります。そのため、パッケージに属したクラスファイルをクラスローダーに正しく見つけてもらうためには、コマンドを起動するクラスパスの下層に、パッケージ階層に対応したフォルダ階層を作り、その中に目的のクラスファイルを配置しておく必要があります。
例として、C:¥内の「cord」フォルダをクラスパスとして、「zoo.animal.Dog」のDogクラスを配置すると以下のような階層構造になります。

```plain
+-- C:¥
  +-- cordフォルダ (ここで起動すると、ここがクラスパスとなる)
    +-- zooフォルダ
      +-- animalフォルダ
        +-- Dog.class （ここにクラスファイルを配置する）
```

まとめると、パッケージ化したクラスを起動する場合は、以下の点に気をつける必要があります。

- パッケージ階層に対応したフォルダ階層を作り、クラスファイルを配置する
- パッケージ階層の一番上のフォルダが見える位置（クラスパスに指定したい位置）で、コマンドを起動する

### 7\. Java sliver SE11試験用の補足

- パッケージは、必ずソースコードの先頭に書く必要があります。パッケージの上にあって良いのは、コメント文もしくは空白行だけです。パッケージ文より上の行に他のコードを記述すると、コンパイルエラーになります


## まとめ

- パッケージにクラス所属させて整理することで、クラス名の衝突を防止し、アクセス制御を細やかに実装することができる
- 「パッケージ名.クラス名」を完全修飾クラス名（FQCN）と言う
- パッケージ名には、名前の衝突を避けるルールが存在する
- パッケージ所属後は、クラスファイルを階層構造にまとめる必要がある

---------------

記事は以上です。
次回は、インポートについてまとめる予定です。
最後までお読みいただき、ありがとうございました。

##  参考情報一覧

この記事は以下の情報を参考にして執筆しました。

- [オラクル認定資格教科書 JavaプログラマSilverSE11]
- [スッキリわかるJava入門 第４版]
- [パーフェクトJava 改訂3版]
- [9.1 パッケージとインポート～Java Basic編](最終更新 2023-11-05)(https://qiita.com/KenyaSaitoh/items/d6a55136de5d8f20cd67) (参照 2025-04-23)