ローカル変数の全体像
メソッド内ローカル変数は「メソッドの中だけで使える一時的なメモ」。メソッドが呼ばれている間だけ存在し、終わったら消えます。オブジェクトの状態(フィールド)とは別物で、外から見えません。初期化しないと使えない、スコープ(見える範囲)がブロックで区切られる、というルールを理解すると安全に書けます。
宣言・初期化・スコープ(基本のルール)
宣言と初期化
ローカル変数は宣言しただけでは使えません。「必ず初期化してから」使います。フィールドと違い、デフォルト値は自動で入りません。
public void greet(String name) {
String trimmed = name == null ? "" : name.trim(); // 初期化
if (trimmed.isEmpty()) System.out.println("Hello, guest");
else System.out.println("Hello, " + trimmed);
}
Java未初期化のローカル変数を使おうとするとコンパイルエラーになります。これは「初期化忘れによるバグ」を防ぐための強いガードです。
スコープ(見える範囲)
ローカル変数は「宣言したブロックの内側」でのみ見えます。ブロックを出ると存在しない扱いになります。
public void demo(boolean ok) {
if (ok) {
int count = 1;
System.out.println(count);
}
// System.out.println(count); // エラー:ここからは見えない
}
Java同じ名前を内側で宣言すると外側を隠してしまい(シャドーイング)、読みづらくなります。基本は避けましょう。
ライフサイクルとメモリ(重要ポイントの深掘り)
いつ生まれていつ消えるか
ローカル変数はメソッドの呼び出し開始で生まれ、ブロック終了で消えます。JVMでは主にスタックフレーム上に置かれ、メソッドが終わるとフレームごと破棄されます。フィールドのように「長生き」しないため、スレッド安全性が高く、使い捨ての計算に向いています。
使い回しより「狭いスコープ」が正解
変数の有効範囲はできるだけ狭く取るほど、誤用や改変リスクが減ります。必要な直前で宣言・初期化し、使い終えたらブロックを閉じるクセをつけましょう。
public int sum(java.util.List<Integer> xs) {
int total = 0; // ここでだけ使う
for (int x : xs) total += x;
return total;
}
Javafinal・実質的 final・ラムダ・内部クラス
final と「実質的 final」
ローカル変数に final を付けると「再代入禁止」になります。ラムダ式やローカル内部クラスが外側の変数を参照するには、その変数が「final または実質的 final(結果として一度も再代入していない)」である必要があります。
public void capture() {
final String prefix = "Hi"; // または、一度も再代入しないなら final 省略でも可
Runnable r = () -> System.out.println(prefix); // 参照可能
r.run();
}
Java再代入した変数はラムダで参照できません。予期せぬ変更を避け、並行性や読みやすさを守るための設計上のルールです。
ローカルクラスとキャプチャ
メソッド内でクラスを定義する「ローカルクラス」も、外側のローカル変数を参照するには同じく final/実質的 final が必要です。
public void localClass() {
String msg = "ok";
class Printer { void print() { System.out.println(msg); } }
new Printer().print(); // msg は再代入していないので参照可
}
Java型推論(var)と可読性
var の使いどころ
Java 10 以降、ローカル変数は var で型推論できます。右辺から型が明確に分かるときに使うと、重複を減らして読みやすくなります。
var list = new java.util.ArrayList<String>(); // 右辺で型が明確
var name = "Taro"; // String と分かる
Javaただし、戻り値がインターフェース型や複雑なジェネリクスで「何の型か分かりにくい」場合は、明示的な型を残す方が読み手に優しいです。
例外・リソースとローカル変数
try-with-resources で安全に閉じる
ファイルやソケットなど「閉じる責務があるリソース」は、ローカル変数に受けて try-with-resources を使うと、ブロック終端で自動クローズされます。
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
public void read(Path p) throws java.io.IOException {
try (var br = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} // ここで br が自動クローズ
}
Javaリソース変数のスコープを最小にすると、扱いが安全になります。
シャドーイングと this の明示
フィールドと同名のローカルは避ける
ローカルがフィールド名を隠すと混乱します。どうしても同名になるなら、フィールド側は this.field と明示して誤解を防ぎます。
public final class Product {
private String name;
public void rename(String name) {
var x = name == null ? "" : name.trim();
if (!x.isEmpty()) this.name = x; // フィールド更新を明示
}
}
Java例題で身につける
例 1: 入力の正規化と短いスコープ
public int countWords(String text) {
String trimmed = text == null ? "" : text.trim(); // ここだけで使う
if (trimmed.isEmpty()) return 0;
String normalized = trimmed.replaceAll("\\s+", " "); // ここだけで使う
return normalized.split(" ").length;
}
Java「使う直前で宣言・初期化」「使い終わったらスコープを抜けて消える」ことで、変数の迷子を防ぎます。
例 2: var と明示型のバランス
public java.util.List<String> top3(java.util.List<String> names) {
var sorted = new java.util.ArrayList<>(names); // 右辺から型が分かる
sorted.sort(String::compareTo);
java.util.List<String> head = sorted.subList(0, Math.min(3, sorted.size())); // 戻り型が List と分かるよう明示
return head;
}
Java「読めるかどうか」で var か明示型かを選びます。
例 3: ラムダでのキャプチャ
public void runTasks(java.util.List<String> tasks) {
int size = tasks.size(); // 実質的 final(再代入なし)
tasks.forEach(t -> System.out.println(size + ": " + t)); // 参照可能
}
Javasize を再代入するとラムダから参照できなくなるため、値を変えない設計にします。
よくあるつまずきと回避
初期化忘れ
フィールドはデフォルトが入りますが、ローカルは入りません。必ず初期化してから使う。分岐で初期化されない経路がないかを意識しましょう。
スコープが広すぎる
メソッド冒頭で「とりあえず全部」宣言すると、後半で誤用や再代入の危険が増えます。必要な直前で最小スコープに。
シャドーイングによる混乱
フィールドと同名のローカルを作らない。やむを得ず同名なら this.field を明示し、ローカル名は意味のある別名にする。
var の乱用
型が分かりづらくなるなら var は使わない。右辺が明確なときだけ使う。
仕上げのアドバイス(重要部分のまとめ)
ローカル変数は「短命で安全な作業メモ」。必ず初期化してから使い、スコープは最小に、シャドーイングは避ける。ラムダやローカルクラスで参照するなら final/実質的 final に保つ。var は「分かりやすいときだけ」。リソースは try-with-resources で閉じる——この型を守ると、メソッドの読みやすさと安全性が一気に上がります。
