【Java】パッケージ(package/import)の基本を3分でわかりやすく
Javaのパッケージ(package)は「 関連するクラスやインターフェースを整理し、名前の衝突を防ぐためのフォルダのような仕組み 」です。Javaのプログラムが大規模になると、たくさんのクラスやインターフェースが必要になってきます。これらを整理せずに置いておくとコードがごちゃごちゃしてきて管理が非常に難しくなります(あのクラスはどこにあったっけ・・・?このクラス名は有効?すでに使用済・・・?など)。
そこでJavaでは「package:パッケージ」という仕組み使って、関連するクラスやインターフェースをまとめて整理し、必要に応じてパッケージを後からインポートして利用する方式を採用します。
- Javaのパッケージ(package)とは?
- パッケージの役割1:クラスやインターフェースの整理
- パッケージの役割2:名前空間の提供
- パッケージの役割3:アクセス制御
- 1. 標準パッケージ(Standard Packages)
- 2. 拡張パッケージ(Extension Packages)
- 3. カスタムパッケージ(Custom Packages)
- 4. サードパーティパッケージ(Third-Party Packages)
- ステップ1:パッケージの宣言
- ステップ2:ディレクトリ構造の作成
- ステップ3:実際のコードを作成する
- ステップ4:パッケージをコンパイルして実行
- import文の使い方
- Tips:名前の衝突を避ける方法
- Tips:ワイルドカード(*)を使ったインポート
- Tips:実はimportしなくてもクラスは使える
- 無名パッケージの特徴・注意点
- サブパッケージの構造
- サブパッケージと親パッケージの違い
- 注意点とTips
Javaのパッケージ(package)とは?
パッケージ(package)はクラスやインターフェースを1つにまとめて整理したものと理解すればOK。そのうえで、単にまとめるだけではなく、様々な目的で利用される性質を持つ!ということを冒頭でご説明しておきます。
パッケージの役割1:クラスやインターフェースの整理例えば、 java.util というパッケージには、コレクションフレームワークやユーティリティクラスが含まれていますがこのように同じ目的のクラスを1つのグループにまとめるために使われるというのがパッケージの第1の役割です。
パッケージの役割2:名前空間の提供第2の役割は名前空間を提供するということ。名前空間というと難しく聞こえますが、要するに同じ名前のクラスを異なるパッケージに同時に存在させることを可能にするということです。
例えば、Javaには日付を扱うためのクラスが2つあります。1つは java.util パッケージに属する Date クラス 、もう1つは java.sql パッケージに属する Date クラス 。これらは同じ名前のクラスですが、異なるパッケージに属しているため、1つのプログラムに同居することが可能になっています。
- java.util.Date : 一般的な日付と時間を扱うためのクラス。
- java.sql.Date : データベースの日時型を扱うためのクラス。
このように、パッケージは名前空間を提供し、同じ名前のクラスが異なるパッケージに存在することを可能にする、というのがパッケージの2つ目の役割。
ポイント JVMはパッケージ名とクラス名を合わせた完全修飾名でクラスを指定する
- JVMによるクラスの読み込み
- 名前の解決: ソースコードでは、クラスは「 java.lang.String 」のようにドットで区切られています。JVMはこれを元に、どのパッケージのどのクラスかを特定します。
- 内部表現への変換: JVM内部では、この完全修飾名が「 java/lang/String 」のようにスラッシュ( / )で区切られた形式に変換されます。この形式は、ファイルシステム上のディレクトリ構造(例えば、 java/lang/String.class )と対応しています。
- クラスローダーの役割: クラスローダーが、この内部表現を用いて適切な場所(例えばJARファイルやディレクトリ)からクラスファイルを探し、メモリに読み込みます。
- なぜこの方法が採用されているか
- 効率的な検索: 内部表現に変換することで、ファイルシステム上のフォルダ構造と一致し、効率よくクラスファイルを見つけられます。
- 一意の識別: パッケージ名を含めた完全修飾名により、同名のクラスが異なるパッケージに存在しても混乱なく識別できます。
第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); >>- 同じパッケージ内のクラス
ClassB は、 ClassA の public 、 protected 、およびデフォルト(パッケージプライベート)フィールドにアクセスできますが、 private フィールドにはアクセスできません。
- 異なるパッケージのクラス
ClassC は、 ClassA の public フィールドにアクセスできます。同様に、 ClassC は ClassA のサブクラスであるため、 protected フィールドにもアクセスできますが、 default および private フィールドにはアクセスできません。
まとめ Javaのパッケージの役割・必要性
- クラス名の衝突を回避できる同じクラス名を使いたい場合でも、パッケージを異なるものにすれば衝突しにくくなります。
- ソースコードを整理しやすい論理的に関連するクラスをまとめることで、コードの見通しが良くなり、保守・管理が楽になります。
- アクセス制御を細かく設定できる同一パッケージ内かどうかでアクセス修飾子を調整できるため、クラスやメソッドの公開範囲を制御しやすくなります。
- 再利用性や共有が容易になるパッケージごとにまとまったライブラリとして扱えるため、他のプロジェクトで再利用しやすくなります。
パッケージの種類
Javaのパッケージは大きく分けて以下4つに分類されます。ザックリいえば、①誰かが作ってくれたもの か ②自分で作ったもの の2種類ということになるのですが、ここでは正式に以下4分類をご紹介します。
1. 標準パッケージ(Standard Packages)標準パッケージは、 Java開発キット(JDK)に含まれており、基本的な機能やAPIを提供するもの。 これらのパッケージはJavaプログラムの基盤を構成し、多くの一般的な操作に使用されます。
ポイント 標準パッケージの具体例
- java.lang
- Javaの基本的なクラスをまとめたもの。例: String , Math , Integer など。
- java.util
- コレクションフレームワークやユーティリティクラスをまとめたもの。例: ArrayList , HashMap , Date など。
- java.io: 入出力操作に関するクラスをまとめたもの。例: File , InputStream , OutputStream など。
拡張パッケージは、 標準ライブラリには含まれないが、Javaプラットフォームによって公式に提供される追加の機能を含むパッケージ 。 javax で始まることが多く、特定の用途に対応したクラスやインターフェースをまとめたものです。
ポイント 拡張パッケージの具体例
- javax.servlet
- サーブレットAPIを提供し、Webアプリケーションの開発に使用される。例: HttpServlet , ServletRequest , ServletResponse など。
- javax.swing
- GUI(グラフィカルユーザインタフェース)コンポーネントを提供。例: JFrame , JButton , JLabel など。
- javax.xml
- XML処理に関するクラスを提供。例: DocumentBuilder , XPath , Transformer など。
カスタムパッケージは、 開発者が独自に作成するパッケージ です。プロジェクト固有のクラスやインターフェースを整理し、再利用性を高めるために使用されます。
詳しくは後述しますが、以下のようにクラスファイルの最初の個所で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:パッケージの宣言カスタムパッケージを作成するには、クラスファイルの最初に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!"); >>Javaでパッケージを宣言するときは、必ずソースファイルの先頭行で宣言する必要があります。
import com.example.packageA.ClassA; package com.example.packageB; // コンパイルエラーが発生する public class ClassC < // sample > ステップ2:ディレクトリ構造の作成次に、パッケージ名に対応するディレクトリ構造をプロジェクト内に作成します。パッケージ com.example.myapp に対応するディレクトリ構造は以下の通り。
project_root/ └── src/ └── com/ └── example/ └── myapp/ └── MyClass.java- ディレクトリ構造との対応パッケージ名はディレクトリ構造と対応させる必要があります 。例えば com.example.utils というパッケージ名であれば、ソースコードは com/example/utils というフォルダ構造の中に配置しなくてはなりません。
- ディレクトリ構成とパッケージ名の不一致に注意もしソースコードが本来のパッケージ構造と異なる場所に置かれていた場合、コンパイルエラーや実行時のクラスロードエラーの原因となります。開発現場では、ビルドツールやIDE(統合開発環境)がプロジェクト構成とパッケージを整合させてくれる場合が多いですが、手動で管理するときには注意が必要です。
- パッケージ階層を設計する意義大規模開発においては、ソースを整理し拡張しやすい構造にするために、適切なパッケージ階層を設計することが重要です。パッケージ名の付け方は会社やプロジェクトごとにガイドラインがある場合が多く、一般的には逆ドメイン名( com.example.project など)をルートにした階層構造がよく利用されます。
パッケージ名に特段の決まりはありません。ルールは、ソースファイルを配置するディレクトリと構造を対応させるということだけなので、必ずしもドメイン名を逆にした文字列ではなくてもOKではあります。
ステップ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:パッケージをコンパイルして実行 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) < ArrayListlist = new ArrayList(); list.add("Hello"); list.add("World"); System.out.println(list); > >ポイント import文
- 異なるパッケージのクラスを使う場合は import が必要例えば com.example.main.Main から com.example.utils.Helper を使いたい場合は、 import com.example.utils.Helper; を追加する必要があります。
- java.lang パッケージのクラスは特別に import 不要String や Math など、 java.lang パッケージに属するクラスは自動的に利用できます。
基本的には、同じパッケージ内なら import 不要、異なるパッケージなら import 必要 というルールで覚えておけばOKです。
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:ワイルドカード(*)を使ったインポート以下は、 java.util パッケージ内のすべてのクラスをインポートする例です。
import java.util.*; public class WildcardImportExample < public static void main(String[] args) < ArrayListlist = new ArrayList(); list.add("Hello"); list.add("World"); System.out.println(list); HashMap map = new HashMap(); map.put(1, "One"); map.put(2, "Two"); System.out.println(map); > >import文で「*」を使うと、そのパッケージ内にある全てのクラスやインターフェースが利用可能になるという意味ですが、実際にインポートされるのは、 あくまでもそのパッケージに含まれるクラスやインターフェースだけ という点に注意が必要です。
- サブパッケージは含まれない例えば、 import java.util.*; と書いた場合、 java.util パッケージ内のクラスはすべて利用可能になりますが、 java.util.concurrent などのサブパッケージ内のクラスは自動的にはインポートされません。
- 必要なクラスだけがロードされる:コンパイル時には名前解決のために全てが候補として認識されますが、実際にメモリに読み込まれるのは、プログラムで実際に使用されているクラスだけです。
実は、フル修飾名(完全修飾名)を使えば、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); >>結論✅ import の正体→クラス名の「短縮表記」を使えるようにする宣言
import java.util.Date; // → 「Dateって書いたら、java.util.Dateのことだよ」と宣言してるだけ無名パッケージ(default package)
無名パッケージ(default package)とは、Javaソースファイルに package 宣言を書かずにクラスやインターフェースを定義したとき、暗黙的に属するパッケージのことです。下記のように package 宣言を記述しないで書いたソースファイル内のクラスは、 すべて無名パッケージに含まれます。
// パッケージ宣言なし public class Sample < // . > 無名パッケージの特徴・注意点- 原則として同一ソースディレクトリのみ有効無名パッケージ内のクラスは、同じディレクトリに置かれたクラス同士からのみ参照(import)できます。複数のディレクトリに無名パッケージのクラスがあっても、それら同士を参照し合うことはできません。
- 規模拡大に向かない無名パッケージは可視性が極めて限定的であり、複数のパッケージ間でコードを整理することが難しいため、大規模なプロジェクトには向いていません。小規模なサンプルコードや学習用のコードなど、パッケージ分けを意識しないケースで一時的に使われることが多いです。
- IDEやビルドツールとの相性一般的なIDE(Eclipse、IntelliJ IDEA、VS Codeなど)やビルドツール(Maven、Gradle)を使う場合、無名パッケージ内のクラスが存在するとソース管理が煩雑になりやすいです。プロジェクト開発時には通常、命名したパッケージを使うことを推奨します。
- アクセス制御の面無名パッケージは、パッケージ単位でのアクセス制御が難しいという面があります。特定のクラスだけを狙ってアクセス制限をかけるといったパッケージスコープの活用ができないので、アクセス制御の観点でもあまり使われません。
サブパッケージとは?
サブパッケージは、基本的なパッケージの「下位」にあるパッケージです。見た目の階層構造はあるものの、実際には親パッケージとは独立した別のパッケージとして扱われます。具体的に見ていきましょう。
サブパッケージの構造- 例:java.util というパッケージがあるとします。この中にさらに細かく分けられたパッケージとして java.util. concurrent が存在します。ここで java.util. concurrent は java.util のサブパッケージと呼ばれます。
- 階層的な名前付け:サブパッケージはドット(.)で区切られた名前で表現され、階層的な意味を持っているように見えます。
- 独立性:親パッケージをインポートしても、そのサブパッケージ内のクラスは自動的にはインポートされません。たとえば、 import java.util.*; としても、 java.util .concurrent 内のクラスは利用できず、個別にインポートする必要があります。
- 管理と整理:プログラムが大きくなると、機能ごとにクラスを整理する必要があります。サブパッケージを使うことで、関連するクラスをさらに細かくグループ分けし、見通しを良くすることができます。
- インポートの注意親パッケージの「*」を使ったインポートはサブパッケージには適用されないので、サブパッケージ内のクラスを使う場合は、個別にインポートするか、サブパッケージ自体を「*」でインポートする必要があります。
- 名前の階層はあくまで見た目サブパッケージは名前に階層があるだけで、親パッケージとの「継承関係」や「包含関係」があるわけではありません。各パッケージは完全に独立して管理されます。
- 整理のメリットサブパッケージを使うことで、コードが増えてもどこにどの機能があるかが分かりやすくなり、保守性が向上します。例えば、並行処理に関するクラスは java.util.concurrent にまとめ、その他のユーティリティは java.util にまとめるといった具合です。
関連記事
【Java】内部クラス(nested class)の仕組みを3分でわかりやすくJavaの内部クラスを初心者でも迷わず学べるよう、クラスとは何かから始めて static ネストクラス・inner class・ローカルクラス・匿名クラスの違いと選び方、コード生成の裏側、実践例を丁寧に解説します。
【Java】シャローコピーとディープコピーを1分で解説 Javaにおけるシャローコピーとディープコピーの違いを初心者向けにわかりやすく解説。clone()メソッドの基本や、コピー方法の使い分けについて具体例を交えて紹介します。 Javaのシールクラス(Sealed Classes)とは?3分でわかりやすく解説Java 17で正式導入されたシールクラス(Sealed Classes)の基本概念と使い方を初心者向けに解説。継承を制限してコードの安全性を高めるメリットや実用例、パターンマッチングとの連携など、開発手順を踏まえて分かりやすく紹介します。