[ Javaの探求インデックス ]
プログラミング言語を学ぶ最初の一歩は、文字列"Hello World"を表示するごくごく小さなプログラムを実際に自分で書いて動かすことから踏み出すことが、言わば慣習となっています。
そこには、プログラムを作り動かすまでの環境を整備し、プログラミング言語/ツールの手順に沿って操作する、プログラマーとしての基本エッセンスが含まれているのです。
この章では、環境を整え、手順に沿って操作するとともに、どのようにJavaプログラムが動いているのかを簡単に説明しながら、実際にJavaプログラムのHelloWorldを動かしていきます。Javaのクラスファイルの内容も解析していきます。
Javaは1995年の登場から十数年を経過し、現代のアプリケーション開発言語の主流の一つになるまでに成長してきました。ツールベンダー各社を始め、オープンソース開発コミュニティからさまざまなJavaプログラム開発ツールが出ています。最近ではエディタ・コンパイラ・デバッガなどの諸機能を一体化した統合開発環境(IDE:Integrated Development Environment)が主流です。IDEで有名なツールには、Eclipse、NetBeans、JBuilderなどがあります。
IDEなどの高機能ツールは使い方を覚えれば楽にJavaプログラムを作成できます。しかしながら、本資料の目指すJavaプログラムの動く仕組みを理解するという観点からみると、その動く仕組みがツールによって覆い隠されてしまっています。
そのため、ここでは、ファイルを編集するツールであるテキスト・エディタ、Java開発キットに含まれる、Javaソースコードをコンパイルするjavacコマンド、Javaクラスファイルを実行するJava仮想マシンを起動するJavaコマンド、Javaクラスファイルを解析するjavapコマンド、Java仮想マシンの内部をモニターするjconsoleコマンド、および、それらを実行するコマンドプロンプト(シェル)環境を使って作業を進めます。
計算機環境によって異なりますが、UNIX系のOS(Linux、Solaris、FreeBSD、MacOS X、その他)を使用しているなら、大抵はVimエディタかEmacsエディタが標準でインストールされています。この両者はプログラム(ソースコード)編集に向いています。また、本章で後にバイナリファイルを16進ダンプで見る際もこの両者のエディタであれば対応可能です。
Windows OSには標準でメモ帳(notepad)やワードパットといったテキスト編集ツールが搭載されています。しかし、これらはプログラム(ソースコード)編集には不向きです。有償・無償のテキスト・エディタが多数出ているので、それらの中からチョイスするのがよいでしょう。
これから生涯プログラミングに本格的に取り組んでいくなら、筆者お勧めはEmacsエディタのWindows移植版であるMeadowまたはNTEmacsです。
あるいは、Eclipse、NetBeansなどをプログラム(ソースコード)編集に使用し、その他のコマンドは別途コマンドプロンプト(シェル)環境上で操作するという方法でもよいでしょう。
テキスト・エディタの選択、インストール、設定については、別紙1.1 環境構築のテキスト・エディタを参照。
Javaは言語仕様も仮想マシン仕様も公開されており、Sun Microsystems社(2011年現在、Oracle社に買収されているが、本記事記述はSun Microsystemsのままとする)以外からもIBMのJikesやGNU GCCに含まれるgcjなどのJavaコンパイラ、KaffeなどのJava仮想マシン、Apache HarmonyプロジェクトでのJ2SE5.0互換JDK開発などが進められております。しかしながらサーバー用途や最新バージョンへの追従や実装の網羅性が薄いなど実用的には不十分であり、Sun Microsystems社から公開されているJava開発キットを使用するのが現時点では妥当です。
Sun Microsystems社のJava開発キット(JDK)のインストール、設定については、別紙1.1 環境構築のJava開発キットを参照。
JDKの各種コマンドを実行するコマンド環境を使用します。各コマンドのオプション指定や、深いディレクトリ、長いファイル名を指定するので、極力高機能なコマンド環境を使用することが望ましいです。最低限、ディレクトリ名・ファイル名補完、過去実行したコマンド履歴の再利用、コマンドライン上での編集は必要です。
UNIX系OSなら、ほぼ標準で搭載されているbashシェルを使用できるので十分でしょう。
Windows XP/Vistaの場合、コマンドプロンプトでも上述の最低限の機能は持っているので何とか使えるレベルにはあります。Cygwin環境をインストールしてUNIX系OS同然のコマンド環境(bash)を使えるようにするのもよいでしょう。ただ、Cygwin環境でWindowsコマンドを使う場合、特有の問題もあるので要注意です。
クラスファイルの内容を把握する際に使用します。クラスファイルはバイナリ・データなので、バイナリ・エディタを使用して内容を解析します。
バイナリ・エディタの選択、インストール、設定については、別紙1.1 環境構築のテキスト・エディタを参照。
ソースファイルの作成、コンパイルしたバイトコードファイルの生成を行うためのディレクトリを用意します。ディレクトリの構成・場所は以下とします。
<ホームディレクトリ> +-- work +-- hello +-- classes +-- src
ホームディレクトリを、/home/torutk とすると、以下のディレクトリ構造を作成します。
/home/torutk/work/hello +-- classes +-- src
~$ mkdir -p work/hello/classes ~$ mkdir -p work/hello/src ~$
ホームディレクトリを、C:\Users\torutk とすると、以下のディレクトリ構造を作成します。
C:\Users\torutk\work\hello +-- classes +-- src
C:\Users\torutk> mkdir work\hello\classes C:\Users\torutk> mkdir work\hello\src C:\Users\torutk>
今回作成するHelloWorldプログラムは、1つのソースファイルから構成され、パッケージは未使用、クラスは1つ、クラス内の要素は静的(static)メソッド1つというシンプルなものです。
ディレクトリ構造で作成したhello/srcディレクトリ下に、HelloWorld.java というファイル名で(大文字・小文字も正確に)ファイルを作成します。
public final class HelloWorld { private HelloWorld() { } public static void main(final String[] args) { System.out.println("Hello, world"); } }
- classにfinal修飾子を付けている訳
- このクラスはサブクラス化して利用する設計上の意図がないため
- privateなコンストラクタを定義している訳
- このクラスはインスタンス化して利用する設計上の意図がないため
- mainメソッドの引数argsがfinal修飾子を付けている訳
- 引数の変数へのメソッド中での代入(基本は誤り)をコンパイルエラーとして検出するため
ソースファイルを作成した結果、以下のディレクトリ・ファイル構成となります。
ホームディレクトリを、/home/torutk とすると、以下のディレクトリ構造を作成します。
/home/torutk/work/hello +-- classes +-- src +-- HelloWorld.java
C:\Users\torutk\work\hello +-- classes +-- src +-- HelloWorld.java
Javaソースファイルをコンパイルして、Javaクラスファイル(バイトコード)を生成します。ここでは、javacコマンドを直接使ってコンパイルします。
javacコマンドのオプションは、クラスファイルの出力先ディレクトリ(トップ)を指定する-dオプション、ソースファイルの格納ディレクトリ(トップ)を指定する-sourcepathオプションを指定しています。
今回の例ではソースファイルが1つしかないので、-sourcepathオプションはなくてもコンパイルできますが(多くの入門書では省略している)、実用上は複数のソースファイルでプログラムを構成し、かつパッケージを使用するので異なるディレクトリにソースファイルが格納されます。このときは、-sourcepathオプションが必須となるので、最初からこのオプションを使っておくべきです。
~$ cd work/hello hello$ javac -d classes -sourcepath src src/HelloWorld.java hello$
実行すると、以下のディレクトリ・ファイル構成となります。
/home/torutk/work/hello +-- classes | +-- HelloWorld.class +-- src +-- HelloWorld.java
C:\Users\torutk> cd work\hello C:\Users\torutk\work\hello> javac -d classes -sourcepath src src\HelloWorld.java C:\Users\torutk\work\hello>
実行すると、以下のディレクトリ・ファイル構成となります。
C:\Users\torutk\work\hello +-- classes | +-- HelloWorld.class +-- src +-- HelloWorld.java
ソースファイルに非ASCIIコード(日本語等)を使う場合、プラットフォームのデフォルトエンコーディングと違うエンコーディング形式で保存する場合は、javacの-encodingオプションでソースファイルのエンコーディングを明示的に指定します。
例えば、ソースファイルをUTF-8形式で作成し、Windows OS上でコンパイルする場合、Windows OSのデフォルトエンコーディングはCP932(またの名をMS932、IANA登録名はWindows-31J。これらはシフトJISを出発点にしている)と異なるので、そのままコンパイルすると警告やエラーとなってしまいます。警告となるのは、Java
2 SE 5用のコンパイル時(-source 1.5)で、エラーとなるのはJava SE 6用のコンパイル時(-source 1.6)のときでした。また、警告のみでクラスファイルが生成されても、文字列リテラルは化けてしまうので、正しい動作とならないことがあります。
Javaでプログラムを書く利点の1つに、異なるOSで同じプログラムを開発し実行できることがあるので、ソースファイルを作成したOSとは別のOSでコンパイルする機会も多いです。そのときに、ソースファイルの文字コードについて発生しえる問題点と対処方法を知っておくとよいでしょう。
筆者お勧めは、ソースファイルのエンコーディングはUTF-8とし、Windows OS上でコンパイルするときは、-encoding UTF-8 指定を追加する方法です。
コンパイルによって生成されたJavaクラスファイルを実行します。
クラスパスの指定には、環境変数CLASSPATHではなく、コマンドラインオプション-cpを使っています。環境変数CLASSPATHによる指定はトラブルを招きやすいので、基本使わないようにします。
javaコマンドで実行します。-cp オプションで、Javaクラスファイルの格納場所を指定します。
hello$ java -cp classes HelloWorld Hello, world hello$
C:\Users\torutk\work\hello> java -cp classes HelloWorld Hello, world C:\Users\torutk\work\hello>
javaコマンド自体は、各OSにおけるネイティブのプログラムです。javaコマンドを実行すると、コマンドライン引数のチェック等を行い、スプラッシュスクリーン指定があればこれを表示し、それからインストールされているJavaVMのライブラリをJNI(ネイティブ側からJava側を呼ぶAPIもある)経由でロード・初期化し、メインクラスをロードし、そのクラスのmainメソッドを呼び出します。
C:\Users\torutk\work\hello>java -cp classes HelloWorld ----_JAVA_LAUNCHER_DEBUG---- JRE path is C:\Program Files\Java\jdk1.6.0\jre EnsureJreInstallation:unsupported platform jvm.cfg[0] = ->-server<- jvm.cfg[1] = ->-client<- jvm.cfg[2] = ->-hotspot<- name: -hotspot vmType: VM_ALIASED_TO alias: -server jvm.cfg[3] = ->-classic<- jvm.cfg[4] = ->-native<- jvm.cfg[5] = ->-green<- 316 micro seconds to parse jvm.cfg Default VM: server JVM path is C:\Program Files\Java\jdk1.6.0\jre\bin\server\jvm.dll JRE path is C:\Program Files\Java\jdk1.6.0\jre CRT path is C:\Program Files\Java\jdk1.6.0\jre\bin\msvcr71.dll 3230 micro seconds to LoadJavaVM JavaVM args: version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 4 option[ 0] = '-Djava.class.path=.' option[ 1] = '-Djava.class.path=classes' option[ 2] = '-Dsun.java.command=HelloWorld' option[ 3] = '-Dsun.java.launcher=SUN_STANDARD' 91544 micro seconds to InitializeJVM Main-Class is 'HelloWorld' Apps' argc is 0 7554 micro seconds to load main class ----_JAVA_LAUNCHER_DEBUG---- こんにちは、世界 C:\Users\torutk\work\hello>
クラスファイルHelloWorld.classの内容を簡単に見てみます。このクラスファイルは非常に小さく、わずか430バイトなので、バイナリを一つずつ読み解いていきます。
HelloWorld.classをバイナリエディタで見たものが以下です。
00000000: cafe babe 0000 0032 001d 0a00 0600 0f09 .......2........ 00000010: 0010 0011 0800 120a 0013 0014 0700 1507 ................ 00000020: 0016 0100 063c 696e 6974 3e01 0003 2829 .....<init>...() 00000030: 5601 0004 436f 6465 0100 0f4c 696e 654e V...Code...LineN 00000040: 756d 6265 7254 6162 6c65 0100 046d 6169 umberTable...mai 00000050: 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 n...([Ljava/lang 00000060: 2f53 7472 696e 673b 2956 0100 0a53 6f75 /String;)V...Sou 00000070: 7263 6546 696c 6501 000f 4865 6c6c 6f57 rceFile...HelloW 00000080: 6f72 6c64 2e6a 6176 610c 0007 0008 0700 orld.java....... 00000090: 170c 0018 0019 0100 0c48 656c 6c6f 2c20 .........Hello, 000000a0: 776f 726c 6407 001a 0c00 1b00 1c01 000a world........... 000000b0: 4865 6c6c 6f57 6f72 6c64 0100 106a 6176 HelloWorld...jav 000000c0: 612f 6c61 6e67 2f4f 626a 6563 7401 0010 a/lang/Object... 000000d0: 6a61 7661 2f6c 616e 672f 5379 7374 656d java/lang/System 000000e0: 0100 036f 7574 0100 154c 6a61 7661 2f69 ...out...Ljava/i 000000f0: 6f2f 5072 696e 7453 7472 6561 6d3b 0100 o/PrintStream;.. 00000100: 136a 6176 612f 696f 2f50 7269 6e74 5374 .java/io/PrintSt 00000110: 7265 616d 0100 0770 7269 6e74 6c6e 0100 ream...println.. 00000120: 1528 4c6a 6176 612f 6c61 6e67 2f53 7472 .(Ljava/lang/Str 00000130: 696e 673b 2956 0031 0005 0006 0000 0000 ing;)V.1........ 00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............! 00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*....... 00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................ 00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%. 00000180: 0200 0100 0000 09b2 0002 1203 b600 04b1 ................ 00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................ 000001a0: 0008 0008 0001 000d 0000 0002 000e ..............
クラスファイルの先頭4バイトは、Javaのクラスファイルであることを示すマジックナンバーで、0xCAFEBABE固定です。
00000000: cafe babe 0000 0032 001d 0a00 0600 0f09
次の4バイトは、クラスファイルが実行対象とするJavaバージョンを識別するバージョン番号です。前半2バイトがマイナー・バージョンで後半2バイトがメジャーバージョンとなります。
以下は、マイナーバージョンが0(0x0000)、メジャーバージョンが50(0x0032)を表します。
00000000: cafe babe 0000 0032 001d 0a00 0600 0f09
バージョン番号と、Java SEのバージョンの対応は以下の表となります。
メジャーバージョン | マイナーバージョン | Java SEバージョン | 備考 |
---|---|---|---|
51 | Java SE 7 | ||
50 | Java SE 6 | ||
49 | J2SE 5.0 | ||
48 | Java2 SE 1.4 | ||
47 | Java2 SE 1.3 | ||
46 | Java2 SE 1.2 | ||
45 | Java 1.1 |
リテラル、実行時に解決するメソッド、フィールド参照、などの各種定数を持つコンスタントプールの個数です。ただし、コンスタントプールには実際に現れない添え字0の分を含んだ個数となります。
以下は、コンスタントプール個数が29(0x1d)を表します。
00000000: cafe babe 0000 0032 001d 0a00 0600 0f09
コンスタントプール個数で示された数だけ、コンスタントプールの定義が続きます。コンスタントプールのフォーマットは種類により異なります。種類は先頭1バイトのタグで決まります。
なお、コンスタントプールの添え時は1から始まります。
00000000: cafe babe 0000 0032 001d 0a00 0600 0f09
タグ:10(0x0a) − CONSTANT_Methodref、class#:6(0x06)、名前と型#:15(0x0f)
00000000: cafe babe 0000 0032 001d 0a00 0600 0f09 .......2........
00000010: 0010 0011 0800 120a 0013 0014 0700 1507
タグ:9(0x09) − CONSTANT_Fieldref、class#:16(0x10)、名前と型#:17(0x11)
00000010: 0010 0011 0800 120a 0013 0014 0700 1507
タグ:8(0x08) − CONSTANT_String、文字列#:18(0x12)
00000010: 0010 0011 0800 120a 0013 0014 0700 1507
タグ:10(0x0a) − CONSTANT_Methodref、class#:19(0x13)、名前と型#:20(0x14)
00000010: 0010 0011 0800 120a 0013 0014 0700 1507
タグ:7(0x07) − CONSTANT_Class、名前#:21(0x15)
00000010: 0010 0011 0800 120a 0013 0014 0700 1507 ................
00000020: 0016 0100 063c 696e 6974 3e01 0003 2829 .....<init>...()
タグ:7(0x07) − CONSTANT_Class、名前#:22(0x16)
00000020: 0016 0100 063c 696e 6974 3e01 0003 2829 .....<init>...()
タグ:1(0x01) − CONSTANT_utf8、長さ:6バイト(0x06)、文字列:"<init>"(0x3c696e69743e)
タグ:1のCONSTANT_utf8が続きます。
コンスタントプール | 文字列内容 | |
[8] | ()V | |
[9] | Code | |
[10] | LineNumberTable | |
[11] | main | |
[12] | (Ljava/lang/String;)V | |
[13] | SourceFile | |
[14] | HelloWorld.java | |
[18] | Hello, world | |
[21] | HelloWorld | |
[22] | java/lang/Object | |
[23] | java/lang/System | |
[24] | out | |
[25] | Ljava/io/PrintStream; | |
[26] | java/io/PrintStream | |
[27] | println | |
[28] | (Ljava/lang/String;)V |
00000080: 6f72 6c64 2e6a 6176 610c 0007 0008 0700
タグ:12(0x0c) − CONSTANT_NameAndType、名前#:7(0x07)、記述#:8(0x08)
00000080: 6f72 6c64 2e6a 6176 610c 0007 0008 0700 orld.java.......
00000090: 170c 0018 0019 0100 0c48 656c 6c6f 2c20 .........Hello,
タグ:7(0x07) − CONSTANT_Class、名前#:23(0x17)
00000090: 170c 0018 0019 0100 0c48 656c 6c6f 2c20 .........Hello,
タグ:12(0x0c) − CONSTANT_NameAndType、名前#:24(0x18)、記述#:25(0x19)
000000a0: 776f 726c 64 07 001a 0c00 1b00 1c01 000a world...........
タグ:7(0x07) − CONSTANT_Class、名前#:26(0x1a)
000000a0: 776f 726c 64 07 001a 0c00 1b00 1c01 000a
タグ:12(0x0c) − CONSTANT_NameAndType、名前#:27(0x1b)、記述#:28(0x1c)
クラス宣言またはインタフェース宣言で使用する修飾子のビットマスクを表します。
00000130: 696e 673b 2956 0031 0005 0006 0000 0000 ing;)V.1........
この例では、ACC_SUPER(0x0020)、ACC_FINAL(0x0010)、ACC_PUBLIC(0x0001)が指定されています。ACC_SUPERは必ずセットされる値なので、このクラスはfinalかつpublic修飾子が指定されたことを表します。
このクラスファイルが定義するクラスを示すコンスタントプールのクラス情報のインデックスを表します。
00000130: 696e 673b 2956 0031 0005 0006 0000 0000 ing;)V.1........
この例では、コンスタントプール[5] を指しています。これは、さらに名前をコンスタントプール[21](すなわち"HelloWorld")を指しています。
このクラスファイルが定義するクラスのスーパークラスを示すコンスタントプールのクラス情報のインデックスを表します。
00000130: 696e 673b 2956 0031 0005 0006 0000 0000 ing;)V.1........
この例では、コンスタントプール[6]を指しています。これは、さらに名前をコンスタントプール[22](すなわち"java/lang/Object")を指しています。
このクラスファイルが定義するクラスの直接のスーパー・インタフェースの数を表します。(直接implementsしているインタフェース数)
00000130: 696e 673b 2956 0031 0005 0006 0000 0000 ing;)V.1........
この例では0個、すなわち直接implementsしているインタフェースがないことを表しています。
このクラスファイルが定義するクラスのフィールド数を表します。
00000130: 696e 673b 2956 0031 0005 0006 0000 0000 ing;)V.1........
この例では0個、すなわちこのクラスで定義しているフィールドはないことを表します。
このクラスファイルが定義するクラスのメソッド数を表します。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
メソッド数で示された数だけ、メソッド情報の定義が続きます。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
アクセス修飾子のマスクを表します。この例ではACC_PRIVATE(0x0002)を定義しています。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
メソッド名を示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[7](すなわち"<init>")を指しています。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
メソッドの引数・戻り値を表現するメソッド・ディスクリプタを示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[8](すなわち" ()V")を指しています。
これは、引数なし、戻り値型はvoidです。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
メソッドの追加属性の数を表します。この例では、メソッド追加属性が1個あることを示します。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
メソッドの追加属性の名前を示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[9](すなわち"Code")を示します。これは、追加属性がCode属性であることを表しています。
00000140: 0002 0002 0007 0008 0001 0009 0000 0021 ...............!
このコード属性の長さを表します。この例では、これより後ろにコード属性が33バイト(0x21)続くことを示します。
00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*.......
この例では、このコードの実行中にオペランドスタック上の最大の深さは1であることを表します。
00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*.......
呼び出し時のメソッド引数を含めたローカル変数の数を表します。この例では、ローカル変数は1個であることを表します。
00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*.......
直後のコード配列のバイト数を表します。この例では、コード配列が5バイト(0x0000 0005)であることを示します。
00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*.......
メソッドを実装するJava仮想マシンコードを表します。
0x2a aload_0 ローカル変数#0をオペランドスタックへロード 0xb7 0001 invokespecial #1 インスタンスメソッド#1を呼び出し (#1 - java/lang/Objectクラスの<init>()V ) 0xb1 return
00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*.......
例外テーブル(exception_table)の個数を表します。この例では例外テーブルは0個を示します。
00000150: 0001 0001 0000 0005 2ab7 0001 b100 0000 ........*.......
00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................
Code属性のもつ属性の個数を表します。この例では、Code属性のもつ属性は1個であることを示します。
00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................
Code属性の属性の名前を示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[10](すなわち "LineNumberTable")を示します。
00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................
LineNumberTable属性の続く長さ(バイト)を表します。この例では10バイトです。
00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................
行番号テーブルが2件あることを示します。
00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................
1つ目の行番号テーブルは、コード配列のインデックス0(0x0000)が、このクラスファイルの元となるソースコードの3行目(0x0003)と対応することを示します。
00000160: 0100 0a00 0000 0a00 0200 0000 0300 0400 ................
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
2つ目の行番号テーブルは、コード配列のインデックス4(0x0004)が、このクラスファイルの元となるソースコードの4行目(0x0004)と対応することを示します。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
アクセス修飾子のマスクを表します。この例ではACC_STATIC(0x0008)かつACC_PUBLIC(0x0001)を定義しています。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
メソッド名を示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[11](すなわち"main")を指しています。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
メソッドの引数・戻り値を表現するメソッド・ディスクリプタを示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[12](すなわち"(Ljava/lang/String;)V")を指しています。
これは、引数String[]、戻り値型はvoidです。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
メソッドの追加属性の数を表します。この例では、メソッド追加属性が1個あることを示します。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
メソッドの追加属性の名前を示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[9](すなわち"Code")を示します。これは、追加属性がCode属性であることを表しています。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
このコード属性の長さを表します。この例では、これより後ろにコード属性が37バイト(0x25)続くことを示します。
00000170: 0400 0900 0b00 0c00 0100 0900 0000 2500 ..............%.
00000180: 0200 0100 0000 09b2 0002 1203 b600 04b1 ................
この例では、このコードの実行中にオペランドスタック上の最大の深さは2であることを表します。
00000180: 0200 0100 0000 09b2 0002 1203 b600 04b1 ................
呼び出し時のメソッド引数を含めたローカル変数の数を表します。この例では、ローカル変数(引数)は1個であることを表します。
00000180: 0200 0100 0000 09b2 0002 1203 b600 04b1 ................
直後のコード配列のバイト数を表します。この例では、コード配列が9バイト(0x0000 0009)であることを示します。
00000180: 0200 0100 0000 09b2 0002 1203 b600 04b1 ................
メソッドを実装するJava仮想マシンコードを表します。
0xb2 0002 getstatic #2 クラスフィールド#2をオペランドスタックへロード (#2 - java/lang/Systemクラスのout ) 0x12 03 ldc #3 データ#3をオペランドスタックへロード (#3 - "Hello, world" ) 0xb6 0004 invokevirtual #4 インスタンスメソッド#4を呼び出し (#4 - java/io/PrintStreamクラスのprintln) 0xb1 return
00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................
例外テーブル(exception_table)の個数を表します。この例では例外テーブルは0個を示します。
00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................
Code属性のもつ属性の個数を表します。この例では、Code属性のもつ属性は1個であることを示します。
00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................
Code属性の属性の名前を示すコンスタントプールのインデックスを表します。この例ではコンスタントプール[10](すなわち "LineNumberTable")を示します。
00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................
LineNumberTable属性の続く長さ(バイト)を表します。この例では10バイトです。
00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................
行番号テーブルが2件あることを示します。
00000190: 0000 0001 000a 0000 000a 0002 0000 0007 ................
1つ目の行番号テーブルは、コード配列のインデックス0(0x0000)が、このクラスファイルの元となるソースコードの7行目(0x0007)と対応することを示します。
000001a0: 0008 0008 0001 000d 0000 0002 000e ..............
2つ目の行番号テーブルは、コード配列のインデックス8(0x0008)が、このクラスファイルの元となるソースコードの8行目(0x0008)と対応することを示します。
このクラスファイルで定義されるクラスの属性(attribute)テーブルに存在する属性数を表します。
000001a0: 0008 0008 0001 000d 0000 0002 000e ..............
この例では、属性数が1つであることを示します。
000001a0: 0008 0008 0001 000d 0000 0002 000e ..............
属性名を示すコンスタントプールのインデックスを表します。この例では、コンスタントプール[13](すなわち"SourceFile")を示します。
000001a0: 0008 0008 0001 000d 0000 0002 000e ..............
属性の長さ(バイト)を表します。SourceFile属性の場合は必ず2(0x0000 0002)となります。
000001a0: 0008 0008 0001 000d 0000 0002 000e ..............
このクラスファイルの元となるソースファイルの名前を示すコンスタントプールのインデックスを表します。この例では、コンスタントプール[14](すなわち"HelloWorld.java")を示します。