ClassNotFoundExceptionとは、クラス名を文字列で指定して読み込もうとしたときに、そのクラスがクラスパス上に見つからないと発生する検査例外です。 よく似た NoClassDefFoundError は、コンパイル時には存在したクラスが、実行時に見つからない・使えないときに JVM が投げるエラーです。

どちらも「実行時にクラスが足りない」ことを知らせていますが、発生のきっかけ・例外の種類・メッセージの書式が違います。この記事では2つを最小のコードで実際に発生させ、スタックトレース全文と比較表、原因別の確認コマンドをまとめました。コマンドと出力は Java 21.0.10 で確認し、Java 25.0.4.1 での表示の違いも載せています(Gradle 8.14.3、Tomcat 10.1.55)。

ClassNotFoundExceptionとNoClassDefFoundErrorの違い早見表

比べる点 ClassNotFoundException NoClassDefFoundError
発生タイミング Class.forName や ClassLoader.loadClass に文字列でクラス名を渡したとき new・static メソッド呼び出しなど、コードが直接使っているクラスを JVM が読み込むとき
種類 検査例外(Exception の系統) エラー(Error の系統)
try-catch 書かないとコンパイルエラー 書かなくてもコンパイルは通る
親クラス ReflectiveOperationException LinkageError
クラス名の表示 ドット区切り com.example.greeter.Greeter スラッシュ区切り com/example/greeter/Greeter
典型原因 クラス名の打ち間違い、JDBC ドライバなどの jar がクラスパスにない 実行時のクラスパスに jar がない、static 初期化で例外、パッケージ名と場所の不一致
対処の方向 文字列のクラス名とクラスパスを確かめる 実行時のクラスパス・依存の scope・初期化処理を確かめる

表示されたメッセージから原因を引く表です。

表示 主な原因 確かめること
ClassNotFoundException: com.example.Foo 名前の間違い、jar がクラスパスにない jar tf でクラスの有無、java -cp の指定
Error: Could not find or load main class Foo 起動するクラス名か、実行する場所が違う パッケージ名つきで指定しているか
NoClassDefFoundError: com/example/Foo 実行時のクラスパスに jar がない 依存の scope、-jar と -cp の併用
NoClassDefFoundError: Could not initialize class Foo static 初期化で例外が起きた 最初に出た ExceptionInInitializerError
NoClassDefFoundError: Main (wrong name: app/Main) パッケージの外側から実行していない 実行するディレクトリとクラス名

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

(記載例)出典:株式会社RJC「Java ClassNotFoundExceptionの原因と対処 違いも解説」

2つの例外の位置づけ

継承関係を表示するコードで確かめた結果です(Java 21.0.10)。

java.lang.ClassNotFoundException -> java.lang.ReflectiveOperationException -> java.lang.Exception -> java.lang.Throwable -> java.lang.Object
java.lang.NoClassDefFoundError -> java.lang.LinkageError -> java.lang.Error -> java.lang.Throwable -> java.lang.Object
java.lang.ExceptionInInitializerError -> java.lang.LinkageError -> java.lang.Error -> java.lang.Throwable -> java.lang.Object

ClassNotFoundException は Exception の系統、NoClassDefFoundError は Error の系統です。Error は「アプリケーションが通常は catch しないもの」という位置づけなので、NoClassDefFoundError を catch して続行するのではなく、クラスパスや初期化処理を直すのが基本になります。

ClassNotFoundExceptionを再現する

存在しないクラス名を Class.forName に渡します。

public class FindClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> c = Class.forName("com.example.report.PdfWriter");
        System.out.println(c.getName());
    }
}

実行結果(Java 21.0.10、javac FindClass.java のあと java -cp . FindClass):

Exception in thread "main" java.lang.ClassNotFoundException: com.example.report.PdfWriter
    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)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:423)
    at java.base/java.lang.Class.forName(Class.java:414)
    at FindClass.main(FindClass.java:3)

読み方は次のとおりです。

  • 1行目のコロンの後ろが、見つからなかったクラス名(ドット区切り)
  • at java.base/... の行は JDK のクラスローダーの内部。上から順に、探しに行った経路が並ぶ
  • いちばん下の FindClass.main(FindClass.java:3) が、自分のコードで読み込みを指示した行

検査例外なのでthrowsかtry-catchが必要

throws ClassNotFoundException を外すと、コンパイルの時点で知らせてくれます。

NoThrows.java:3: error: unreported exception ClassNotFoundException; must be caught or declared to be thrown
        Class<?> c = Class.forName("com.example.report.PdfWriter");
                                  ^
1 error

起動するクラスが見つからないとき

java コマンドに渡したクラス名が見つからない場合も、原因として ClassNotFoundException が表示されます。

$ java -cp . NoSuch
Error: Could not find or load main class NoSuch
Caused by: java.lang.ClassNotFoundException: NoSuch

クラス名の大文字・小文字の違い、.class や .java を付けたままの指定、-cp の指定漏れが主な原因です。

NoClassDefFoundErrorを再現する

2つのクラスをコンパイルしたあと、片方の class ファイルを消して実行します。「コンパイル時にはあったが、実行時にはない」状態を作るためです。

// App.java
public class App {
    public static void main(String[] args) {
        System.out.println("起動しました");
        Helper helper = new Helper();
        System.out.println(helper.greet());
    }
}
// Helper.java
public class Helper {
    public String greet() {
        return "こんにちは";
    }
}

実行結果(Java 21.0.10):

$ javac App.java Helper.java
$ java -cp . App
起動しました
こんにちは
$ rm Helper.class
$ java -cp . App
起動しました
Exception in thread "main" java.lang.NoClassDefFoundError: Helper
    at App.main(App.java:4)
Caused by: java.lang.ClassNotFoundException: Helper
    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

ポイントは3つあります。

  • 「起動しました」は表示されています。 クラスは必要になった時点で読み込まれるため、Helper を使う4行目に来て初めてエラーになります。起動直後ではなく、特定の機能を使ったときにだけ発生するのはこのためです。
  • Caused by: java.lang.ClassNotFoundException: Helper が付いています。 JVM が内部でクラスを探して見つからず、それを NoClassDefFoundError に包んで投げています。2つが同時に表示されるのは、この関係によるものです。
  • ... 1 more は、上のトレースと共通する行(App.main)を省略したという意味です。

Could not initialize classは初期化の例外が原因

クラスファイルが存在していても NoClassDefFoundError になる場合があります。static 初期化子や static フィールドの初期化で例外が起きたときです。

// Settings.java
public class Settings {
    static final int TIMEOUT;

    static {
        // 環境変数 APP_TIMEOUT が未設定だと null が渡り、ここで例外になる
        TIMEOUT = Integer.parseInt(System.getenv("APP_TIMEOUT"));
    }

    static int timeout() {
        return TIMEOUT;
    }
}
// InitDemo.java
public class InitDemo {
    public static void main(String[] args) {
        for (int i = 1; i <= 2; i++) {
            try {
                System.out.println(Settings.timeout());
            } catch (Throwable t) {
                System.out.println("---- " + i + "回目 ----");
                t.printStackTrace(System.out);
            }
        }
    }
}

実行結果(Java 21.0.10、環境変数 APP_TIMEOUT を設定せずに実行):

---- 1回目 ----
java.lang.ExceptionInInitializerError
    at InitDemo.main(InitDemo.java:5)
Caused by: java.lang.NumberFormatException: Cannot parse null string
    at java.base/java.lang.Integer.parseInt(Integer.java:624)
    at java.base/java.lang.Integer.parseInt(Integer.java:778)
    at Settings.<clinit>(Settings.java:6)
    ... 1 more
---- 2回目 ----
java.lang.NoClassDefFoundError: Could not initialize class Settings
    at InitDemo.main(InitDemo.java:5)
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.NumberFormatException: Cannot parse null string [in thread "main"]
    at java.base/java.lang.Integer.parseInt(Integer.java:624)
    at java.base/java.lang.Integer.parseInt(Integer.java:778)
    at Settings.<clinit>(Settings.java:6)
    ... 1 more

1回目は ExceptionInInitializerError、2回目以降は NoClassDefFoundError: Could not initialize class になります。一度初期化が止まったクラスは、その JVM の中では使えない状態として記録されるためです。

調べるときは、次の順で見ます。

  1. Could not initialize class Settings の Settings が、初期化で止まったクラス
  2. Caused by: に元の例外(ここでは NumberFormatException)と、Settings.<clinit>(Settings.java:6) の行番号が出る。<clinit> は static 初期化を表す名前
  3. ログをさかのぼり、最初に出た ExceptionInInitializerError を探す

環境変数を設定すると、同じコードが正常に動きます。

$ APP_TIMEOUT=30 java -cp . InitDemo
30
30

2回目のエラーに Caused by: が付くかどうかは、Java の版で違いました。

版 2回目の NoClassDefFoundError の表示
Java 8(1.8.0_504)・11.0.32.1 Could not initialize class Settings の1行と at の行だけ。原因は付かない
Java 17.0.20.1・21.0.10・25.0.4.1 Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.NumberFormatException: Cannot parse null string [in thread "main"] が付く

Java 8・11 で動かしている場合は、1回目の ExceptionInInitializerError をログから探すことが特に大切です。

wrong nameはパッケージと実行場所の不一致

package app; と宣言した Main クラスを、いろいろな指定で起動した結果です(Java 21.0.10)。

$ java -cp . app.Main
app.Main が動きました
$ java -cp . Main
Error: Could not find or load main class Main
Caused by: java.lang.ClassNotFoundException: Main
$ cd app
$ java -cp . Main
Error: Could not find or load main class Main
Caused by: java.lang.NoClassDefFoundError: Main (wrong name: app/Main)
$ java -cp . app.Main
Error: Could not find or load main class app.Main
Caused by: java.lang.ClassNotFoundException: app.Main

パッケージに属するクラスは、パッケージの外側(app ディレクトリの親)から、app.Main のようにパッケージ名つきで指定します。app ディレクトリの中に入って Main と指定すると、ファイルは見つかるものの中身の名前(app/Main)と合わないため、wrong name と表示されます。

原因別の確認コマンド

ここからは、ライブラリの jar を使う構成で確かめます。greeter-1.0.jar(ライブラリ)と、それを使う app.jar(app.Main)を用意しました。出力中のパスは /home/taro/work に置き換えています(表示は環境に合わせて置き換えています)。

クラスパスにjarが入っているかをjava -cpで確かめる

$ java -cp app.jar app.Main
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/greeter/Greeter
    at app.Main.main(Main.java:7)
Caused by: java.lang.ClassNotFoundException: com.example.greeter.Greeter
    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 -cp app.jar:greeter-1.0.jar app.Main
こんにちは、RJC さん

-cp(-classpath)には、必要な jar をすべて並べます。区切りは Linux・macOS がコロン :、Windows がセミコロン ; です(Windows は未実行)。

実行中のクラスパスと、クラスの読み込み元を表示するコードも用意しました。

public class ShowCp {
    public static void main(String[] args) {
        System.out.println("java.class.path = " + System.getProperty("java.class.path"));
        System.out.println("ClassLoader     = " + ShowCp.class.getClassLoader());
        try {
            Class<?> c = Class.forName("com.example.greeter.Greeter");
            System.out.println("読み込み元      = " + c.getProtectionDomain().getCodeSource().getLocation());
        } catch (ClassNotFoundException e) {
            System.out.println("見つかりません  = " + e.getMessage());
        }
    }
}
$ java -cp cpout:greeter-1.0.jar ShowCp
java.class.path = cpout:greeter-1.0.jar
ClassLoader     = jdk.internal.loader.ClassLoaders$AppClassLoader@639fee48
読み込み元      = file:/home/taro/work/greeter-1.0.jar

$ java -cp cpout ShowCp
java.class.path = cpout
ClassLoader     = jdk.internal.loader.ClassLoaders$AppClassLoader@639fee48
見つかりません  = com.example.greeter.Greeter

$ java -verbose:class -cp app.jar:greeter-1.0.jar app.Main | grep Greeter
[0.037s][info][class,load] com.example.greeter.Greeter source: file:/home/taro/work/greeter-1.0.jar

java -verbose:class を付けると、読み込んだクラスと読み込み元が1行ずつ表示されます。同じクラスが複数の jar に入っていて、意図しないほうが使われているときの確認にも使えます。

jarの中にクラスがあるかをjar tfで確かめる

$ jar tf greeter-1.0.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/greeter/
com/example/greeter/Greeter.class

$ jar tf app.jar | grep Greeter
(何も表示されない=app.jar には入っていない)

$ for f in app.jar greeter-1.0.jar; do jar tf "$f" | grep -q 'com/example/greeter/Greeter.class' && echo "$f"; done
greeter-1.0.jar

jar tf は jar の中身の一覧を表示します。パッケージはスラッシュ区切りのディレクトリとして入っているので、com/example/greeter/Greeter.class の形で探します。最後の for 文は、複数の jar からクラスを含むものを探す書き方です。

java -jarでは-cpが使われない

$ java -cp greeter-1.0.jar -jar app.jar
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/greeter/Greeter
    at app.Main.main(Main.java:7)
Caused by: java.lang.ClassNotFoundException: com.example.greeter.Greeter
(以下略)

$ cat manifest.txt
Main-Class: app.Main
Class-Path: greeter-1.0.jar
$ jar cfm app-cp.jar manifest.txt -C app/out .
$ java -jar app-cp.jar
こんにちは、RJC さん

$ java -jar app-all.jar
こんにちは、RJC さん
$ jar tf app-all.jar | grep class
app/Main.class
com/example/greeter/Greeter.class

-jar で起動すると、-cp の指定は使われません。 実行結果のとおり、-cp greeter-1.0.jar を付けても NoClassDefFoundError になりました。対処は3つあります。

方法 やること 確認結果
-jar をやめる java -cp app.jar:greeter-1.0.jar app.Main 動作
マニフェストに Class-Path を書く Class-Path: greeter-1.0.jar(jar からの相対パス) 動作
fat jar にまとめる 依存するクラスを1つの jar に入れる 動作

fat jar は、ここでは依存 jar を展開して1つにまとめ直す方法で作りました。ビルドツールでは、Maven の maven-shade-plugin や Gradle の Shadow プラグインがよく使われます(どちらも今回は未実行)。fat jar で NoClassDefFoundError が出たら、jar tf app-all.jar | grep クラス名 で、まとめた jar に入っているかを確かめます。

JDBCドライバが見つからないとき

Class.forName("ドライバのクラス名") は、ClassNotFoundException の代表的な発生箇所です。引数で受け取ったクラス名を Class.forName に渡すだけの小さなプログラム(Drv)で確かめました。

$ java -cp cpout Drv org.mariadb.jdbc.Driver
Exception in thread "main" java.lang.ClassNotFoundException: org.mariadb.jdbc.Driver
(以下略)
$ java -cp cpout:/usr/share/java/mariadb-java-client.jar Drv org.mariadb.jdbc.Driver
org.mariadb.jdbc.Driver を読み込めました

ドライバの jar をクラスパスに加えると読み込めます。Web アプリケーションなら WEB-INF/lib、ビルドツールなら依存関係への追加が対処になります。

依存のscope違いを確かめる(Gradle・Maven)

「コンパイルは通るのに実行すると NoClassDefFoundError」という場合、依存がコンパイル時だけ有効な設定になっていることがあります。Gradle 8.14.3 で再現しました。

plugins {
    id 'application'
}

repositories {
    maven { url = uri('repo') }   // 確認用のローカルリポジトリ
}

dependencies {
    compileOnly 'com.example:greeter:1.0'   // コンパイル時だけ使える
}

application {
    mainClass = 'app.Main'
}

tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' }
$ gradle -q run
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/greeter/Greeter
    at app.Main.main(Main.java:7)
Caused by: java.lang.ClassNotFoundException: com.example.greeter.Greeter
    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
(以下、Gradle の BUILD FAILED の表示は省略)

$ gradle -q dependencies --configuration compileClasspath
compileClasspath - Compile classpath for source set 'main'.
\--- com.example:greeter:1.0

$ gradle -q dependencies --configuration runtimeClasspath
runtimeClasspath - Runtime classpath of source set 'main'.
No dependencies

compileClasspath には greeter があり、runtimeClasspath は No dependencies です。実行時のクラスパスに入っていないことが、コマンドで確かめられます。compileOnly を implementation に変えると動きました。

$ gradle -q run
こんにちは、RJC さん

$ gradle -q dependencies --configuration runtimeClasspath
runtimeClasspath - Runtime classpath of source set 'main'.
\--- com.example:greeter:1.0

逆に runtimeOnly にすると、今度はコンパイルの段階で error: package com.example.greeter does not exist と表示されました。

Gradle の指定 Maven の scope コンパイル時 実行時 使いどころ
implementation compile(既定) 使える 使える 通常の依存
compileOnly provided 使える 含まれない サーバーが用意する API(Servlet API など)
runtimeOnly runtime 使えない 使える JDBC ドライバなど、名前で読み込むもの

Maven の列は一般に知られた仕様で、今回の環境では Maven のプラグインを取得できず、mvn dependency:tree は未実行です。Maven で確かめるときのコマンドは次のとおりです。

# 依存の一覧と scope を木の形で表示する(各行の末尾に :compile や :provided が付く)
mvn dependency:tree

# 特定のライブラリだけに絞る
mvn dependency:tree -Dincludes=com.example:greeter

# 実行時のクラスパスを1行で表示する
mvn dependency:build-classpath -Dmdep.includeScope=runtime

provided や compileOnly は、実行環境がそのクラスを用意してくれる場合に使う指定です。用意されない環境で動かすと、今回と同じ NoClassDefFoundError になります。

Tomcatではクラスローダーごとに探す場所が違う

Web アプリケーションは、Tomcat が用意するアプリケーション専用のクラスローダーで読み込まれます。組み込みの Tomcat 10.1.55 に Web アプリケーションのディレクトリを配置し、WEB-INF/lib に greeter の jar を入れずにサーブレットを呼び出しました。

package web;

import com.example.greeter.Greeter;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        res.setContentType("text/plain; charset=UTF-8");
        if ("forName".equals(req.getParameter("mode"))) {
            try {
                Class.forName("com.example.greeter.Greeter");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                res.getWriter().println("ClassNotFoundException: " + e.getMessage());
                return;
            }
        }
        res.getWriter().println(new Greeter().greet("Tomcat"));
        res.getWriter().println("loader = " + Greeter.class.getClassLoader().getClass().getName());
    }
}

/hello にアクセスしたときのエラーページ(HTTP 500)の Root Cause 部分です。

Root Cause
java.lang.NoClassDefFoundError: com/example/greeter/Greeter
    web.HelloServlet.doGet(HelloServlet.java:24)
    jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
    jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
Root Cause
java.lang.ClassNotFoundException: com.example.greeter.Greeter
    org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1377)
    org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1186)
    web.HelloServlet.doGet(HelloServlet.java:24)
    jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
    jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)

?mode=forName を付けたときの結果です。

$ curl "http://127.0.0.1:8390/hello?mode=forName"
ClassNotFoundException: com.example.greeter.Greeter

(サーバー側の出力。先頭の9行)
java.lang.ClassNotFoundException: com.example.greeter.Greeter
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1377)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1186)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:423)
    at java.base/java.lang.Class.forName(Class.java:414)
    at web.HelloServlet.doGet(HelloServlet.java:17)
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)

トレースに org.apache.catalina.loader.WebappClassLoaderBase.loadClass が出ていれば、Web アプリケーションのクラスローダーが探して見つからなかったという意味です。このクラスローダーが探すのは、そのアプリケーションの WEB-INF/classes と WEB-INF/lib、および Tomcat 自身が持つ共有のライブラリです(探す場所は一般に知られた仕様で、今回確かめたのは WEB-INF/lib の有無による違いです)。

WEB-INF/lib に greeter-1.0.jar を置いて起動し直すと、次のように動きました。

こんにちは、Tomcat さん
loader = org.apache.catalina.loader.ParallelWebappClassLoader
確かめること コマンド・場所
war の中に jar が入っているか jar tf app.war | grep WEB-INF/lib
展開先に jar があるか ls webapps/アプリ名/WEB-INF/lib
依存の scope が provided になっていないか 前の章の表(Tomcat が用意しない jar は implementation / compile に)

war の確認コマンド2つは、今回はディレクトリを直接配置したため未実行です。

Java 21とJava 25での表示

同じ class ファイルを Java 25.0.4.1 で実行した結果です。

Exception in thread "main" java.lang.ClassNotFoundException: com.example.report.PdfWriter
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:467)
    at java.base/java.lang.Class.forName(Class.java:458)
    at FindClass.main(FindClass.java:3)
Exception in thread "main" java.lang.NoClassDefFoundError: com/example/greeter/Greeter
    at app.Main.main(Main.java:7)
Caused by: java.lang.ClassNotFoundException: com.example.greeter.Greeter
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
    ... 1 more
比べる点 Java 21.0.10 Java 25.0.4.1
1行目のメッセージ 同じ 同じ
ClassLoaders$AppClassLoader.loadClass の行 あり なし
JDK 内部の行番号 641・526 など 580・490 など
Could not initialize class の Caused by あり あり
wrong name の表示 Main (wrong name: app/Main) 同じ

メッセージの文面は同じで、JDK 内部の at の行だけが変わります。 見る場所(1行目のクラス名、Caused by、自分のコードの行)は、どちらの版でも共通です。Java 22〜24 は未実行です。

調べる順番

flowchart TD
    A["1行目の例外名とクラス名を読む"] --> B{"Could not initialize class と出ている?"}
    B -- はい --> C["ログをさかのぼり、最初の ExceptionInInitializerError と Caused by を直す"]
    B -- いいえ --> D{"wrong name と出ている?"}
    D -- はい --> E["パッケージの外側から、パッケージ名つきで実行する"]
    D -- いいえ --> F["jar tf で、クラスがどの jar に入っているか探す"]
    F --> G{"その jar は実行時のクラスパスにある?"}
    G -- ない --> H["-cp / Class-Path / WEB-INF/lib / 依存の scope を直す"]
    G -- ある --> I["クラス名のつづりと大文字小文字、-jar と -cp の併用を確かめる"]

よくある質問

Q. ClassNotFoundException と NoClassDefFoundError は、どちらを先に直せばよいですか? A. Caused by: で2つが一緒に出ているときは、原因は1つです。表示されたクラスが入っている jar を、実行時のクラスパスに加えれば両方とも解消します。

Q. IDE では動くのに、jar にして実行するとエラーになります。 A. IDE は依存 jar を自動でクラスパスに入れて実行します。java -jar では -cp が使われないので、マニフェストの Class-Path を書くか、fat jar にまとめます。

Q. NoClassDefFoundError を try-catch で受けてもよいですか? A. 文法上は書けますが、Error の系統なので、受けて続行するよりも原因(クラスパスや初期化処理)を直すのが基本です。任意の機能の有無を調べたい場合は、Class.forName と ClassNotFoundException の組み合わせが向いています。

Q. クラスファイルはあるのに NoClassDefFoundError になります。 A. メッセージが Could not initialize class なら static 初期化の例外、wrong name ならパッケージと実行場所の不一致です。どちらもクラスパスではなく、メッセージのかっこ内や Caused by が手がかりになります。

まとめ

  • ClassNotFoundException は、文字列でクラス名を渡して読み込むときの検査例外。クラス名はドット区切りで表示されます。
  • NoClassDefFoundError は、コードが直接使うクラスを実行時に読み込めないときのエラー。クラス名はスラッシュ区切りです。
  • Caused by: ClassNotFoundException が付いていれば、実行時のクラスパスに jar がないことが原因です。
  • Could not initialize class は static 初期化の例外が原因。最初に出た ExceptionInInitializerError を探します。
  • java -jar では -cp が使われません。Class-Path か fat jar で対応します。
  • compileOnly / provided の依存は実行時に含まれません。gradle dependencies --configuration runtimeClasspath で確かめられます。
  • Tomcat では WEB-INF/lib に jar があるかを確かめます。

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

(記載例)出典:株式会社RJC「Java ClassNotFoundExceptionの原因と対処 違いも解説」

株式会社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で一緒に開発しながら、 成長を楽しみませんか?