Javaのpackageとは、クラスをフォルダのように分類して名前の重なりを防ぐ仕組みで、importとは、別のパッケージにあるクラスを短い名前で書けるようにする宣言です。そしてクラスパスとは、javac や java が「クラスをどこから探すか」を示す場所の一覧です。 この3つはセットで働くので、どれか1つがずれると「シンボルを見つけられません」や「NoClassDefFoundError」といったエラーになります。

この記事では、フォルダ構成を作るところから、javac -d でコンパイルし、java -cp で実行するまでをコマンドと実行結果つきで通しで説明します。あわせて、import の4つの形、同名クラスの衝突、よく出る5つのエラーを実際に再現して原因別に直す方法、jar をクラスパスに入れる方法までまとめました。コードはすべて OpenJDK 21.0.10 で実行し、Java 25 の import module は OpenJDK 25.0.4.1 で実行した結果を載せています。

packageとimportとクラスパスの早見表

用語 何をするものか 書く場所・指定する場所 例
package 宣言 そのファイルのクラスが属するパッケージを決める ソースファイルの先頭(1ファイルに1つ) package com.example.shop.model;
import 宣言 別パッケージのクラスを短い名前で使えるようにする package の後、クラス定義の前 import java.util.List;
クラスパス クラスファイルや jar を探す場所の一覧 -cp(-classpath)オプション、または環境変数 CLASSPATH java -cp out:lib/* com.example.shop.App
完全修飾名 パッケージ名まで含めたクラスの正式な名前 java コマンドの引数、import、コード中 com.example.shop.App

import の4つの形は次のとおりです。

形 書き方 使えるようになるもの
単一型インポート import java.util.ArrayList; そのクラス1つ
オンデマンドインポート import java.time.*; そのパッケージ直下の public な型すべて(サブパッケージは含まない)
static インポート import static java.lang.Math.max; その static メンバー1つ(メソッドや定数)
static オンデマンドインポート import static java.lang.System.*; そのクラスの static メンバーすべて

よく使うコマンドもまとめておきます(区切り文字は Linux/macOS が :、Windows が ;)。

やりたいこと コマンド
パッケージのフォルダ構成でコンパイルする javac -d out $(find src -name '*.java')
関連するソースを自動で探してコンパイルする javac -d out -sourcepath src src/com/example/shop/App.java
パッケージ付きのクラスを実行する java -cp out com.example.shop.App
jar をクラスパスに入れて実行する java -cp 'app:lib/*' com.example.shop.App
jar を作る jar --create --file shop.jar --main-class com.example.shop.App -C out .

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Javaのpackageとimportの使い方|クラスパスの通し方」

動作確認の環境

項目 内容
主に使った JDK OpenJDK 21.0.10(Ubuntu 24.04、シェルは bash)
Java 25 の確認 OpenJDK 25.0.4.1(import module の節)
エラー文の言語 日本語は -J-Duser.language=ja、英語は -J-Duser.language=en を付けて表示
表記について 実行時に付けた言語・文字コードのオプションは、記事では省いて javac/java と表記しています

フォルダ構成からコンパイル・実行まで通しで行う

手順1:フォルダ構成とソースを用意する

パッケージ名とフォルダの階層は一致させます。 com.example.shop.model パッケージのクラスは、com/example/shop/model/ フォルダに置きます。

shop/                         ← 作業フォルダ(ここでコマンドを実行する)
└── src/
    └── com/
        └── example/
            └── shop/
                ├── App.java              (package com.example.shop;)
                ├── model/
                │   └── Item.java         (package com.example.shop.model;)
                └── util/
                    └── Prices.java       (package com.example.shop.util;)
// src/com/example/shop/model/Item.java
package com.example.shop.model;

public class Item {
    private final String name;
    private final int price;

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String name() { return name; }
    public int price() { return price; }
}
// src/com/example/shop/util/Prices.java
package com.example.shop.util;

import com.example.shop.model.Item;
import java.util.List;

public class Prices {
    public static final int FREE_SHIPPING_LINE = 3000;

    public static int total(List<Item> items) {
        int sum = 0;
        for (Item item : items) {
            sum += item.price();
        }
        return sum;
    }

    public static String format(int yen) {
        return String.format("%,d円", yen);
    }
}
// src/com/example/shop/App.java
package com.example.shop;

import com.example.shop.model.Item;      // 単一の型をインポート
import com.example.shop.util.Prices;
import java.util.List;

public class App {
    public static void main(String[] args) {
        List<Item> cart = List.of(new Item("ノート", 480), new Item("ペン", 150));
        int total = Prices.total(cart);
        System.out.println("合計: " + Prices.format(total));
        System.out.println("送料無料まで: " + Prices.format(Prices.FREE_SHIPPING_LINE - total));
    }
}

ソースファイルの中の並び順は package → import → クラス定義 です。import を package より前に書くと、次のエラーになります。

PkgOrder.java:2: エラー: class, interface, enumまたはrecordがありません
package com.example;
^

手順2:javac -d でコンパイルする

-d は、クラスファイルの出力先フォルダを指定するオプションです。出力先の中に、パッケージ名どおりのフォルダが自動で作られます。

$ javac -d out src/com/example/shop/App.java src/com/example/shop/model/Item.java src/com/example/shop/util/Prices.java
$ find out -type f | sort
out/com/example/shop/App.class
out/com/example/shop/model/Item.class
out/com/example/shop/util/Prices.class

ファイルを全部並べるのが大変なときは、次の2通りの書き方があります。どちらも同じ3つのクラスファイルができることを確認しました。

$ javac -d out $(find src -name '*.java')                           (すべての .java を渡す)
$ javac -d out -sourcepath src src/com/example/shop/App.java        (必要なソースを src から自動で探す)

-sourcepath は「ソースファイルを探す起点」です。App.java が import している Item と Prices を、src/com/example/shop/model/Item.java のようにパッケージ名から場所を割り出して、一緒にコンパイルしてくれます。

手順3:java -cp で実行する

$ java -cp out com.example.shop.App
合計: 630円
送料無料まで: 2,370円

実行のときのポイントは2つです。

  • -cp には「パッケージの一番上のフォルダが入っている場所」を指定する:この例では com フォルダが入っている out
  • クラス名は完全修飾名で指定する:App ではなく com.example.shop.App

java コマンドは、クラスパス out と完全修飾名 com.example.shop.App を組み合わせて、out/com/example/shop/App.class を探します。

flowchart LR
    A["-cp out"] --> C["out/com/example/shop/App.class を探す"]
    B["com.example.shop.App"] --> C
    C --> D["App が使う Item・Prices も同じクラスパスから探す"]

クラスパスの通し方

クラスパスは「クラスを探す場所の一覧」で、コンパイル時(javac)と実行時(java)の両方で使われます。

指定方法 書き方 補足
-cp オプション java -cp out com.example.shop.App -classpath、--class-path も同じ意味
複数の場所を指定 -cp app:lib/a.jar:lib/b.jar Linux/macOS は :、Windows は ; で区切る
フォルダ内の jar をまとめて指定 -cp 'app:lib/*' * はそのフォルダの jar すべて。シェルに展開されないようクォートで囲む
環境変数 CLASSPATH CLASSPATH='app:lib/*' java com.example.shop.App -cp を付けると、そちらが使われる
何も指定しない java Hello カレントフォルダ(.)だけが対象になる

クラスパスに入れるものは、次の3種類です。

入れるもの 例 意味
クラスファイルのフォルダ out パッケージの一番上(com)が入っているフォルダ
jar ファイル lib/shop-model.jar jar ファイルそのものを指定する
jar のフォルダ+* lib/* フォルダ内のすべての jar

jar は、置いてあるフォルダではなく、jar ファイルそのもの(または フォルダ/*)を指定する点がつまずきやすいところです。後の節で実際に確かめます。

-cp を指定すると、カレントフォルダは自動では含まれなくなります。カレントフォルダも対象にしたいときは -cp .:lib/* のように . を書き加えます。

importの4つの形を使い分ける

4つの形を1つのファイルにまとめました。

import java.util.ArrayList;                 // 1. 単一型インポート
import java.time.*;                         // 2. オンデマンドインポート(パッケージ内のすべての型)
import static java.lang.Math.max;           // 3. static インポート(メンバー1つ)
import static java.util.Map.entry;
import static java.lang.System.*;           // 4. static オンデマンドインポート(static メンバーすべて)

public class Four {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("A");
        LocalDate day = LocalDate.of(2026, 10, 2);
        DayOfWeek dow = day.getDayOfWeek();
        out.println(list + " " + day + " " + dow);     // System.out を out と書ける
        out.println(max(3, 7));                        // Math.max を max と書ける
        out.println(entry("key", 1));
        String s = "java.lang は import 不要";          // String, Math, System, Integer など
        out.println(s.length() + " " + Integer.parseInt("42"));
    }
}

実行結果(java Four.java、Java 21.0.10):

[A] 2026-10-02 FRIDAY
7
key=1
21 42

importは「短い名前で書けるようにする」だけ

import は、ほかの言語の「ファイルを読み込む」処理とは違います。クラスの完全修飾名を省略して書けるようにする宣言であり、import しなくても完全修飾名で書けば同じクラスを使えます。

public class Fqcn {
    public static void main(String[] args) {
        java.util.List<String> names = new java.util.ArrayList<>();
        names.add("A");
        System.out.println(names);
    }
}
[A]

java.langはimport不要

String、System、Math、Integer、Object、Exception などが入っている java.lang パッケージは、自動で import されています。上の Four.java でも、String と Integer は import なしで使えています。また、同じパッケージのクラスどうしも import は不要です。

import が必要か 例
不要:java.lang のクラス String、System、Math、Integer、Thread
不要:同じパッケージのクラス com.example.shop 内の別クラス
必要:それ以外のパッケージ java.util.List、java.time.LocalDate、com.example.shop.model.Item

* はサブパッケージを含まない

import java.util.*; で使えるようになるのは java.util 直下の型だけです。java.util.function のような下の階層は別のパッケージとして扱われます。

import java.util.*;
public class Sub {
    public static void main(String[] args) {
        List<String> a = List.of("x");
        Function<String, Integer> f = String::length;   // java.util.function は別パッケージ
        System.out.println(f.apply(a.get(0)));
    }
}
Sub.java:5: エラー: シンボルを見つけられません
        Function<String, Integer> f = String::length;   // java.util.function は別パッケージ
        ^
  シンボル:   クラス Function
  場所: クラス Sub

import java.util.function.Function; を足すと通ります。パッケージ名の階層は、見た目は入れ子ですが、import のうえでは1つ1つが独立したパッケージです。

* を使っても遅くならない

「* で全部 import すると遅くなるのでは」という疑問をよく見かけます。確かめるために、import の行だけが違う2つのファイルをコンパイルして、クラスファイルを比べました(行番号がずれないよう、行数はそろえています)。

// s1/Speed.java                     // s2/Speed.java
import java.util.ArrayList;          import java.util.*;
import java.util.List;               (空行)
public class Speed { ... 以下同じ ... }
$ sha256sum s1/o/Speed.class s2/o/Speed.class
11dafa4c66541edb3986497ac2b62b9310b726a6e333488890b709c4f378578e  s1/o/Speed.class
11dafa4c66541edb3986497ac2b62b9310b726a6e333488890b709c4f378578e  s2/o/Speed.class

2つのクラスファイルは1バイトも違いませんでした。 クラスファイルには import 文は残らず、実際に使ったクラス(java/util/ArrayList、java/util/List など)の完全修飾名だけが記録されます。そのため、実行時の速度やメモリは import の書き方に左右されません。

それでも単一型インポートが好まれるのは、速度ではなく読みやすさのためです。

観点 単一型インポート オンデマンドインポート(*)
実行時の速度 同じ 同じ(クラスファイルが同一)
どのクラスを使っているか import を見ればわかる 本文を読まないとわからない
同名クラスの衝突 起きにくい 起きやすい(次の節)
行数 増える(IDE が自動で整理する) 少ない

static importの使いどころ

static import を使うと、Math.max を max、Collectors.toList を toList のようにクラス名なしで書けます。

import static java.lang.Integer.MAX_VALUE;
import static java.lang.Integer.parseInt;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

import java.util.List;
import java.util.stream.Stream;

public class StaticOk {
    public static void main(String[] args) {
        List<Integer> nums = Stream.of("3", "1", "2").map(s -> parseInt(s)).collect(toList());
        System.out.println(nums);
        System.out.println(Stream.of("a", "b").collect(joining("-")));
        System.out.println(MAX_VALUE);
    }
}
[3, 1, 2]
a-b
2147483647
static import が向いている場面 理由
テストのアサーション(assertEquals など) 何度も書くので、短いほうが読みやすい
Collectors.toList、Map.entry などの補助メソッド 名前だけで意味が伝わる
数式で多用する Math.sin、Math.PI など 式が読みやすくなる
名前だけでは出どころがわかりにくいもの クラス名を付けたまま書くほうが親切

static オンデマンドインポートを2つ並べると、同じ名前の定数がぶつかることがあります。

import static java.lang.Integer.*;
import static java.lang.Long.*;
...
System.out.println(MAX_VALUE);
StaticAmb.java:5: エラー: MAX_VALUEの参照はあいまいです
        System.out.println(MAX_VALUE);
                           ^
  Longの変数 MAX_VALUEとIntegerの変数 MAX_VALUEの両方が一致します

Integer.MAX_VALUE のようにクラス名を付けて書くと解決します。

同名クラスの衝突(java.util.Dateとjava.sql.Date)

別のパッケージに同じ名前のクラスがあるとき、両方を * で import すると、どちらを指すのか決められずエラーになります。代表例が java.util.Date と java.sql.Date です。

import java.util.*;
import java.sql.*;

public class Amb {
    public static void main(String[] args) {
        Date d = new Date(0);
        System.out.println(d);
    }
}
Amb.java:6: エラー: Dateの参照はあいまいです
        Date d = new Date(0);
        ^
  java.sqlのクラス java.sql.Dateとjava.utilのクラス java.util.Dateの両方が一致します

英語表示では error: reference to Date is ambiguous です。import しただけではエラーにならず、Date という名前を実際に使った行でエラーになります。

両方を単一型インポートで書くことはできません。

import java.util.Date;
import java.sql.Date;
Amb2.java:2: エラー: 同じ単純名の型がDateの単一型インポートによってすでに定義されています
import java.sql.Date;
^

直し方:片方を単一型インポート、もう片方を完全修飾名で書く

import java.util.*;
import java.sql.*;
import java.util.Date;            // 単一型インポートが * より優先される

public class AmbFix {
    public static void main(String[] args) {
        Date utilDate = new Date(0L);                       // java.util.Date
        java.sql.Date sqlDate = new java.sql.Date(0L);      // もう片方は完全修飾名で書く
        System.out.println(utilDate.getClass().getName());
        System.out.println(sqlDate.getClass().getName() + " " + sqlDate);
    }
}

実行結果(タイムゾーン UTC):

java.util.Date
java.sql.Date 1970-01-01

単一型インポートは、オンデマンドインポート(*)より優先されます。 名前がぶつかったときの優先順位は次のとおりです。

優先順位 名前の出どころ
1(最優先) そのファイル内で定義したクラス、単一型インポートしたクラス
2 同じパッケージのクラス
3 オンデマンドインポート(*)したクラス、java.lang のクラス

同じ名前のクラスを2つ使うファイルでは、よく使うほうを import し、もう片方を完全修飾名で書くと読みやすくまとまります。

よく出るエラーを再現して原因別に直す

パッケージ・import・クラスパスに関係するエラーは、コンパイル時(javac が出す) と 実行時(java が出す) に分けて考えると整理できます。

エラー(日本語/英語) いつ出る 主な原因
シンボルを見つけられません/cannot find symbol コンパイル時 import の書き忘れ、クラス名のつづり違い
パッケージ○○は存在しません/package ○○ does not exist コンパイル時 パッケージ名のつづり違い、クラスパスやソースパスの指定もれ
メイン・クラスを検出およびロードできませんでした(ClassNotFoundException) 実行時(起動直後) クラス名が完全修飾名でない、-cp の場所が違う
NoClassDefFoundError: ○○ (wrong name: △△) 実行時(起動直後) パッケージのフォルダの中に入って、短い名前で実行した
NoClassDefFoundError: ○○ 実行時(途中) コンパイル時にはあったクラスや jar が、実行時のクラスパスにない

シンボルを見つけられません(importの書き忘れ)

public class NoImport {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("A");
        System.out.println(names);
    }
}
NoImport.java:3: エラー: シンボルを見つけられません
        List<String> names = new ArrayList<>();
        ^
  シンボル:   クラス List
  場所: クラス NoImport
NoImport.java:3: エラー: シンボルを見つけられません
        List<String> names = new ArrayList<>();
                                 ^
  シンボル:   クラス ArrayList
  場所: クラス NoImport
エラー2個

英語表示は error: cannot find symbol/symbol: class List です。「シンボル: クラス List」の行が、見つからなかった名前を教えてくれています。import java.util.List; と import java.util.ArrayList; を足すと通ります。

パッケージは存在しません(package does not exist)

このエラーには、原因が2通りあります。1つ目はつづりの間違いです。

import java.utl.List;          // util のつづり違い
import java.util.Lists;        // クラス名の間違い
import java.util.function;     // パッケージ名で止まっている(クラス名も * もない)
public class Typo { }
Typo.java:1: エラー: パッケージjava.utlは存在しません
import java.utl.List;
               ^
Typo.java:2: エラー: シンボルを見つけられません
import java.util.Lists;
                ^
  シンボル:   クラス Lists
  場所: パッケージ java.util
Typo.java:3: エラー: シンボルを見つけられません
import java.util.function;
                ^
  シンボル:   クラス function
  場所: パッケージ java.util

パッケージ名が違えば「パッケージは存在しません」(英語:package java.utl does not exist)、パッケージは合っていてクラス名が違えば「シンボルを見つけられません」と、メッセージが変わります。

2つ目は、自分で作ったパッケージの場所を javac に伝えていない場合です。最初の例の App.java だけを、ほかの指定なしでコンパイルしてみます。

$ javac -d out2 src/com/example/shop/App.java
src/com/example/shop/App.java:3: エラー: パッケージcom.example.shop.modelは存在しません
import com.example.shop.model.Item;      // 単一の型をインポート
                             ^
src/com/example/shop/App.java:4: エラー: パッケージcom.example.shop.utilは存在しません
import com.example.shop.util.Prices;
                            ^
(この後に「シンボルを見つけられません」が7件続き、合計でエラー9個)

ソースは正しいのに、Item と Prices の場所がわからないためエラーになっています。直し方は3通りあり、最後の2つで通ることを確認しました。

直し方 コマンド
関連するソースをすべて渡す javac -d out $(find src -name '*.java')
ソースの起点を教える javac -d out -sourcepath src src/com/example/shop/App.java
コンパイル済みのクラスや jar の場所を教える javac -d out2 -cp out src/com/example/shop/App.java/javac -d out3 -cp 'lib/*' ...

パッケージ宣言とフォルダがずれている場合も同じエラーになります。package com.example.model; と書いたファイルを src/wrong/Item.java に置き、-sourcepath src でコンパイルすると「パッケージcom.example.modelは存在しません」になりました。javac はパッケージ名からフォルダを割り出して探すためです。package 宣言とフォルダ階層をそろえると解決します。

メイン・クラスを検出およびロードできませんでした(ClassNotFoundException)

コンパイルは通ったのに実行できない、というときに最も多いのがこのエラーです。

$ java -cp out App
エラー: メイン・クラスAppを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: App

$ java com.example.shop.App
エラー: メイン・クラスcom.example.shop.Appを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: com.example.shop.App

英語表示は Error: Could not find or load main class App/Caused by: java.lang.ClassNotFoundException: App です。

実行したコマンド 原因 直し方
java -cp out App クラス名にパッケージ名がない java -cp out com.example.shop.App
java com.example.shop.App -cp がなく、カレントフォルダに com フォルダがない -cp out を付ける
java -cp out com.example.shop.app 大文字小文字の違い クラス名のとおり App と書く

wrong name(フォルダの中に入って実行した)

クラスファイルのあるフォルダまで移動して、短い名前で実行すると、少し違うエラーになります。

$ cd out/com/example/shop
$ java App
エラー: メイン・クラスAppを検出およびロードできませんでした
原因: java.lang.NoClassDefFoundError: App (wrong name: com/example/shop/App)

英語表示は Caused by: java.lang.NoClassDefFoundError: App (wrong name: com/example/shop/App) です。App.class というファイルは見つかったものの、中に書かれている正式な名前が com/example/shop/App で、指定された App と一致しない、という意味です。

同じことは、package com.example.hello; と書いたファイルを -d なしでコンパイルしたときにも起こります。クラスファイルがソースと同じ場所にできるので、java Greeter と実行すると wrong name になりました。

$ javac Greeter.java          (package com.example.hello; のファイル)
$ java Greeter
エラー: メイン・クラスGreeterを検出およびロードできませんでした
原因: java.lang.NoClassDefFoundError: Greeter (wrong name: com/example/hello/Greeter)

$ javac -d . Greeter.java     (-d を付けるとパッケージのフォルダが作られる)
$ java com.example.hello.Greeter
Hello
直し方 コマンド
パッケージの一番上まで戻って、完全修飾名で実行する cd で out に戻り java com.example.shop.App
その場から -cp で一番上を指す java -cp ../../.. com.example.shop.App(動作確認済み)
コンパイル時に -d を付けてフォルダを作る javac -d . Greeter.java → java com.example.hello.Greeter

NoClassDefFoundError(コンパイル時にはあったクラスが実行時にない)

コンパイルは通り、起動もしたのに、途中で止まるのが NoClassDefFoundError です。out から Prices.class だけを取り除いて実行すると再現できます。

$ java -cp out com.example.shop.App
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/shop/util/Prices
    at com.example.shop.App.main(App.java:10)
Caused by: java.lang.ClassNotFoundException: com.example.shop.util.Prices
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
    ... 1 more

Java はクラスを「初めて使うとき」に読み込みます。 そのため、App の main は動き始めており、Prices を使う 10 行目に来た時点でエラーになっています。

名前 種類 起きる場面
ClassNotFoundException 例外(Exception) 名前を指定してクラスを読み込もうとしたが、見つからなかった(java コマンドのクラス名指定、Class.forName など)
NoClassDefFoundError エラー(Error) コンパイル時には存在したクラスを使おうとしたが、実行時に見つからなかった

2つは別々に出るというより、上の出力のように NoClassDefFoundError の「Caused by」に ClassNotFoundException が入る形でよく現れます。どちらの場合も、確認することは同じです。

  • メッセージに出ているクラス名(com/example/shop/util/Prices)を確かめる
  • そのクラスが入ったフォルダや jar が、実行時の -cp に入っているかを確かめる
  • コンパイル時と実行時で、クラスパスの内容が同じかを確かめる

jarをクラスパスに入れる

jar は、クラスファイルをパッケージのフォルダ構成ごと1つにまとめた zip 形式のファイルです。ライブラリは jar の形で配られます。ここでは model と util をそれぞれ jar にして、ライブラリとして使う状況を作ります。

$ jar --create --file lib/shop-model.jar -C out com/example/shop/model
$ jar --create --file lib/shop-util.jar -C out com/example/shop/util
$ jar --list --file lib/shop-model.jar
META-INF/
META-INF/MANIFEST.MF
com/example/shop/model/
com/example/shop/model/Item.class

-C out は「out フォルダに移動してから」という意味で、jar の中身が com/ から始まるようにしています。app フォルダには App.class だけを置きました(app/com/example/shop/App.class)。

$ java -cp app com.example.shop.App
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/shop/model/Item
    at com.example.shop.App.main(App.java:9)
Caused by: java.lang.ClassNotFoundException: com.example.shop.model.Item

$ java -cp app:lib/shop-model.jar com.example.shop.App
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/shop/util/Prices
    at com.example.shop.App.main(App.java:10)

$ java -cp app:lib/shop-model.jar:lib/shop-util.jar com.example.shop.App
合計: 630円
送料無料まで: 2,370円

jar を1つ足すごとに、エラーになるクラスが Item → Prices と先へ進み、2つとも入れると動きました。jar が増えてきたら lib/* でまとめて指定できます。

$ java -cp 'app:lib/*' com.example.shop.App
合計: 630円
送料無料まで: 2,370円

$ java -cp app:lib com.example.shop.App
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/shop/model/Item
書き方 結果 理由
-cp app:lib/shop-model.jar:lib/shop-util.jar 動く jar を1つずつ指定している
-cp 'app:lib/*' 動く lib 内の jar がすべて対象になる
-cp app:lib NoClassDefFoundError フォルダを指定しても、中の jar は探されない
CLASSPATH='app:lib/*' java com.example.shop.App 動く 環境変数でも指定できる

lib/* はクォートで囲みます。 囲まないとシェルが lib/shop-model.jar lib/shop-util.jar と空白区切りに展開してしまい、クラスパスとして正しく伝わりません。また、lib/*.jar ではなく lib/* と書くのが Java のクラスパスの書き方です。

実行できるjarを作る

--main-class を付けて jar を作ると、java -jar でクラス名を指定せずに実行できます。

$ jar --create --file shop.jar --main-class com.example.shop.App -C out .
$ java -jar shop.jar
合計: 630円
送料無料まで: 2,370円

$ unzip -p shop.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: 21.0.10 (Ubuntu)
Main-Class: com.example.shop.App

jar の中の MANIFEST.MF に Main-Class が書き込まれています。なお、java -jar を使うときは -cp や CLASSPATH の指定は使われません。ほかの jar が必要な場合は、MANIFEST.MF の Class-Path に書くか、java -cp 'shop.jar:lib/*' com.example.shop.App の形で実行します(Class-Path 属性の動作は公式ドキュメントで確認した内容で、手元では未実行です)。

Maven や Gradle を使うと、依存する jar の取得とクラスパスの組み立てを自動で行ってくれます。それでも、エラーが出たときに「どのクラスが、どのクラスパスから見つからないのか」を読み解けると、原因にたどり着くのが早くなります。

パッケージの決め方とアクセスの範囲

パッケージ名の付け方

ルール 例
すべて小文字で書く com.example.shop
持っているドメイン名を逆順にして先頭に置く example.com → com.example
その後ろに、アプリ名・役割を続ける com.example.shop.model、com.example.shop.util
java. で始まる名前は使わない 標準ライブラリ用の名前

ドメイン名を逆順にするのは、世界中のライブラリとパッケージ名が重ならないようにするための慣習です。

package宣言を書かない場合(無名パッケージ)

package 宣言のないクラスは「無名パッケージ(デフォルトパッケージ)」に属します。小さな確認用のプログラムには手軽ですが、名前付きパッケージのクラスからは import できません。

// pkg/User.java
package pkg;
import Tool;            // 無名パッケージの Tool を import しようとした
pkg/User.java:2: エラー: '.'がありません
import Tool;
           ^

import には必ず「パッケージ名.クラス名」の形が必要なためです。複数のファイルで構成するプログラムでは、最初から package を付けておくとよいでしょう。

publicでないクラスは別パッケージから使えない

import できるのは public なクラスだけです。class Hidden { } のように public を付けていないクラスを別パッケージから import すると、次のエラーになります。

b/Use.java:2: エラー: aのHiddenはpublicではありません。パッケージ外からはアクセスできません
import a.Hidden;
        ^

これは「パッケージの中だけで使う部品」を外から見えなくできる、便利な仕組みでもあります。外に公開するクラスだけを public にすると、パッケージの使い方がわかりやすくなります。

Java 25のimport module(モジュールインポート宣言)

Java 25 では、JEP 511「Module Import Declarations」が正式機能になりました。import module モジュール名; と書くと、そのモジュールが公開しているパッケージの public な型をまとめて使えます。 OpenJDK 25.0.4.1 で実行した結果です。

import module java.base;

public class Mod {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(List.of("B", "A"));
        Collections.sort(names);
        Map<String, Integer> lengths = names.stream().collect(Collectors.toMap(n -> n, String::length));
        System.out.println(names + " " + lengths + " " + LocalDate.of(2026, 10, 2));
    }
}
$ java Mod.java          (Java 25.0.4.1)
[A, B] {A=1, B=1} 2026-10-02

java.util(List、Map)、java.util.stream(Collectors)、java.time(LocalDate)と、3つのパッケージのクラスを1行の import で使えています。import java.util.*; と違って、モジュール内の複数のパッケージが対象になるのが特徴です。

同じファイルを Java 21.0.10 で実行すると、構文として認識されずエラーになります。

$ java Mod.java          (Java 21.0.10)
Mod.java:1: エラー: '.'がありません
import module java.base;
             ^

同名クラスの衝突は、* のときと同じように起こります。java.base と java.sql の両方を import すると、Date があいまいになりました。

import module java.base;
import module java.sql;
...
Date d = new Date(0);
ModAmb.java:6: エラー: Dateの参照はあいまいです
        Date d = new Date(0);
        ^
  java.sqlのクラス java.sql.Dateとjava.utilのクラス java.util.Dateの両方が一致します

ここに import java.util.Date; を1行足すと、単一型インポートが優先されて Thu Jan 01 00:00:00 UTC 1970 と表示されました(Java 25.0.4.1、タイムゾーン UTC)。直し方はこれまでと同じです。

import の書き方 対象 使える Java
import java.util.List; クラス1つ すべて
import java.util.*; 1つのパッケージ直下の型 すべて
import module java.base; モジュールが公開する全パッケージの型 Java 25 以降(Java 23・24 ではプレビュー)

なお、Java 25 のコンパクトソースファイル(クラス宣言を省いて void main() だけを書く形式)では、import module java.base; が自動で適用されます。短い確認用コードやスクリプト的な使い方では便利で、規模の大きいコードでは、どのクラスを使っているかが読み取りやすい単一型インポートが引き続き向いています。

つまずきやすい点のチェックリスト

確認すること 見るところ
package 宣言とフォルダ階層は一致しているか package a.b.c; のファイルは a/b/c/ にあるか
ファイル内の順番は package → import → クラスか 先頭の数行
javac に -d を付けたか 出力先にパッケージのフォルダができているか
java に渡しているのは完全修飾名か com.example.shop.App(.class や .java は付けない)
-cp はパッケージの一番上が入っている場所か com フォルダの1つ上
jar は jar ファイルそのもの、または フォルダ/* で指定したか フォルダ名だけでは jar は探されない
区切り文字は OS に合っているか Linux/macOS は :、Windows は ;
コンパイル時と実行時のクラスパスは同じか NoClassDefFoundError のとき
同名クラスを * で両方 import していないか 「参照はあいまいです」のとき

よくある質問

Q. import と package はどちらを先に書きますか? A. package が先です。ソースファイルは「package 宣言(1つだけ)→ import 宣言(いくつでも)→ クラス定義」の順で書きます。package 宣言より前に書けるのはコメントだけです。

Q. import を書きすぎると、プログラムは重くなりますか? A. なりません。import は名前を短く書くための宣言で、クラスファイルには残りません。import java.util.*; と単一型インポートで、できあがるクラスファイルが完全に同じになることを本文で確認しました。使っていない import は、読みやすさのために IDE の「インポートの整理」機能で片付けるとよいでしょう。

Q. クラスパスを毎回指定するのが大変です。環境変数 CLASSPATH に設定してよいですか? A. 設定はできますが、ほかのプログラムにも影響するため、プログラムごとに -cp で指定するほうが見通しがよくなります。入力が長くなるときは、起動用のシェルスクリプトやバッチファイルにまとめるか、Maven・Gradle などのビルドツールに任せる方法が広く使われています。

Q. ClassNotFoundException と NoClassDefFoundError は、直し方が違いますか? A. 確認することはほぼ同じで、「メッセージに出ているクラスが、実行時のクラスパスにあるか」を調べます。起動直後に出るならクラス名や -cp の指定、途中で出るなら jar の入れ忘れ、wrong name と出るなら実行するフォルダとクラス名の指定を見直します。

まとめ

  • package はクラスの住所、import は短い名前で書くための宣言、クラスパスは探す場所の一覧
  • package 宣言とフォルダ階層を一致させ、javac -d out でコンパイル、java -cp out 完全修飾名 で実行する
  • import は4つの形:単一型、オンデマンド(*)、static、static オンデマンド。java.lang と同じパッケージは import 不要
  • * を使っても速度は変わらない:クラスファイルが同一になる。* はサブパッケージを含まない
  • 同名クラスの衝突は、片方を単一型インポート、もう片方を完全修飾名で書いて解決する
  • コンパイル時のエラーは import とソースの場所、実行時のエラーはクラス名と -cp を確認する
  • jar は jar ファイルそのものか lib/* で指定する:Java 25 では import module でモジュール単位の import もできる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Javaのpackageとimportの使い方|クラスパスの通し方」

株式会社RJCでは、Javaのパッケージやクラスパスの仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?