Dev Study
← 解説「.java・.class・バイトコード・javac」に戻る

サンプルコードで身につける: .java・.class・バイトコード・javac

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

1用語早見表: ソースから .class、クラスから main まで

このレッスンの用語の早見表です。前半はファイルと道具の名前、後半はソースの中に書くものの名前です。TypeScript との対応は位置や形のたとえで、 と .js のように中身の性質が違うものもあります。

用語早見表(ソースから .class、クラスから main まで)表
用語 | 一言で
手元では: 何として見えるか
近いもの: Node / NestJS で近いもの
 
ソース(.java)| 人が書く Java のコード
手元では: Main.java のようなテキストファイル
近いもの: .ts
 
javac | .java を読んで .class を作るコンパイラ
手元では: JDK の bin/javac
近いもの: tsc
 
コンパイル | javac でソースを .class に変える作業
手元では: javac Main.java
近いもの: tsc での変換
 
.class | javac の出力。JVM が読むファイル
手元では: Main.class。先頭4バイトは CAFEBABE
近いもの: tsc が出す .js(弱い対応。当たらない点は本文)
 
バイトコード | .class の中の、JVM 向けの命令列
手元では: javap という道具で読める形にできる
近いもの: V8 が実行時に中で作るバイトコード
 
クラス | フィールドとメソッドをまとめた型の定義
手元では: class PoliteGreeter { ... }
近いもの: TypeScript の class
 
オブジェクト | クラスから new で作った実体
手元では: new PoliteGreeter("Hello")
近いもの: new で作ったインスタンス
 
メソッド | クラスの中に書く、呼び出せる処理
手元では: greet(String name)
近いもの: class のメソッド
 
static | クラスに属し、オブジェクト無しで呼べる印
手元では: PoliteGreeter.shout(...)
近いもの: TypeScript の static
 
main | JVM が最初に呼ぶメソッド
手元では: public static void main(String[] args)
近いもの: 無い(Node はファイルを上から実行する)

2最小の Hello を1語ずつ読む

画面に1行出すだけの最小のプログラムです。このコースのサンプルが動く 22 では、この1行のためにと を書きます。 void main(String[] args) の各語が何を表すかをコメントに書きました。引数を渡さずに起動するので、args.length は 0 と出ます。

Java

3javac の前と後でフォルダを見る

レッスン本体のコードを prog.java という名前で置き、 にかける前後でフォルダの中を見た記録です。ソース1つから .class が3つできています。起動するときは、ファイル名ではなく main を持つクラスの名前(Main)を に渡します。

JDK 22(eclipse-temurin:22-jdk)で実行コマンド出力
$ ls
prog.java
$ javac prog.java
$ ls
Greeter.class
Main.class
PoliteGreeter.class
prog.java
$ java Main
Hello, Spring
Good evening, Spring
NO OBJECT NEEDED!
g1 is a PoliteGreeter
same class = true
same object = false

4同じものを TypeScript で書くと

レッスン本体と同じ内容を TypeScript で書いたものです。class、、、static の形はほぼ対応します。違いは、TypeScript ではファイルの一番外に文を書けて main が要らないことと、interface が tsc を通すと消えることです。Java の Greeter は Greeter.class というファイルになって実行時にも残ります。

interface Greeter {
  greet(name: string): string;
}

class PoliteGreeter implements Greeter {
  constructor(private readonly prefix: string) {}

  greet(name: string): string {
    return `${this.prefix}, ${name}`;
  }

  static shout(s: string): string {
    return s.toUpperCase() + "!";
  }
}

// ここから下はファイルの一番外。Java ではこの位置に文を書けないので、main メソッドの中に書く
const g1: Greeter = new PoliteGreeter("Hello");
const g2: Greeter = new PoliteGreeter("Good evening");
console.log(g1.greet("Spring"));
console.log(g2.greet("Spring"));
console.log(PoliteGreeter.shout("no object needed"));

5混同しやすいもの: main の書き方は JDK の版で違う

同じソースを JDK 22 と JDK 25 で動かした結果です。public static void main(String[] args) はどちらでも動きます。void main() だけのクラスや、クラス宣言を書かないファイルは JDK 25 でだけ動きました。文だけを書いたファイルは、どちらでもコンパイルエラーです。JDK 25 向けに書かれたサンプルは、JDK 22 では動かないことがあります。

Temurin 22.0.2 と Temurin 25.0.4.1 で同じソースを実行(各1回。エラーは先頭の1〜2行だけを載せた)表
class Classic { public static void main(String[] args) { ... } }
JDK 22 : 動く
JDK 25 : 動く
 
class InstanceMain { void main() { ... } }
JDK 22 : Error: Main method not found in class InstanceMain, please define the main method as:
public static void main(String[] args)
JDK 25 : 動く
 
クラス宣言を書かず、void main() { ... } だけのファイル
JDK 22 : Compact.java:1: error: implicitly declared classes are a preview feature and are disabled by default.
JDK 25 : 動く
 
System.out.println("x"); の1行だけのファイル
JDK 22 : Bare.java:1: error: class, interface, enum, or record expected
JDK 25 : Bare.java:1: error: class, interface, annotation type, enum, record, method or field expected
 
main が無いクラスを java で起動
JDK 22 : Error: Main method not found in class NoMain, please define the main method as:
public static void main(String[] args)
JDK 25 : JDK 22 と同じ文面

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