信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
Javaのパッケージ — クラスに「住所」をつける仕組み
なぜJavaにはパッケージがあるのか。完全修飾名・ディレクトリ構造・importの意味を根本原理から解説する初心者向け記事。
一言結論
パッケージは「名前の衝突」を防ぐ名前空間。完全修飾名はクラスの住所であり、importはその住所の省略記法にすぎない。
最初に:「名前が被る」問題
Javaでプログラムを書いていると、StringやListといったクラス名を当たり前のように使う。でもちょっと立ち止まって考えてほしい。世界中に何百万人もいるJava開発者が、みんな好きな名前でクラスを作っている。
// あなたが作ったクラス
class Logger { ... }
// ライブラリAのLogger
class Logger { ... }
// ライブラリBのLogger
class Logger { ... }
全員がLoggerと名付けたら、コンパイラはどれを使えばいいか判断できない。
この問題はJavaが生まれた1990年代から予測されていた。その解決策が パッケージ だ。
パッケージとは「住所」だ
パッケージは、クラスに住所をつける仕組みだ。
東京都渋谷区○○1-2-3 の 田中太郎
住所がなければ「田中太郎」が世界中にいても届けられない。住所があれば一意に特定できる。
Javaではこれをクラス名の前に . でつなげて表現する:
java.lang.String
com.example.myapp.Logger
org.apache.commons.lang3.StringUtils
java.lang.String は「java.lang パッケージにある String クラス」という意味だ。これを 完全修飾名(Fully Qualified Class Name / FQCN) と呼ぶ。
完全修飾名を使えば、たとえ世界中にLoggerクラスがあっても:
com.example.myapp.Logger logger1 = new com.example.myapp.Logger();
org.slf4j.Logger logger2 = org.slf4j.LoggerFactory.getLogger(...);
と書けば、どちらのLoggerかを完全に区別できる。
ディレクトリ構造がパッケージを表す
パッケージ名は、ファイルの置き場所(ディレクトリ)と完全に一致する 必要がある。
src/
└── com/
└── example/
└── myapp/
└── Logger.java ← パッケージは com.example.myapp
Logger.java の中身はこう書く:
package com.example.myapp; // ← ディレクトリと完全に一致させる
public class Logger {
// ...
}
なぜディレクトリと一致させなければならないのか?
答えはシンプルで、コンパイラとJVMが「クラスファイルを探す」ときにこのルールを使うからだ。
com.example.myapp.Logger が必要になったとき、コンパイラは:
- クラスパス(クラスを探す場所)の中から
com/example/myapp/Logger.classを探す
パッケージ名の . をスラッシュに置き換えたパスだ。もしディレクトリ構造とパッケージ名がずれていたら、クラスが見つからずエラーになる。
❌ Logger.javaをsrcの直下に置いて package com.example.myapp と書く → コンパイル・実行時にエラー
✅ src/com/example/myapp/Logger.java に置いて package com.example.myapp と書く → OK
importは「住所の省略宣言」だ
完全修飾名を毎回書くのは大変だ。java.util.ArrayList<java.lang.String> なんて何度も書きたくない。そこでimportを使う:
import java.util.ArrayList;
// これ以降は省略形で書ける
ArrayList<String> list = new ArrayList<>();
// importしなければ毎回フルネームが必要
java.util.ArrayList<String> list2 = new java.util.ArrayList<>();
import は「このクラスは完全修飾名の代わりに短縮名で書く」という宣言だ。コンパイル時に完全修飾名に展開されるだけで、実行に影響はまったくない。
❌ 誤解: importするとそのクラスがメモリに読み込まれる
✅ 正解: importはただの「省略記法の宣言」。クラス読み込みは実行時に必要になったとき
ワイルドカードimportもある:
import java.util.*; // java.utilパッケージの全クラスを省略記法で使う
ただし「どのクラスを使っているか」が分かりにくくなるため、明示的なimportを好む開発者も多い。
java.langだけは特別
Stringを使うとき、import java.lang.String; を書かない。書かなくても使える。
これは特例で、java.lang パッケージだけが 自動的にimportされる。java.lang はJavaの根幹を担うパッケージで、String, Integer, Object, System, Math などが含まれている。これらは頻繁すぎて毎回importを書くのが非現実的なため、言語仕様レベルで自動importが保証されている。
パッケージ名の慣習
パッケージ名には一般的な慣習がある:
ドメイン名を逆さにして先頭に使う
例: example.com → com.example.myapp
なぜドメイン名を逆にするのか?ドメイン名は世界中で一意だからだ。com.exampleというパッケージ名を使えるのはexample.comの所有者だけ、という自然な一意性が生まれる。
Javaの標準ライブラリが java.* や javax.* で始まるのも同じ理由で、Javaを開発したSun Microsystems(現Oracle)のドメインに由来している。
まとめ
パッケージ = クラスの「住所」(名前空間)。名前の衝突を防ぐ
完全修飾名 = パッケージ名 + クラス名(例: java.lang.String)
ディレクトリ構造 = パッケージと完全に一致する(コンパイラが探せるように)
import = 完全修飾名を省略するための宣言(実行には影響しない)
java.lang = 唯一の自動import対象(Javaの根幹クラス群)
名前の衝突という現実問題に対して、「住所をつける」という直感的な解決策がパッケージだ。仕様を暗記するより、この思想を理解しておくと、パッケージ周りのエラーが出たときに何が起きているか迷わなくなる。