はじめに
こんにちは。
プログラミング初心者Wakinozaと申します。
Java勉強中に調べたことを記事にまとめています。
十分気をつけて執筆していますが、なにぶん初心者が書いた記事なので、理解が浅い点などあるかと思います。
記事を参考にされる方は、初心者の記事であることを念頭において、お読みいただけると幸いです。
間違い等あれば、指摘いただけると助かります。
対象読者
- Javaを勉強中の方
- Java Silver試験を勉強中の方
- Javaのパッケージの規則についてざっくり知りたい方
目次
1. パッケージとは
2. パッケージの構造
3. 完全修飾クラス名
4. パッケージの利点
5. パッケージの命名方法
6. クラスローダー
7. Java Silver試験用の補足
本文
1. パッケージとは
Javaには、コードを整理する仕組みがいくつか用意されています。
数十行程度のコードなら、mainメソッドにそのまま書いても問題ないでしょう。しかし、コードが数百行にもなれば、可読性が下がります。そんな時は、共通する挙動を「メソッド」にまとめて整理します。
しかしコードが長くなれば、いずれメソッド数も増え過ぎてしまいます。その場合は、関連するメソッドやフィールドを「クラス」にまとめて整理します。そしてさらにコードが長くなれば、クラスも増え過ぎて管理しずらくなります。そんな場合に利用するのが、「パッケージ」です。
ソースコードの先頭行にpackage文を記述することで、クラスを任意のパッケージに所属されることができます。
package zoo.animal;
//先頭行でパッケージ宣言することで、
//Dogクラスをzoo.animalパッケージに所属させました。
public class Dog{
//any code
}
パッケージ名は、「.」(ドット)をつけて、右側にサブパッケージをつけることができます。
上のコードで言うと、「zoo」がパッケージ名、「animal」はサブパッケージ名です。
2. パッケージの構造
パッケージの理解において重要なのは、パッケージに親子関係や階層関係がないという点です。
以下の2つのパッケージがあるとします。
- zoo.animal
- zoo.staff
一見するとフォルダ構造のように、zooパッケージの中に、animalとstaffの2つの子パッケージがあるようにイメージしがちですが、そうではありません。パッケージの中にパッケージを入れることはできませんし、2つは互いに関係のない独立したパッケージです。サブパッケージは、あくまクラス情報を整理するキーワードに過ぎません。
パッケージを親子関係や階層構造で捉えないように注意が必要です。
ちなみにパッケージ宣言をしていないクラスの状態は、「無名パッケージに属している」または「デフォルトパッケージに属している」と表現されます。デフォルトパッケージに属するクラスは、import文でインポートすることができません。
3. 完全修飾クラス名
パッケージに所属するクラスは、「パッケージ名.クラス名」が正式名称となります。この正式名称を、完全修飾クラス名(full qualified class name :FQCN)と言います。
上のコードで言うと、Dogクラスの完全修飾クラス名は、「zoo.animal.Dog」となります。
4. パッケージの利点
パッケージの利点は、主に3つあります。
1. クラスの整理
大規模なソフトウェアを開発すると、クラスだけでも数百・数千となり、コードの全体像を把握するのが困難になります。クラスにはそれぞれ関連の濃いものもあれば、関連が薄いものもあります。関連が濃いクラスを1つのパッケージにまとめて整理することで、コードの構造を捉えやすくなります。
2. 名前空間の提供
Javaには「同じソフトウェア上に、同じ"名前"を持つクラスを複数宣言することはできない」と言うルールがあります。この"名前"は、クラス名ではなく完全修飾クラス(FQCN)です。
仮に同じクラス名が複数存在しても、それぞれ別のパッケージに所属していれば、それぞれのFQCNは異なります。FQCNが異なれば別の名前と見なされるため、同じソフトウェア上に共存できます。
数百名もの開発者が作業する現場において、クラス名が衝突しないように協議するのは困難です。しかし、パッケージが独立した名前空間を提供しているおかげで、開発者はクラス名の衝突を心配せずに作業できるのです。
3. アクセス制御
アクセス制御には、同じパッケージ内からアクセスを許可する「protected」や「package protected」などの修飾子があります。パッケージを利用することで、アクセス制御の範囲を細かく調整できます。
次に、具体的なパッケージの命名方法を見ていきます。
5.パッケージの命名方法
パッケージ名は、Java識別子のルールに適合するものなら、開発者が自由につけることができます。しかし実際には、慣習として守られているルールが主に2つ存在します。
1. クラス名との衝突を避けるルール
パッケージ名のアルファベットは小文字にすることが一般的です。その理由は、パッケージ名とクラス名が同じ名前空間に属するからです。
同じ名前空間に存在するため、パッケージ名・サブパッケージ名・クラス名に同じ名前をつけることができません。しかし、Javaでは大文字と小文字を区別します。仮に、パッケージとクラスの両方を「animal」と命名したいとします。パッケージ名を先頭小文字の「animal」、クラス名を先頭大文字の「Animal」とすれば、同じ単語でも大文字小文字の違いで別の単語を判定され、名前の衝突が起こりません。
パッケージ名のアルファベットは小文字、クラス名のアルファベットは先頭大文字と言うルールを守ることで、パッケージ名とクラス名の衝突を気にせず、自由に命名することができるのです。
2. パッケージ同士の名前の衝突を避けるルール
同じクラス名があっても、パッケージ名が違えば区別できました。しかし、同じパッケージ名が複数存在すれば、その前提が崩れます。同じ名前のパッケージがそれぞれ同じクラス名を含んでいれば、FQCNは同一となり、区別できなくなります。こうした事態を防ぐため、同じソフトウェア上で、同じパッケージ名を複数利用しないようにする必要がありました。
複数の会社で開発を分担している場合、パッケージ名が衝突しないように協議するのは困難です。そこで、インターネットドメインをパッケージ名に利用するルールが生まれました。
インターネットドメインは、組織ごとに世界で1つしか存在しないため、世界全体でたったひとつの衝突のないパッケージ名を作ることができます。
具体的には、ドメインを逆さまに繋ぎ合わせてパッケージ名を作ります。「wakioza.com」がドメイン名だとすると、パッケージ名は「com.wakinoza」となります。サブパッケージ名に、製品名やプロジェクト名をつける場合もあります。プロジェクト名が「silver」とすると、パッケージ名は「com.wakinoza.silver」となります。
インターネットドメインを利用してパッケージを命名することで、大規模な開発でもパッケージ名の衝突を防ぐことができるのです。
蛇足ですが、いくつかのパッケージ名は、事実上予約済みであるため利用することはできません。代表的なのが、APIなどで使われる「java」です。そのほかに「javax」「com.oracle」「com.sun」なども実質予約済みなので、利用できないと考えた方が良いでしょう。
6. クラスローダー
パッケージ宣言後を、フォルダ内のクラスファイルを階層構造にする必要があります。
先ほど「2 パッケージの構造」でパッケージには階層構造ではないと言ってなかった?と思った方、鋭いですね。ここ、パッケージのややこしいところです。
確かにパッケージ自体に階層構造はありません。「zoo.animal」と「zoo.staff」は同じ単語を共有するパッケージであっても、それぞれ独立したパッケージで、親子関係や階層構造はありません。
しかし、クラスファイルの配置は、別です。パッケージの記述に則った階層構造を作り、適切な位置にクラスファイルを配置する必要があります。
ソフトウェア上のパッケージの構造と、フォルダ内のクラスファイルの配置構造は、分けて考える点を注意しなければなりません。
クラスファイルを階層構造にしなければならないのは、プログラム起動時の「クラスローダー」のためです。
Javaプログラムを起動する際は、コマンドラインにmainメソッドを含むクラスのFQCNを指定します。起動コマンドが実行されると、JVM内部のクラスローダーと言うプログラムが起動し、PC内からFQCNの名前を持つクラスファイルを捜索します。起動コマンドにフォルダ名も記述していないのに、クラスローダーはどうやって目的のファイルを見つけているのでしょう。
実は、クラスローダーは「クラスパス」と呼ばれるヒント情報を参照し、捜索範囲を絞っているのです。クラスパスは、コマンドで指定することもできますが、特に指定がない場合は、起動コマンドを実行したフォルダがクラスパスとなります。
またクラスローダーは、FQCNの階層構造を辿って、目的のクラスを捜索するという性質があります。そのため、パッケージに属したクラスファイルをクラスローダーに正しく見つけてもらうためには、コマンドを起動するクラスパスの下層に、パッケージ階層に対応したフォルダ階層を作り、その中に目的のクラスファイルを配置しておく必要があります。
例として、C:¥内の「cord」フォルダをクラスパスとして、「zoo.animal.Dog」のDogクラスを配置すると以下のような階層構造になります。
+-- C:¥
+-- cordフォルダ (ここで起動すると、ここがクラスパスとなる)
+-- zooフォルダ
+-- animalフォルダ
+-- Dog.class (ここにクラスファイルを配置する)
まとめると、パッケージ化したクラスを起動する場合は、以下の点に気をつける必要があります。
- パッケージ階層に対応したフォルダ階層を作り、クラスファイルを配置する
- パッケージ階層の一番上のフォルダが見える位置(クラスパスに指定したい位置)で、コマンドを起動する
7. Java sliver SE11試験用の補足
- パッケージは、必ずソースコードの先頭に書く必要があります。パッケージの上にあって良いのは、コメント文もしくは空白行だけです。パッケージ文より上の行に他のコードを記述すると、コンパイルエラーになります
まとめ
- パッケージにクラス所属させて整理することで、クラス名の衝突を防止し、アクセス制御を細やかに実装することができる
- 「パッケージ名.クラス名」を完全修飾クラス名(FQCN)と言う
- パッケージ名には、名前の衝突を避けるルールが存在する
- パッケージ所属後は、クラスファイルを階層構造にまとめる必要がある
記事は以上です。
次回は、インポートについてまとめる予定です。
最後までお読みいただき、ありがとうございました。
参考情報一覧
この記事は以下の情報を参考にして執筆しました。
- [オラクル認定資格教科書 JavaプログラマSilverSE11]
- [スッキリわかるJava入門 第4版]
- [パーフェクトJava 改訂3版]
- [9.1 パッケージとインポート~Java Basic編](最終更新 2023-11-05)(https://qiita.com/KenyaSaitoh/items/d6a55136de5d8f20cd67) (参照 2025-04-23)