Javaの探求インデックス

1章 HelloWorldで理解するJavaの仕組み

 プログラミング言語を学ぶ最初の一歩は、文字列"Hello World"を表示するごくごく小さなプログラムを実際に自分で書いて動かすことから踏み出すことが、言わば慣習となっています。

 そこには、プログラムを作り動かすまでの環境を整備し、プログラミング言語/ツールの手順に沿って操作する、プログラマーとしての基本エッセンスが含まれているのです。

 この章では、環境を整え、手順に沿って操作するとともに、どのようにJavaプログラムが動いているのかを簡単に説明しながら、実際にJavaプログラムのHelloWorldを動かしていきます。Javaのクラスファイルの内容も解析していきます。

1.1 環境を整える

 Javaは1995年の登場から十数年を経過し、現代のアプリケーション開発言語の主流の一つになるまでに成長してきました。ツールベンダー各社を始め、オープンソース開発コミュニティからさまざまなJavaプログラム開発ツールが出ています。最近ではエディタ・コンパイラ・デバッガなどの諸機能を一体化した統合開発環境(IDE:Integrated Development Environment)が主流です。IDEで有名なツールには、Eclipse、NetBeans、JBuilderなどがあります。

 IDEなどの高機能ツールは使い方を覚えれば楽にJavaプログラムを作成できます。しかしながら、本資料の目指すJavaプログラムの動く仕組みを理解するという観点からみると、その動く仕組みがツールによって覆い隠されてしまっています。

 そのため、ここでは、ファイルを編集するツールであるテキスト・エディタ、Java開発キットに含まれる、Javaソースコードをコンパイルするjavacコマンド、Javaクラスファイルを実行するJava仮想マシンを起動するJavaコマンド、Javaクラスファイルを解析するjavapコマンド、Java仮想マシンの内部をモニターするjconsoleコマンド、および、それらを実行するコマンドプロンプト(シェル)環境を使って作業を進めます。

1.1.1 ツールの整備

(1) テキスト・エディタ

 計算機環境によって異なりますが、UNIX系のOS(Linux、Solaris、FreeBSD、MacOS X、その他)を使用しているなら、大抵はVimエディタかEmacsエディタが標準でインストールされています。この両者はプログラム(ソースコード)編集に向いています。また、本章で後にバイナリファイルを16進ダンプで見る際もこの両者のエディタであれば対応可能です。

 Windows OSには標準でメモ帳(notepad)やワードパットといったテキスト編集ツールが搭載されています。しかし、これらはプログラム(ソースコード)編集には不向きです。有償・無償のテキスト・エディタが多数出ているので、それらの中からチョイスするのがよいでしょう。
 これから生涯プログラミングに本格的に取り組んでいくなら、筆者お勧めはEmacsエディタのWindows移植版であるMeadowまたはNTEmacsです。

 あるいは、Eclipse、NetBeansなどをプログラム(ソースコード)編集に使用し、その他のコマンドは別途コマンドプロンプト(シェル)環境上で操作するという方法でもよいでしょう。

 テキスト・エディタの選択、インストール、設定については、別紙1.1 環境構築テキスト・エディタを参照。

(2) Java開発キット(JDK)

 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開発キットを参照。

(3) コマンドプロンプト(シェル)

 JDKの各種コマンドを実行するコマンド環境を使用します。各コマンドのオプション指定や、深いディレクトリ、長いファイル名を指定するので、極力高機能なコマンド環境を使用することが望ましいです。最低限、ディレクトリ名・ファイル名補完、過去実行したコマンド履歴の再利用、コマンドライン上での編集は必要です。

 UNIX系OSなら、ほぼ標準で搭載されているbashシェルを使用できるので十分でしょう。

 Windows XP/Vistaの場合、コマンドプロンプトでも上述の最低限の機能は持っているので何とか使えるレベルにはあります。Cygwin環境をインストールしてUNIX系OS同然のコマンド環境(bash)を使えるようにするのもよいでしょう。ただ、Cygwin環境でWindowsコマンドを使う場合、特有の問題もあるので要注意です。

(4) バイナリ・エディタ 

 クラスファイルの内容を把握する際に使用します。クラスファイルはバイナリ・データなので、バイナリ・エディタを使用して内容を解析します。

 バイナリ・エディタの選択、インストール、設定については、別紙1.1 環境構築テキスト・エディタを参照。

1.1.2 ディレクトリの作成

 ソースファイルの作成、コンパイルしたバイトコードファイルの生成を行うためのディレクトリを用意します。ディレクトリの構成・場所は以下とします。

<ホームディレクトリ>
    +-- work
          +-- hello
                +-- classes
                +-- src

Unix系OS

ホームディレクトリを、/home/torutk とすると、以下のディレクトリ構造を作成します。

/home/torutk/work/hello
    +-- classes
    +-- src
~$ mkdir -p work/hello/classes
~$ mkdir -p work/hello/src
~$

Windows OS

ホームディレクトリを、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> 

1.2 ソースファイルを作成する

 今回作成する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");
    }
}
classfinal修飾子を付けている訳
このクラスはサブクラス化して利用する設計上の意図がないため
privateなコンストラクタを定義している訳
このクラスはインスタンス化して利用する設計上の意図がないため
mainメソッドの引数argsfinal修飾子を付けている訳
引数の変数へのメソッド中での代入(基本は誤り)をコンパイルエラーとして検出するため

ディレクトリ・ファイル構成

ソースファイルを作成した結果、以下のディレクトリ・ファイル構成となります。

Unix系OS

ホームディレクトリを、/home/torutk とすると、以下のディレクトリ構造を作成します。

/home/torutk/work/hello
    +-- classes
    +-- src
         +-- HelloWorld.java

Windows OS

C:\Users\torutk\work\hello
    +-- classes
    +-- src
         +-- HelloWorld.java

1.3 コンパイルする

 Javaソースファイルをコンパイルして、Javaクラスファイル(バイトコード)を生成します。ここでは、javacコマンドを直接使ってコンパイルします。

 javacコマンドのオプションは、クラスファイルの出力先ディレクトリ(トップ)を指定する-dオプション、ソースファイルの格納ディレクトリ(トップ)を指定する-sourcepathオプションを指定しています。

 今回の例ではソースファイルが1つしかないので、-sourcepathオプションはなくてもコンパイルできますが(多くの入門書では省略している)、実用上は複数のソースファイルでプログラムを構成し、かつパッケージを使用するので異なるディレクトリにソースファイルが格納されます。このときは、-sourcepathオプションが必須となるので、最初からこのオプションを使っておくべきです。

javacコマンドによるコンパイル

Unix系OS

~$ cd work/hello
hello$ javac -d classes -sourcepath src src/HelloWorld.java
hello$ 

 実行すると、以下のディレクトリ・ファイル構成となります。

/home/torutk/work/hello
    +-- classes
    |    +-- HelloWorld.class
    +-- src
         +-- HelloWorld.java

Windows OS(コマンドプロンプト)

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 指定を追加する方法です。

1.4 実行する

 コンパイルによって生成されたJavaクラスファイルを実行します。

 クラスパスの指定には、環境変数CLASSPATHではなく、コマンドラインオプション-cpを使っています。環境変数CLASSPATHによる指定はトラブルを招きやすいので、基本使わないようにします。

javaコマンドによる実行

javaコマンドで実行します。-cp オプションで、Javaクラスファイルの格納場所を指定します。

Unix系OS

hello$ java -cp classes HelloWorld
Hello, world

hello$ 

Windows OS(コマンドプロンプト)

C:\Users\torutk\work\hello> java -cp classes HelloWorld
Hello, world

C:\Users\torutk\work\hello> 

javaの実行時の動き

javaコマンド自体は、各OSにおけるネイティブのプログラムです。javaコマンドを実行すると、コマンドライン引数のチェック等を行い、スプラッシュスクリーン指定があればこれを表示し、それからインストールされているJavaVMのライブラリをJNI(ネイティブ側からJava側を呼ぶAPIもある)経由でロード・初期化し、メインクラスをロードし、そのクラスのmainメソッドを呼び出します。

javaコマンド実装調査

制御可能な環境変数

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>

1.5 クラスファイルの構造を調べる

クラスファイルHelloWorld.classの内容を簡単に見てみます。このクラスファイルは非常に小さく、わずか430バイトなので、バイナリを一つずつ読み解いていきます。

HelloWorld.classの内容

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から始まります。

コンスタントプール[1]

00000000: cafe babe 0000 0032 001d 0a00 0600 0f09

タグ:10(0x0a) − CONSTANT_Methodref、class#:6(0x06)、名前と型#:15(0x0f)

コンスタントプール[2]

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)

コンスタントプール[3]

00000010: 0010 0011 0800 120a 0013 0014 0700 1507

タグ:8(0x08) − CONSTANT_String、文字列#:18(0x12)

コンスタントプール[4]

00000010: 0010 0011 0800 120a 0013 0014 0700 1507

タグ:10(0x0a) − CONSTANT_Methodref、class#:19(0x13)、名前と型#:20(0x14)

コンスタントプール[5]

00000010: 0010 0011 0800 120a 0013 0014 0700 1507

タグ:7(0x07) − CONSTANT_Class、名前#:21(0x15)

コンスタントプール[6]

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)

コンスタントプール[7]

00000020: 0016 0100 063c 696e 6974 3e01 0003 2829 .....<init>...()

タグ:1(0x01) − CONSTANT_utf8、長さ:6バイト(0x06)、文字列:"<init>"(0x3c696e69743e)

コンスタントプール[8]〜[14]、[18]、[21]〜[28]

タグ: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  
コンスタントプール[15]

00000080: 6f72 6c64 2e6a 6176 610c 0007 0008 0700

タグ:12(0x0c) − CONSTANT_NameAndType、名前#:7(0x07)、記述#:8(0x08)

コンスタントプール[16]

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)

コンスタントプール[17]

00000090: 170c 0018 0019 0100 0c48 656c 6c6f 2c20 .........Hello,

タグ:12(0x0c) − CONSTANT_NameAndType、名前#:24(0x18)、記述#:25(0x19)

コンスタントプール[19]

000000a0: 776f 726c 64 07 001a 0c00 1b00 1c01 000a world...........

タグ:7(0x07) − CONSTANT_Class、名前#:26(0x1a)

コンスタントプール[20]

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 ...............!

メソッド情報配列

メソッド数で示された数だけ、メソッド情報の定義が続きます。

メソッド情報[0]

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)と対応することを示します。

メソッド情報[1]

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つであることを示します。

属性テーブル

属性テーブル[0]

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")を示します。