※当サイトの記事には、広告・プロモーションが含まれます。

Javaでビルド後に生成される.classファイル自体にはpackage文は影響しない?

gigazine.net

UUIDv7をプライマリキーとして使用することで、データベース用の連番キーの生成や管理が不要になり、アプリケーションのロジックを簡素化することが可能です。さらに、UUIDv7は標準UUIDの形式に準拠しているため、従来のUUID向けのコードやライブラリをそのまま活用でき、簡単にバージョンを移行できるとのこと。

UUIDなのにデータベースのプライマリキーに設定してもパフォーマンスの問題を起こさない「UUIDv7」の標準化作業が進行中 - GIGAZINE

⇧「主キー(プライマリキー)」がアルファベットを含みたくない(10進数の数字のみで構成したい)の場合とか、どうなるんだろうか?

Javaでビルド後に生成される.classファイル自体にはpackage文は影響しない?

いままで、気にするような機会が無かったので、今さら感がありますが、

docs.oracle.com

-d directory

クラス・ファイルの出力先ディレクトリを設定します。そのディレクトリはjavacによって作成されないため、すでに存在している必要があります。クラスがパッケージに含まれる場合、javacは、パッケージ名を反映したサブディレクトリにクラス・ファイルを置き、必要に応じてディレクトリを作成します。

-d C:\myclassesと指定し、クラスの名前がcom.mypackage.MyClassである場合、クラス・ファイルはC:\myclasses\com\mypackage\MyClass.classになります。

-dオプションが指定されていない場合、javacは各クラス・ファイルを、その生成元のソース・ファイルと同じディレクトリ内に置きます。

注: -dオプションで指定したディレクトリはユーザー・クラス・パスに自動的には追加されません。

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/javac.html

⇧ とあるように、「.java」ファイルで、package文を記述していると、ビルド後に

■package文を記載している場合のビルドで生成されるもの

  • 「.class」ファイルに記述したpackage文のサブディレクト
  • 「.class」ファイル

が生成され、package文を記述していないと、

■package文を記載していない場合のビルドで生成されるもの

  • 「.class」ファイル

が生成されますと。

つまり、package文を記述しようが記述しまいが、生成される「.class」ファイル自体には影響しませんと。

と思ってたんだけど、ちょっと分からん...、後述します。

Eclipseを使っていると、クラス(「.java」ファイル)の作成時に、

「デフォルト・パッケージの使用は推奨されません」っていう警告が出てくるから、「デフォルト・パッケージ」は宜しくないっていう刷り込みがされていて、「デフォルト・パッケージ」で運用するJavaアプリケーションを想定していなかったのだけど、package文の記述の無いJavaアプリケーションを扱う機会を得て、曖昧になっていたので調べてみた次第です。

ちなみに、Eclipseの「プロジェクト(P)」>「プロジェクトのビルド(B)」でビルドされると(「自動的にビルド(M)」にチェックが付いていると、「プロジェクトのビルド(B)」は選択できない)、「javac」の「-d」オプションを付けた時と同じ挙動になるっぽい。(つまり、package文が記述されていれば、サブディレクトリが生成される)

⇧「パッケージ・エクスプローラー」だと表示されていないけど、

⇧「プロジェクト・エクスプローラー」で、「.class」ファイルが表示されて、「.classpath」ファイルで指定したパスに「.class」ファイルは生成されてました。

「.java」ファイルが「デフォルト・パッケージ」配下にあるので、サブディレクトリなどは作成されていないようです。

で、package文は、「.class」ファイルにも影響するのかどうかなんだけど、

■package文なしの「.java」ファイルから生成された「.class」ファイル

// 次からコンパイル OOMESample06.java (バージョン 11 : 55.0, スーパー・ビット)
public class OOMESample06 {
  
  // メソッド記述子 #6 ()V
  // スタック: 1, ローカル: 1
  public OOMESample06();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [8]
    4  return
      行数:
        [pc: 0, 行: 4]
      ローカル変数テーブル:
        [pc: 0, pc: 5] ローカル: this インデックス: 0 型: OOMESample06
  
  // メソッド記述子 #15 ([Ljava/lang/String;)V
  // スタック: 4, ローカル: 5
  public static void main(java.lang.String[] args) throws java.lang.ClassNotFoundException;
     0  new java.util.ArrayList [19]
     3  dup
     4  invokespecial java.util.ArrayList() [21]
     7  astore_1 [classList]
     8  iconst_0
     9  istore_2 [i]
    10  new OOMESample06$MyClassLoader [22]
    13  dup
    14  invokespecial OOMESample06$MyClassLoader() [24]
    17  astore_3 [loader]
    18  aload_3 [loader]
    19  new java.lang.StringBuilder [25]
    22  dup
    23  ldc <String "DynamicClass"> [27]
    25  invokespecial java.lang.StringBuilder(java.lang.String) [29]
    28  iload_2 [i]
    29  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [32]
    32  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [36]
    35  invokevirtual java.lang.ClassLoader.loadClass(java.lang.String) : java.lang.Class [40]
    38  astore 4 [dynamicClass]
    40  aload_1 [classList]
    41  aload 4 [dynamicClass]
    43  invokeinterface java.util.List.add(java.lang.Object) : boolean [46] [nargs: 2]
    48  pop
    49  iinc 2 1 [i]
    52  goto 10
      行数:
        [pc: 0, 行: 6]
        [pc: 8, 行: 7]
        [pc: 10, 行: 10]
        [pc: 18, 行: 11]
        [pc: 40, 行: 12]
        [pc: 49, 行: 13]
        [pc: 52, 行: 9]
      ローカル変数テーブル:
        [pc: 0, pc: 55] ローカル: args インデックス: 0 型: java.lang.String[]
        [pc: 8, pc: 55] ローカル: classList インデックス: 1 型: java.util.List
        [pc: 10, pc: 55] ローカル: i インデックス: 2 型: int
        [pc: 18, pc: 52] ローカル: loader インデックス: 3 型: java.lang.ClassLoader
        [pc: 40, pc: 52] ローカル: dynamicClass インデックス: 4 型: java.lang.Class
      ローカル変数型テーブル:
        [pc: 8, pc: 55] ローカル: classList インデックス: 1 型: java.util.List<java.lang.Class<?>>
        [pc: 40, pc: 52] ローカル: dynamicClass インデックス: 4 型: java.lang.Class<?>
      スタック・マップ・テーブル: フレーム数 1
        [pc: 10, 追加: {java.util.List, int}]
      メソッド・パラメーター:
        args

  インナー・クラス:
    [インナー・クラス情報: #22 OOMESample06$MyClassLoader, 外部クラス情報: #1 OOMESample06
     インナー名: #70 MyClassLoader, アクセス・フラグ: 8 static]

Nest Members:
   #22 OOMESample06$MyClassLoader
}

■package文ありの「.java」ファイルから生成された「.class」ファイル

// 次からコンパイル EndpointAppWatchServiceNormal.java (バージョン 1.8 : 52.0, スーパー・ビット)
public class apps.EndpointAppWatchServiceNormal {
  
  // メソッド記述子 #6 ()V
  // スタック: 1, ローカル: 1
  public EndpointAppWatchServiceNormal();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [8]
    4  return
      行数:
        [pc: 0, 行: 10]
      ローカル変数テーブル:
        [pc: 0, pc: 5] ローカル: this インデックス: 0 型: apps.EndpointAppWatchServiceNormal
  
  // メソッド記述子 #15 ([Ljava/lang/String;)V
  // スタック: 2, ローカル: 2
  public static void main(java.lang.String[] args);
     0  new apps.EndpointAppWatchServiceNormal [1]
     3  dup
     4  invokespecial apps.EndpointAppWatchServiceNormal() [16]
     7  astore_1 [endpointAppWatchServiceNormal]
     8  aload_1 [endpointAppWatchServiceNormal]
     9  aload_0 [args]
    10  invokevirtual apps.EndpointAppWatchServiceNormal.startWatchServiceNormal(java.lang.String[]) : void [17]
    13  return
      行数:
        [pc: 0, 行: 13]
        [pc: 8, 行: 14]
        [pc: 13, 行: 15]
      ローカル変数テーブル:
        [pc: 0, pc: 14] ローカル: args インデックス: 0 型: java.lang.String[]
        [pc: 8, pc: 14] ローカル: endpointAppWatchServiceNormal インデックス: 1 型: apps.EndpointAppWatchServiceNormal
      メソッド・パラメーター:
        args
  
  // メソッド記述子 #15 ([Ljava/lang/String;)V
  // スタック: 4, ローカル: 5
  public void startWatchServiceNormal(java.lang.String[] args);
     0  aload_1 [args]
     1  arraylength
     2  ifne 8
     5  invokestatic apps.service.impl.service_watcher.BaseServiceWatcherRecursive.usage() : void [24]
     8  iconst_0
     9  istore_2 [recursive]
    10  aload_1 [args]
    11  iconst_0
    12  aaload
    13  ldc <String "-r"> [29]
    15  invokevirtual java.lang.String.equals(java.lang.Object) : boolean [31]
    18  ifeq 23
    21  iconst_1
    22  istore_2 [recursive]
    23  iload_2 [recursive]
    24  ifeq 64
    27  aload_1 [args]
    28  iconst_1
    29  aaload
    30  iconst_0
    31  anewarray java.lang.String [32]
    34  invokestatic java.nio.file.Paths.get(java.lang.String, java.lang.String[]) : java.nio.file.Path [37]
    37  astore_3 [dirNormal]
    38  new apps.service.impl.service_watcher.ServiceWatcherFromNormalImpl [43]
    41  dup
    42  aload_3 [dirNormal]
    43  iload_2 [recursive]
    44  invokespecial apps.service.impl.service_watcher.ServiceWatcherFromNormalImpl(java.nio.file.Path, boolean) [45]
    47  astore 4 [serviceWatcherFromNormalImpl]
    49  aload 4 [serviceWatcherFromNormalImpl]
    51  invokevirtual apps.service.impl.service_watcher.ServiceWatcherFromNormalImpl.processEvents() : void [48]
    54  goto 64
    57  astore 4 [e]
    59  aload 4 [e]
    61  invokevirtual java.io.IOException.printStackTrace() : void [51]
    64  return
      例外テーブル:
        [pc: 38, pc: 54] -> 57 時刻 : java.io.IOException
      行数:
        [pc: 0, 行: 19]
        [pc: 5, 行: 20]
        [pc: 8, 行: 23]
        [pc: 10, 行: 24]
        [pc: 21, 行: 25]
        [pc: 23, 行: 28]
        [pc: 27, 行: 29]
        [pc: 38, 行: 32]
        [pc: 49, 行: 34]
        [pc: 54, 行: 36]
        [pc: 59, 行: 38]
        [pc: 64, 行: 41]
      ローカル変数テーブル:
        [pc: 0, pc: 65] ローカル: this インデックス: 0 型: apps.EndpointAppWatchServiceNormal
        [pc: 0, pc: 65] ローカル: args インデックス: 1 型: java.lang.String[]
        [pc: 10, pc: 65] ローカル: recursive インデックス: 2 型: boolean
        [pc: 38, pc: 64] ローカル: dirNormal インデックス: 3 型: java.nio.file.Path
        [pc: 49, pc: 54] ローカル: serviceWatcherFromNormalImpl インデックス: 4 型: apps.service.impl.service_watcher.ServiceWatcherFromNormalImpl
        [pc: 59, pc: 64] ローカル: e インデックス: 4 型: java.io.IOException
      スタック・マップ・テーブル: フレーム数 4
        [pc: 8, 同じ]
        [pc: 23, 追加: {int}]
        [pc: 57, フル, スタック: {java.io.IOException}, ローカル: {apps.EndpointAppWatchServiceNormal, java.lang.String[], int, java.nio.file.Path}]
        [pc: 64, チョップ 1 ローカル]
      メソッド・パラメーター:
        args
}

う~ん、コンパイル対象のクラス名には、package文で宣言したパッケージ名が付いているんだけど、コンパイル後(「.class」ファイル)がどういう扱われるのかが分からん...

動作確認してみたら、package文ありの「.java」ファイルでビルドした「.class」ファイルはpackage文で指定したサブディレクトリが作成されている必要があって、そこに配置されていないと「.class」ファイルの実行で、エラーになりましたわ...

「.java」ファイルでpackage文を宣言してるので、

package文の宣言で指定した「apps.jvm.option」の階層のサブディレクトリを作成して、そこに、ビルドした「.class」ファイルを配置したら動作しました。

まじか...

何の気なしに、package文を追加すると、ビルド後の「.class」ファイルが動作しないってことやん...

package文があることが当たり前な感覚になっていたから、あんまり気にしてこなかったけど...

というわけで、

  • package文あり
  • package文なし

は別物で、「package文」は影響しまくります。

毎度モヤモヤ感が半端ない...

今回はこのへんで。