Dev Study
← 解説「パッケージ・完全修飾名・import・クラスパス・クラスローダ」に戻る

サンプルコードで身につける: パッケージ・完全修飾名・import・クラスパス・クラスローダ

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1用語早見表: 名前の付け方と、クラスの探し方

このレッスンの用語の早見表です。前半の4つはソースに書く名前の話、後半の2つは実行時にを探す話です。 は Node と同じ単語ですが、することが違います。 は完全修飾名の略として使われる慣習的な略語で、の語ではありません。PATH は OS の環境変数で、とは別物です。

用語早見表(名前の付け方と、クラスの探し方)表
用語 | 一言で
手元では: 何として見えるか
近いもの: Node / NestJS で近いもの
 
パッケージ | クラスをまとめる名前空間
手元では: package com.example.demo;
近いもの: フォルダとモジュールのパス(弱い対応。
相対パスという考え方が無い)
 
単純名 | パッケージ名を付けない短い名前
手元では: List、ArrayList
近いもの: import した後の名前
 
完全修飾名 | パッケージ名と単純名を . でつないだ名前
手元では: java.util.List。FQCN と略されることもある
近いもの: 無い(Node は名前ではなくパスで指す)
 
import | クラスを単純名で書けるようにする宣言
手元では: import java.util.List;
近いもの: 無い(Node の import は名前が同じだけで別物)
 
クラスパス | .class を探しに行く場所の一覧
手元では: java -cp out:lib/x.jar
近いもの: node_modules の探し方(弱い対応。
当たらない点は本文)
 
CLASSPATH | クラスパスを渡す環境変数
手元では: java -cp を書くと、こちらは使われない
近いもの: 無い
 
PATH | OS が java などのコマンドを探す場所の一覧
クラスパスとは別物
手元では: シェルの環境変数 PATH
近いもの: Node を使うときと同じ PATH
 
クラスローダ | クラスを探して読み込む役の部品
手元では: getClassLoader() で見える
近いもの: 対応を確かめていない

2完全修飾名・単純名・パッケージ名を表示する

実行中のクラスに名前を聞いて表示します。getName() が完全修飾名、getSimpleName() がです。String のは で、このパッケージのクラスは import を書かなくても単純名で使えます(自分と同じパッケージのクラスも同じです)。このファイルには が無いので、Main のパッケージ名は空になります。

Java

3どのクラスを、誰が、どこから読み込んだか

クラスパスの値と、3つのクラスを読み込んだを表示します。クラスパスは . (今いるフォルダ)で、自分の Main はそこから app という名前のローダが読み込みました。String や java.sql.Driver は に入っているクラスで、別のローダが JDK の中から読み込みます。クラスパスに無い名前を頼むと になります。

Java

4import を書いても書かなくても、同じ .class になる

import の有無だけが違うソースを4つコンパイルして、 を比べた記録です。import して単純名で書いた a と、import せず完全修飾名で書いた b は同じ内容でした。import だけして使わない c と、import の無い d も同じで、c の .class に Base64 という文字は 0 件、実行時の読み込みのログにも出ません。行番号の情報がずれないよう、b と d には同じ行数のコメントを入れてあります。

JDK 25(Temurin 25.0.4.1)。2回実行して同じ結果。# の行は補足、... は省略した行コマンド出力
# a: import java.util.Base64; と書き、Base64.getEncoder() を使う
# b: import を書かず、java.util.Base64.getEncoder() と書く
# c: import java.util.Base64; と書くが、どこでも使わない
# d: import を書かず、Base64 も使わない
## a: javac ok, Demo.class 614 bytes, sha256 3bd0c2bdc5bda77a
## b: javac ok, Demo.class 614 bytes, sha256 3bd0c2bdc5bda77a
## c: javac ok, Demo.class 404 bytes, sha256 b0e22163e1ff1150
## d: javac ok, Demo.class 404 bytes, sha256 b0e22163e1ff1150
##### cmp a b (import+simple name vs no import+FQN)
IDENTICAL
##### cmp c d (unused import vs no import)
IDENTICAL
##### javap -v c/Demo.class | grep -c Base64
0
...
##### run a with -Xlog:class+load | grep Base64
[0.009s][info][class,load] java.util.Base64 source: jrt:/java.base
[0.009s][info][class,load] java.util.Base64$Encoder source: jrt:/java.base
AQID
##### run c (unused import) with -Xlog:class+load | grep -c Base64
0

5混同しやすいもの: import してあっても、使う行までは探しに行かない

app.Main は lib.util.Helper を import していますが、使うのは引数を渡したときだけです。Helper.class を消しても、引数なしなら最後まで動きました。引数を渡すと、Helper を使う行で になります。Node の import は相手のファイルを読み込みます。Java の import は読み込みをしないので、足りないクラスは使う行に来るまで分かりません。

JDK 25(Temurin 25.0.4.1)。2回実行して同じ結果。... は省略した行コマンド出力
## java -cp out app.Main
main start
main end
exit=0
## java -cp out app.Main x
main start
Helper class initialized
hello from Helper
main end
exit=0
## rm out/lib/util/Helper.class ; java -cp out app.Main
main start
main end
exit=0
## java -cp out app.Main x (Helper.class is gone)
main start
Exception in thread "main" java.lang.NoClassDefFoundError: lib/util/Helper
at app.Main.main(Main.java:9)
Caused by: java.lang.ClassNotFoundException: lib.util.Helper
...

図やコードに出てくる用語(タップで説明)