パッケージの全体像
パッケージは「クラスを論理的にグループ化し、名前が衝突しないようにする仕組み」です。ファイルの先頭に package com.example.app; のように宣言し、ディレクトリ構造と一致させます。利用側から見ると「完全修飾名(例:com.example.app.User)」で一意にクラスを指せるようになり、大規模開発でも整理と再利用がしやすくなります。
基本構文とディレクトリ配置
パッケージ宣言とクラス定義
// src/main/java/com/example/app/User.java
package com.example.app;
public class User {
public final String name;
public User(String name) { this.name = name; }
}
Javapackage はファイル先頭に1回だけ書きます。省略すると「デフォルトパッケージ」になりますが、実務では推奨されません(import やアクセス制御が不便になり、ビルドツールとの相性も悪くなります)。
ディレクトリ構造との一致
src/main/java/
└── com/
└── example/
└── app/
└── User.java
パッケージ名はディレクトリの階層に直結します。ビルドやクラスパスの探索はこの前提で動くため、「宣言と置き場所の不一致」はコンパイルや実行で失敗の原因になります。
import と完全修飾名の使い分け(重要ポイントの深掘り)
別パッケージからの利用
// src/main/java/com/example/Main.java
package com.example;
import com.example.app.User; // 短い名前で使う宣言
public class Main {
public static void main(String[] args) {
User u = new User("sato"); // import で短く書ける
com.example.app.User u2 = new com.example.app.User("suzuki"); // 完全修飾名でも可
System.out.println(u.name + ", " + u2.name);
}
}
Javaimport は「ソースコード内で単純名を使えるようにする」宣言です。なくても完全修飾名で書けますが、可読性が落ちます。java.lang(String, Math など)は常に見えるため import 不要です。
同名クラスの衝突を明示的に解決
異なるパッケージに同名クラス(例:java.util.Date と java.sql.Date)がある場合、片方は import、もう片方は完全修飾名にして衝突を避けます。ワイルドカード import の多用は衝突の温床になるため、基本は個別 import を選びます。
アクセス修飾子とパッケージ境界(重要ポイントの深掘り)
可視性のルール
package com.example.lib;
public class PublicApi { /* どこからでも見える */ }
class InternalHelper { /* 同パッケージ内のみ(package-private) */ }
Java- public: どのパッケージからも参照可能。ライブラリの公開 API。
- package-private(修飾子なし): 同じパッケージからのみ参照可能。内部ヘルパーに適切。
- protected: 同パッケージ+サブクラスから参照可能(継承前提の設計向け)。
- private: クラス内のみ。
「外に見せる最小限だけ public、内部は package-private で隠す」が基本設計です。パッケージを「モジュールの境界」として意識すると、変更に強くなります。
命名規約と設計指針
パッケージ名の付け方
逆ドメイン名 + プロジェクト + 機能
例:com.company.project.user、org.example.util.text
Java- すべて小文字、短くわかりやすく。
- 組織やアプリの単位から階層化し、機能単位で細分化。
- 省略形や曖昧な名前(
utils,commonだけ)は避け、具体性を持たせる。
デフォルトパッケージは避ける
package を省略した「デフォルトパッケージ」は、クラスパスやツール連携で扱いづらく、他パッケージから参照も不便です。学習用の小さなサンプル以外では必ずパッケージを宣言します。
実用例で身につける
例 1: 複数パッケージで役割分離
// src/main/java/com/example/app/model/User.java
package com.example.app.model;
public record User(String name, int age) {}
// src/main/java/com/example/app/service/UserService.java
package com.example.app.service;
import com.example.app.model.User;
public class UserService {
public boolean isAdult(User u) { return u.age() >= 18; }
}
// src/main/java/com/example/App.java
package com.example;
import com.example.app.model.User;
import com.example.app.service.UserService;
public class App {
public static void main(String[] args) {
var u = new User("sato", 20);
var svc = new UserService();
System.out.println(svc.isAdult(u)); // true
}
}
Javaモデルとサービスをパッケージで分けることで、依存方向と役割が明確になります。
例 2: 内部実装を隠す(package-private)
// src/main/java/com.example.calc/Adder.java
package com.example.calc;
public class Adder {
public int add(int a, int b) { return Internal.add(a, b); }
}
class Internal { // 外部から見えない
static int add(int a, int b) { return a + b; }
}
Java外に見せる API を絞り、内部実装は同パッケージ内に閉じ込めます。差し替えや最適化がしやすくなります。
例 3: テストと本体の配置(参考)
src/main/java/com/example/...
src/test/java/com/example/...
テストは本体と同じパッケージに置くと、package-private な内部にアクセスできます。Junit などのテストフレームワークとの相性も良くなります。
仕上げのアドバイス(重要部分のまとめ)
パッケージは「構造と境界」。宣言とディレクトリを一致させ、役割ごとに階層化します。公開は最小限(public)、内部は package-private で隠す。import は個別指定を基本にして衝突を避け、完全修飾名で明示的に併用する判断軸を持つ。デフォルトパッケージは使わず、逆ドメイン+機能で小文字の明確な命名に統一する——この設計を守れば、規模が大きくなっても読みやすく安全な構成を保てます。
