Javaのパッケージ(package)は「関連するクラスやインターフェースを整理し、名前の衝突を防ぐためのフォルダのような仕組み」です。Javaのプログラムが大規模になると、たくさんのクラスやインターフェースが必要になってきます。これらを整理せずに置いておくとコードがごちゃごちゃしてきて管理が非常に難しくなります(あのクラスはどこにあったっけ・・・?このクラス名は有効?すでに使用済・・・?など)。
そこでJavaでは「package:パッケージ」という仕組み使って、関連するクラスやインターフェースをまとめて整理し、必要に応じてパッケージを後からインポートして利用する方式を採用します。

パッケージには、1:標準で提供されるもの、2:他の人から提供されるもの、3:独自に作成するものなど、いくつか種類も分かれますので、この点に関してもページ後半で詳しくご説明します。
このページではJavaにおけるパッケージとは何か?実際の使い方や作り方までを網羅的にわかりやすくご説明します。
Javaのパッケージ(package)とは?
パッケージ(package)はクラスやインターフェースを1つにまとめて整理したものと理解すればOK。そのうえで、単にまとめるだけではなく、様々な目的で利用される性質を持つ!ということを冒頭でご説明しておきます。

まず初めにパッケージの基本的な仕組みと、その利用目的について簡単にご説明しておきます。
パッケージの役割1:クラスやインターフェースの整理
Javaのプログラムが大規模になると、多くのクラスやインターフェースが生成されるようになります。パッケージはこれらを論理的なグループとしてまとめ管理しやすくするために用いられます。
参考 クラスとは? / インターフェースとは?
例えば、java.utilというパッケージには、コレクションフレームワークやユーティリティクラスが含まれていますがこのように同じ目的のクラスを1つのグループにまとめるために使われるというのがパッケージの第1の役割です。
パッケージの役割2:名前空間の提供
第2の役割は名前空間を提供するということ。名前空間というと難しく聞こえますが、要するに同じ名前のクラスを異なるパッケージに同時に存在させることを可能にするということです。

基本的に同じクラス名は一度使ったら別のソースコードでは利用できません。が、パッケージを分ければ利用可能!ということ。
例えば、Javaには日付を扱うためのクラスが2つあります。1つはjava.utilパッケージに属するDateクラス、もう1つはjava.sqlパッケージに属するDateクラス。これらは同じ名前のクラスですが、異なるパッケージに属しているため、1つのプログラムに同居することが可能になっています。
public class DateSample {
public static void main(String[] args) {
// java.util.Date を使って現在の日時を取得
java.util.Date utilDate = new java.util.Date();
System.out.println("java.util.Date: " + utilDate);
// java.sql.Date を使って現在の日付を取得(時刻は 00:00:00)
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("java.sql.Date: " + sqlDate);
}
}
このように、パッケージは名前空間を提供し、同じ名前のクラスが異なるパッケージに存在することを可能にする、というのがパッケージの2つ目の役割。
JVMはパッケージ名とクラス名を合わせた完全修飾名でクラスを指定する
JVMがクラスを読み込むときの仕組みは以下の通り。(この動きを知っていると、パッケージの役割がより鮮明度が高く理解できるかと思います。)
このように、JVMはクラスを読み込む際にパッケージ名とクラス名を合わせた完全修飾名を内部的に使用し、内部表現に変換して効率よくクラスファイルを検索・読み込みする仕組みを採用しています。
パッケージの役割3:アクセス制御
第3の役割はアクセス制御です。Javaのパッケージはアクセス修飾子(参考 アクセス修飾子とは?)と組み合わせることで、アクセス制御をより細かく管理するために使われます。
パッケージ自体が特定のアクセス修飾子を持つわけではありませんが、アクセス修飾子とパッケージを組み合わせることで、クラスやメンバー(フィールドやメソッド)のアクセス範囲を決定します。
| アクセス修飾子 | 同一クラス | 同一パッケージ内の他のクラス | サブクラス(同一パッケージ) | サブクラス(異なるパッケージ) | 他のパッケージのクラス |
|---|---|---|---|---|---|
| public | 〇 | 〇 | 〇 | 〇 | 〇 |
| protected | 〇 | 〇 | 〇 | 〇(※1) | ✕ |
| デフォルト | 〇 | 〇 | 〇 | ✕ | ✕ |
| private | 〇 | ✕ | ✕ | ✕ | ✕ |
※1: protectedメンバーは、異なるパッケージのサブクラスからアクセスする場合、サブクラス内でthisを使ってアクセスする必要がある。
サンプルコード アクセス修飾子とパッケージの関係の簡易サンプル
// パッケージAのクラス
package com.example.packageA;
public class ClassA {
public String publicField = "Public Field";
protected String protectedField = "Protected Field";
String defaultField = "Default Field"; // デフォルトアクセス(パッケージプライベート)
private String privateField = "Private Field";
public void showFields() {
System.out.println("publicField: " + publicField);
System.out.println("protectedField: " + protectedField);
System.out.println("defaultField: " + defaultField);
System.out.println("privateField: " + privateField);
}
}
// パッケージAの別のクラス
package com.example.packageA;
public class ClassB {
public void accessFields() {
ClassA obj = new ClassA();
System.out.println(obj.publicField); // アクセス可能
System.out.println(obj.protectedField); // アクセス可能
System.out.println(obj.defaultField); // アクセス可能
// System.out.println(obj.privateField); // アクセス不可(コンパイルエラー)
}
}

ClassBは、ClassAのpublic、protected、およびデフォルト(パッケージプライベート)フィールドにアクセスできますが、privateフィールドにはアクセスできません。
// パッケージBのクラス
package com.example.packageB;
import com.example.packageA.ClassA;
public class ClassC extends ClassA {
public void accessFields() {
ClassA obj = new ClassA();
System.out.println(obj.publicField); // アクセス可能
// System.out.println(obj.protectedField); // アクセス不可(インスタンス経由の場合)
// System.out.println(obj.defaultField); // アクセス不可
// System.out.println(obj.privateField); // アクセス不可
System.out.println(this.protectedField); // アクセス可能(サブクラス経由の場合)
}
}

ClassCは、ClassAのpublicフィールドにアクセスできます。同様に、ClassCはClassAのサブクラスであるため、protectedフィールドにもアクセスできますが、defaultおよびprivateフィールドにはアクセスできません。
パッケージはプログラムを1つにまとめるだけでなく、名前空間の提供とアクセス制御も同時に行っている!ものだとおさえておきましょう。

で、このパッケージは自分で作るものもあれば、誰かから提供されるものもあります。続いてはパッケージの種類についてご説明します。
パッケージの種類
Javaのパッケージは大きく分けて以下4つに分類されます。ザックリいえば、①誰かが作ってくれたもの か ②自分で作ったもの の2種類ということになるのですが、ここでは正式に以下4分類をご紹介します。
1. 標準パッケージ(Standard Packages)
標準パッケージは、Java開発キット(JDK)に含まれており、基本的な機能やAPIを提供するもの。これらのパッケージはJavaプログラムの基盤を構成し、多くの一般的な操作に使用されます。
ポイント 標準パッケージの具体例
- java.lang
- Javaの基本的なクラスをまとめたもの。例:
String,Math,Integerなど。
- Javaの基本的なクラスをまとめたもの。例:
- java.util
- コレクションフレームワークやユーティリティクラスをまとめたもの。例:
ArrayList,HashMap,Dateなど。
- コレクションフレームワークやユーティリティクラスをまとめたもの。例:
- java.io: 入出力操作に関するクラスをまとめたもの。例:
File,InputStream,OutputStreamなど。
2. 拡張パッケージ(Extension Packages)
拡張パッケージは、標準ライブラリには含まれないが、Javaプラットフォームによって公式に提供される追加の機能を含むパッケージ。javaxで始まることが多く、特定の用途に対応したクラスやインターフェースをまとめたものです。
ポイント 拡張パッケージの具体例
- javax.servlet
- サーブレットAPIを提供し、Webアプリケーションの開発に使用される。例:
HttpServlet,ServletRequest,ServletResponseなど。
- サーブレットAPIを提供し、Webアプリケーションの開発に使用される。例:
- javax.swing
- GUI(グラフィカルユーザインタフェース)コンポーネントを提供。例:
JFrame,JButton,JLabelなど。
- GUI(グラフィカルユーザインタフェース)コンポーネントを提供。例:
- javax.xml
- XML処理に関するクラスを提供。例:
DocumentBuilder,XPath,Transformerなど。
- XML処理に関するクラスを提供。例:
3. カスタムパッケージ(Custom Packages)
カスタムパッケージは、開発者が独自に作成するパッケージです。プロジェクト固有のクラスやインターフェースを整理し、再利用性を高めるために使用されます。
詳しくは後述しますが、以下のようにクラスファイルの最初の個所でpackageキーワードを用いて宣言します。
package com.example.myapp;
public class MyClass {
// クラスの内容
}
4. サードパーティパッケージ(Third-Party Packages)
サードパーティパッケージは、外部のライブラリやフレームワークに含まれるパッケージです。これらのパッケージは、特定の機能や拡張機能を提供し、Javaエコシステムの一部として広く利用されています。
以下のようなcom.google.gson: JSON処理のためのGsonライブラリなどが一例です。
import com.google.gson.Gson;
public class JsonExample {
public static void main(String[] args) {
Gson gson = new Gson();
String json = gson.toJson(new Person("John", 30));
System.out.println(json);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}

上記分類を頭に入れたところで、ここからはカスタムパッケージの作り方と、パッケージの使い方についてご説明します。
カスタムパッケージの作り方
ここからは、カスタムパッケージを作成する手順を順を追って1つ1つ説明していきます。
ステップ1:パッケージの宣言
カスタムパッケージを作成するには、クラスファイルの最初にpackageキーワードを利用してパッケージを宣言します。パッケージ名は通常、逆ドメイン名形式を使用して一意にします。例えば、com.example.myappというパッケージを作成する場合、以下のように宣言します。
package com.example.myapp;
public class MyClass {
public void sayHello() {
System.out.println("Hello from MyClass in com.example.myapp package!");
}
}
ステップ2:ディレクトリ構造の作成
次に、パッケージ名に対応するディレクトリ構造をプロジェクト内に作成します。パッケージcom.example.myappに対応するディレクトリ構造は以下の通り。
project_root/
└── src/
└── com/
└── example/
└── myapp/
└── MyClass.java
パッケージ名に特段の決まりはありません。ルールは、ソースファイルを配置するディレクトリと構造を対応させるということだけなので、必ずしもドメイン名を逆にした文字列ではなくてもOKではあります。
一方、Javaのパッケージ名はドメイン名の逆順で作成する!というのがもはや暗黙の了解になっているためパッケージ名を付けるときはこの無難な命名にするのがよいでしょう。
ステップ3:実際のコードを作成する
次に、カスタムパッケージを使うクラスを作成し、これらのクラスを他のクラスからインポートして使用します。インポートを行う場合はimportキーワードを利用します。

importキーワードの使い方は後ほど再度詳しくご説明します。
サンプルコード カスタムパッケージのクラス
// ファイル: src/com/example/myapp/MyClass.java
package com.example.myapp;
public class MyClass {
public void sayHello() {
System.out.println("Hello from MyClass in com.example.myapp package!");
}
}
サンプルコード カスタムパッケージを使用するクラス
// ファイル: src/com/example/anotherpackage/Main.java
package com.example.anotherpackage;
import com.example.myapp.MyClass;
public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.sayHello();
}
}
ステップ4:パッケージをコンパイルして実行
最後にカスタムパッケージを含むプロジェクトをコンパイルします。プロジェクトのルートディレクトリで以下のコマンドを実行して、すべてのJavaファイルをコンパイルします。
javac -d bin src/com/example/myapp/MyClass.java src/com/example/anotherpackage/Main.java
-d binオプションは、コンパイルされたクラスファイルをbinディレクトリに出力することを指定しています。
次に、コンパイルされたクラスを実行します。
java -cp bin com.example.anotherpackage.Main
このコマンドは、クラスパス(-cp bin)を指定して、Mainクラスを実行します。出力は以下のようになります。
Hello from MyClass in com.example.myapp package!
まとめると、カスタムパッケージを作成する手順は以下の通り。
- クラスファイルの最初にパッケージを宣言する。
- パッケージ名に対応するディレクトリ構造を作成する。
- カスタムパッケージのクラスを作成し、他のクラスからインポートして使用する。
- プロジェクトをコンパイルし、実行する。
パッケージの使い方/インポート方法

最後にパッケージの使い方についてご説明しておきます。
パッケージはimport文でインポートすることで簡単に利用できるようになります。
import文の使い方
他のパッケージのクラスを使用するためには、import文を使ってクラスをインポートします。import文ではパッケージ名とクラス名を指定します。
以下は、java.utilパッケージのArrayListクラスをインポートして利用する方法です。ドットで区切ったときの最後の部分がクラス名であり、それ以前がパッケージ名となります。
import java.util.ArrayList;
public class ImportExample {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
System.out.println(list);
}
}
Tips:名前の衝突を避ける方法
異なるパッケージに同じ名前のクラスが存在する場合、名前の衝突が発生することがあります。このような場合には、クラスの完全修飾名(パッケージ名を含むクラス名)を使用することで、名前の衝突を避けることができます。
以下の例では、java.util.Dateとjava.sql.Dateの両方を使用していますが、名前の衝突を避けるために、完全修飾名を使用しています。
import java.util.Date;
public class DateExample {
public static void main(String[] args) {
// java.util.Dateを使用
Date utilDate = new Date();
System.out.println("java.util.Date: " + utilDate);
// java.sql.Dateを使用
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("java.sql.Date: " + sqlDate);
}
}
Tips:ワイルドカード(*)を使ったインポート
パッケージ内の複数のクラスをインポートする場合、ワイルドカード(*)を使用することで、パッケージ内のすべてのクラスを一度にインポートすることが可能です。

必要なクラスだけを個別にインポートする方がコードの可読性が向上しますが、一応Tipsとしてご紹介。
以下は、java.utilパッケージ内のすべてのクラスをインポートする例です。
import java.util.*;
public class WildcardImportExample {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
System.out.println(list);
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
System.out.println(map);
}
}
import文で「*」を使うと、そのパッケージ内にある全てのクラスやインターフェースが利用可能になるという意味ですが、実際にインポートされるのは、あくまでもそのパッケージに含まれるクラスやインターフェースだけという点に注意が必要です。
このように、「*」を使っても、対象となるのは指定したパッケージに含まれるクラスやインターフェースだけであり、サブパッケージやその他の要素は含まれません。
Tips:実はimportしなくてもクラスは使える
実は、フル修飾名(完全修飾名)を使えば、importしなくてもクラスは使えます!Javaでは、クラスを使うときに次のどちらかで指定できます:
| 方法 | 書き方 | 例 |
|---|---|---|
| importする | import java.util.Date; のように宣言して、あとは Date と書くだけで使える | Date d = new Date(); |
| importしない | クラス名の前に パッケージ名をすべて書く(=フル修飾名) | java.util.Date d = new java.util.Date(); |
✅ 実際の例(↓のように、import文がなくても問題なく使えるのがポイントです。)
public class Example {
public static void main(String[] args) {
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
System.out.println("utilDate: " + utilDate);
System.out.println("sqlDate: " + sqlDate);
}
}
無名パッケージ(default package)
無名パッケージ(default package)とは、Javaソースファイルに package 宣言を書かずにクラスやインターフェースを定義したとき、暗黙的に属するパッケージのことです。下記のように package 宣言を記述しないで書いたソースファイル内のクラスは、すべて無名パッケージに含まれます。
// パッケージ宣言なし
public class Sample {
// ...
}
無名パッケージの特徴・注意点
- 原則として同一ソースディレクトリのみ有効
無名パッケージ内のクラスは、同じディレクトリに置かれたクラス同士からのみ参照(import)できます。複数のディレクトリに無名パッケージのクラスがあっても、それら同士を参照し合うことはできません。 - 規模拡大に向かない
無名パッケージは可視性が極めて限定的であり、複数のパッケージ間でコードを整理することが難しいため、大規模なプロジェクトには向いていません。小規模なサンプルコードや学習用のコードなど、パッケージ分けを意識しないケースで一時的に使われることが多いです。 - IDEやビルドツールとの相性
一般的なIDE(Eclipse、IntelliJ IDEA、VS Codeなど)やビルドツール(Maven、Gradle)を使う場合、無名パッケージ内のクラスが存在するとソース管理が煩雑になりやすいです。プロジェクト開発時には通常、命名したパッケージを使うことを推奨します。 - アクセス制御の面
無名パッケージは、パッケージ単位でのアクセス制御が難しいという面があります。特定のクラスだけを狙ってアクセス制限をかけるといったパッケージスコープの活用ができないので、アクセス制御の観点でもあまり使われません。
サブパッケージとは?
サブパッケージは、基本的なパッケージの「下位」にあるパッケージです。見た目の階層構造はあるものの、実際には親パッケージとは独立した別のパッケージとして扱われます。具体的に見ていきましょう。
サブパッケージの構造
サブパッケージと親パッケージの違い
注意点とTips
- インポートの注意
親パッケージの「*」を使ったインポートはサブパッケージには適用されないので、サブパッケージ内のクラスを使う場合は、個別にインポートするか、サブパッケージ自体を「*」でインポートする必要があります。 - 名前の階層はあくまで見た目
サブパッケージは名前に階層があるだけで、親パッケージとの「継承関係」や「包含関係」があるわけではありません。各パッケージは完全に独立して管理されます。 - 整理のメリット
サブパッケージを使うことで、コードが増えてもどこにどの機能があるかが分かりやすくなり、保守性が向上します。例えば、並行処理に関するクラスはjava.util.concurrentにまとめ、その他のユーティリティはjava.utilにまとめるといった具合です。
サブパッケージは、プログラムを機能や目的ごとに整理するための便利な手法です。親パッケージと見た目で階層構造になっていますが、実際には別々のパッケージとして扱われるため、インポートの際には注意が必要です。こうした特徴を理解することで、より効率的にJavaのコードを管理・整理できるようになります。
