🐈

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

に公開

はじめに

こんにちは。
プログラミング初心者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つは互いに独立したパッケージです。サブパッケージは、あくまクラス情報を整理するキーワードに過ぎません。
パッケージを親子関係や階層構造で捉えないよう、注意が必要です。

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」となります。
インターネットドメインを利用してパッケージを命名することで、大規模な開発でもパッケージ名の衝突を防ぐことができるのです。

6. クラスローダー

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

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

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

クラスファイルを階層構造にする理由は、「クラスローダー」の仕様に由来します。
Javaプログラムを起動する際は、コマンドラインにmainメソッドを含むクラスのFQCNを指定します。起動コマンドが実行されると、JVM内部のクラスローダーと言うプログラムが起動し、PC内からFQCNの名前を持つクラスファイルを捜索します。PC内の膨大なファイルの中から、クラスローダーはどうやって目的のファイルを見つけているのでしょう。
実は、クラスローダーは「クラスパス」と呼ばれるヒント情報を参照し、捜索範囲を絞っているのです。クラスパスは、コマンドで指定することができますが、特に指定がない場合は、起動コマンドを実行したフォルダがクラスパスとなります。
またクラスローダーは、クラスパスを起点に、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)
GitHubで編集を提案

Discussion