はじめに
株式会社Good Labでエンジニアをしている コータロー です。
日々、Java・SQL・Gitなどの技術情報や、新人エンジニア向けの学習ノウハウ、
AI活用についての情報を発信しています。
Good Labについて気になった方は、コーポレートサイトもぜひご覧ください。
▶コーポレートサイト
「Javaって古くない?」「もっとモダンな言語がいいのでは?」
新人エンジニアの方から、こういった声をよく聞きます。気持ちはわかります。SNSでは新しい言語やフレームワークの話題が飛び交っていますし、Javaのコードは一見すると長くて堅苦しく見えるかもしれません。
でも、実際にJavaで仕事をしてみると「この言語、よく考えられているな」と感じる場面がたくさんあります。
この記事では、Javaをこれから学ぶ方・学び始めたばかりの方に向けて、Javaの良さを正面から、具体的なコード例とともに伝えます。
1. 型安全:「動かしてみたらエラー」を減らせる
Javaは静的型付け言語です。変数や引数、戻り値にはすべて型があり、型の不整合はコンパイル時に検出されます。
import java.util.ArrayList;
import java.util.List;
public class TypeSafeExample {
public static void main(String[] args) {
// コンパイル時に型の不整合を検出
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// names.add(123); // コンパイルエラー!String型のリストにintは入れられない
for (String name : names) {
System.out.println(name.toUpperCase());
}
}
}
ALICE
BOB
動的型付け言語では「実行してみたら型が違ってエラー」ということが起きがちですが、Javaではコンパイルの段階で防げます。プロジェクトの規模が大きくなるほど、この恩恵は大きくなります。
IDEの補完・リファクタリング機能が強力に働くのも、型情報があるからこそです。
2. ボイラープレートの削減:recordで「書かなくていいコード」を書かない
「Javaはコードが長い」という印象を持っている方もいるかもしれません。かつては事実でした。しかし、Java 16以降のrecordによって状況は大きく変わっています。
従来の書き方(Java 15以前)
イミュータブルなデータクラスを定義するには、コンストラクタ、getter、equals()、hashCode()、toString() をすべて手書きする必要がありました。
public class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User user)) return false;
return age == user.age && name.equals(user.name);
}
@Override
public int hashCode() {
return java.util.Objects.hash(name, age);
}
@Override
public String toString() {
return "User[name=" + name + ", age=" + age + "]";
}
}
recordなら1行
public class RecordExample {
// record:たった1行でイミュータブルなデータクラスを定義
record User(String name, int age) {}
public static void main(String[] args) {
User user1 = new User("田中太郎", 25);
User user2 = new User("田中太郎", 25);
// toString() が自動生成される
System.out.println(user1);
// equals() / hashCode() も自動生成される
System.out.println("同一内容か: " + user1.equals(user2));
// アクセサメソッドも自動生成
System.out.println("名前: " + user1.name());
System.out.println("年齢: " + user1.age());
}
}
User[name=田中太郎, age=25]
同一内容か: true
名前: 田中太郎
年齢: 25
たった1行の定義で、コンストラクタ・アクセサ・equals()・hashCode()・toString() がすべて自動生成されます。「Javaは冗長」という時代は終わりつつあります。
3. sealed + パターンマッチング:型で「漏れ」を防ぐ設計
Java 17で正式導入されたsealed class/interfaceと、Java 21で完成したパターンマッチングの組み合わせは、Javaの表現力を大きく引き上げました。
public class SealedExample {
// sealed:許可されたサブクラスだけが継承できる
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// パターンマッチングで網羅的に処理(Java 21)
static double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
Shape tri = new Triangle(3.0, 8.0);
System.out.printf("円の面積: %.2f%n", area(circle));
System.out.printf("長方形の面積: %.2f%n", area(rect));
System.out.printf("三角形の面積: %.2f%n", area(tri));
}
}
円の面積: 78.54
長方形の面積: 24.00
三角形の面積: 12.00
ここで注目すべきは、switch文にdefaultケースがないことです。Shapeはsealed interfaceなので、コンパイラが「すべてのサブタイプを網羅しているか」を検証してくれます。もし将来 Pentagon を追加したら、この switch文はコンパイルエラーになり、対応漏れを確実に検知できます。
「実行時に初めて気づくバグ」ではなく、「コンパイル時に防ぐ仕組み」。これがJavaらしい設計思想です。
4. Stream API:データ処理を宣言的に書く
Java 8で導入されたStream APIは、コレクションの操作を直感的に書ける仕組みです。
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamExample {
record Employee(String name, String department, int salary) {}
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("田中", "開発部", 450_000),
new Employee("鈴木", "開発部", 520_000),
new Employee("佐藤", "営業部", 400_000),
new Employee("高橋", "営業部", 480_000),
new Employee("伊藤", "開発部", 600_000)
);
// 部署ごとの平均給与を計算
Map<String, Double> avgByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::department,
Collectors.averagingInt(Employee::salary)
));
avgByDept.forEach((dept, avg) ->
System.out.printf("%s の平均給与: %.0f円%n", dept, avg));
System.out.println("---");
// 給与50万円以上の社員名を取得
List<String> highEarners = employees.stream()
.filter(e -> e.salary() >= 500_000)
.map(Employee::name)
.toList();
System.out.println("給与50万円以上: " + highEarners);
}
}
開発部 の平均給与: 523333円
営業部 の平均給与: 440000円
---
給与50万円以上: [鈴木, 伊藤]
注意:
avgByDeptはHashMapを内部的に使用するため、部署の出力順序は実行環境によって異なる場合があります。順序を保証したい場合はLinkedHashMapを指定できます。
「何をしたいか(What)」を書けば、「どうやるか(How)」はStream APIが処理してくれます。forループで添字を管理する必要がなく、フィルタ・変換・集約の意図がコードに直接表れます。
5. Optional:nullとの正しい付き合い方
NullPointerExceptionはJavaプログラマの宿敵とも言われてきました。Java 8で導入されたOptionalは、「値が存在しないかもしれない」ことを型で表現する仕組みです。
import java.util.Optional;
public class NullSafeExample {
record User(String name, String email) {}
// Optionalで「値がないかもしれない」ことを型で明示
static Optional<User> findUserByName(String name) {
if ("田中".equals(name)) {
return Optional.of(new User("田中", "tanaka@example.com"));
}
return Optional.empty();
}
public static void main(String[] args) {
// 存在する場合
String email1 = findUserByName("田中")
.map(User::email)
.orElse("メールアドレス未登録");
System.out.println("田中のメール: " + email1);
// 存在しない場合
String email2 = findUserByName("山田")
.map(User::email)
.orElse("メールアドレス未登録");
System.out.println("山田のメール: " + email2);
}
}
田中のメール: tanaka@example.com
山田のメール: メールアドレス未登録
nullチェックのif文を書き忘れることはあっても、Optional を返すメソッドなら「値がないケースを考慮すべき」ということがシグネチャから読み取れます。
6. テキストブロック:複数行文字列の可読性
Java 15で正式導入されたテキストブロック(""")は、JSONやSQLなどの複数行文字列を見たまま書ける機能です。
public class TextBlockExample {
public static void main(String[] args) {
// テキストブロック(Java 15〜):複数行の文字列をそのまま書ける
String json = """
{
"name": "田中太郎",
"age": 25,
"skills": ["Java", "Spring Boot", "SQL"]
}
""";
System.out.println(json);
// SQL文もインデントを保ったまま可読性よく書ける
String sql = """
SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.dept_id = d.id
WHERE e.salary >= 500000
ORDER BY e.name
""";
System.out.println(sql);
}
}
{
"name": "田中太郎",
"age": 25,
"skills": ["Java", "Spring Boot", "SQL"]
}
SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.dept_id = d.id
WHERE e.salary >= 500000
ORDER BY e.name
+ による文字列連結やエスケープ地獄とは無縁です。
7. 仮想スレッド:大量の同時処理をシンプルに
Java 21で正式導入された仮想スレッド(Virtual Threads / Project Loom)は、Javaの並行処理を根本から変える機能です。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
// 仮想スレッドで10万タスクを同時実行
long start = System.currentTimeMillis();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 100_000).forEach(i -> {
executor.submit(() -> {
// I/O待ちをシミュレート
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return i;
});
});
} // try-with-resources で自動的にシャットダウン&タスク完了を待機
long elapsed = System.currentTimeMillis() - start;
System.out.println("10万タスク完了!");
System.out.println("実行時間: " + elapsed + "ms");
System.out.println("使用スレッド: 仮想スレッド(軽量・OS非依存)");
}
}
10万タスク完了!
実行時間: ***ms(環境によって異なる)
使用スレッド: 仮想スレッド(軽量・OS非依存)
従来のプラットフォームスレッドは1スレッドあたり数百KB〜1MB程度のメモリを消費するため、10万スレッドの同時実行は現実的ではありませんでした。仮想スレッドはJVMが管理する軽量なスレッドで、数十万〜数百万単位でも生成可能です。
重要なのは、既存の同期的なコードをほぼそのまま使えること。非同期プログラミング(コールバック地獄やリアクティブストリーム)に書き換える必要がありません。
補足: 仮想スレッドはI/O待ちの多い処理で真価を発揮します。CPU集約型の処理では、プラットフォームスレッドと比べて速度面の優位はありません。
8. エコシステムの厚み
Javaの強みは言語仕様だけではありません。エコシステムの成熟度と規模が桁違いです。
主要フレームワーク・ライブラリ
| 分野 | 名前 | 概要 |
|---|---|---|
| Webアプリ | Spring Boot | Java Webアプリのデファクトスタンダード。Fortune 500企業の90%以上が採用 |
| ビルドツール | Maven / Gradle | 依存関係管理とビルドの自動化 |
| ORM | Hibernate / JPA | データベースとオブジェクトのマッピング |
| テスト | JUnit 5 / Mockito | 単体テスト・モックの定番 |
| ログ | SLF4J / Logback | 構造化ログの標準 |
| JSON | Jackson / Gson | JSONのシリアライズ・デシリアライズ |
これらはいずれも長年の実績があり、ドキュメント・書籍・Q&Aサイトの情報量が豊富です。「困ったら検索すれば答えが見つかる」環境は、学習コストの大幅な削減につながります。
Spring Bootの存在感
Spring Bootは単なるフレームワークではなく、事実上のJavaアプリケーション開発プラットフォームです。2026年現在、Spring AIの登場によりLLM連携も容易になり、AI時代のアプリケーション開発にも対応しています。
9. JVM:「一度書けばどこでも動く」は今も健在
Javaのコードはバイトコードにコンパイルされ、JVM(Java Virtual Machine) 上で実行されます。Windows、macOS、Linuxを問わず、JVMさえあれば同じプログラムが動きます。
さらにJVM上では、Kotlin、Scala、Groovyなどの言語も動作します。Javaのエコシステムに投資した知識は、これらの言語に移行しても活きるということです。
GC(ガベージコレクション)の進化
メモリ管理はJVMが自動で行ってくれます。C/C++のように手動でメモリを解放する必要はありません。
さらに、GCの性能も年々進化しています。Java 21以降ではGenerational ZGCがデフォルトで利用可能になり、大規模アプリケーションでも停止時間(pause time)をミリ秒単位に抑えられるようになりました。
10. キャリアとしてのJava
技術的な魅力だけでなく、キャリアの観点からもJavaには大きな価値があります。
求人市場
- TIOBE Index 2026年3月時点で世界第3〜4位を維持
- 日本国内のエンジニア求人では、Javaは常にトップクラスの需要
- 金融、官公庁、EC、物流、医療など基幹系システムでの採用が圧倒的に多い
学習投資の回収効率
Javaを深く学ぶことで得られる知識は、Java以外でも活きます。
- オブジェクト指向の設計原則(SOLID、デザインパターン)
- 型システムの理解(ジェネリクス、型安全設計)
- 並行処理の基礎(スレッド、同期、アトミック操作)
- JVMの仕組み(GC、クラスローダー、バイトコード)
これらはKotlin、C#、TypeScriptなど他の言語に移っても応用できる普遍的な知識です。
資格制度
Oracle Certified Java Programmer(Java Silver / Gold) という公式認定資格があり、スキルの客観的な証明として使えます。SES企業や大手SIerでの評価指標になっている場合も多いです。
11. 2026年のJava:進化は止まっていない
最新LTS:Java 25(2025年9月リリース)
2026年4月現在、最新のLTS(Long-Term Support)バージョンはJava 25です。主な新機能を紹介します。
| 機能 | JEP | 概要 |
|---|---|---|
| コンパクトソースファイル | JEP 512 | クラス宣言なしでプログラムを書ける |
| 柔軟なコンストラクタ本体 | JEP 513 |
super() の前にバリデーション処理を記述可能 |
| モジュールインポート宣言 | - | モジュール単位でのインポートが可能に |
| プリミティブ型のパターンマッチング | - | switch文でプリミティブ型にもパターンマッチングを適用 |
「Hello World」をクラス宣言なしで書けるようになり、入門者のハードルが下がりました。
// Java 25:コンパクトソースファイル(JEP 512)
void main() {
System.out.println("Hello, Java 25!");
}
注意: この構文はJava 25以降で利用可能です。Java 21環境では従来の
public static void main(String[] args)を使用してください。
LTSリリースサイクル
Javaは6か月ごとに新バージョンがリリースされ、2年ごとにLTSが提供されます。次のLTSはJava 29(2027年9月予定) です。
| バージョン | 種別 | リリース年月 |
|---|---|---|
| Java 17 | LTS | 2021年9月 |
| Java 21 | LTS | 2023年9月 |
| Java 25 | LTS | 2025年9月 |
| Java 29 | LTS(予定) | 2027年9月 |
「枯れた言語」どころか、半年に一度の新機能投入で着実に進化を続けています。
まとめ
Javaの良さを改めて整理します。
| 観点 | Javaの強み |
|---|---|
| 型安全 | コンパイル時にバグを検出。IDEの補完・リファクタリングも強力 |
| モダンな構文 | record、sealed class、パターンマッチング、テキストブロック |
| 並行処理 | 仮想スレッドにより、シンプルなコードで大規模並行処理 |
| エコシステム | Spring Boot、Maven/Gradle、JUnit等の成熟した開発基盤 |
| ポータビリティ | JVMによるクロスプラットフォーム実行 |
| キャリア | 求人数トップクラス、公式認定資格、普遍的なスキルの習得 |
| 継続的進化 | 6か月ごとのリリース、2年ごとのLTS提供 |
Javaは「古い言語」ではなく、30年の蓄積の上に、今も進化を続けている言語です。
最初は記述量の多さや型の厳格さに戸惑うかもしれません。でもそれは、大規模開発で安全にコードを書くための仕組みです。学べば学ぶほど「こうなっている理由」がわかってきます。
これからJavaを学ぶ皆さん、ぜひ「Javaらしさ」を楽しんでください。
参考
- Oracle Java SE Documentation
- OpenJDK JDK 25
- JEP 512: Compact Source Files and Instance Main Methods
- JEP 513: Flexible Constructor Bodies
- JEP 444: Virtual Threads
- JEP 395: Records
- JEP 409: Sealed Classes
- JEP 441: Pattern Matching for switch
- Spring Boot 公式
- TIOBE Index
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!