ブロック構造の全体像
ブロック構造は、波括弧 { ... } で囲まれた「まとまり」です。Java のコードはブロックの入れ子でできており、スコープ(見える範囲)とライフタイム(生きている期間)、制御の始まりと終わりをブロックが決めます。メソッド本体・if/else・for/while・try/catch/finally・匿名ブロック・クラス初期化子(static/インスタンス)など、すべてが「ブロック」という共通ルールの上に成り立ちます。
ブロックの基本とスコープの関係
ブロックは「見える範囲」を切る
ブロック内で宣言したローカル変数は、そのブロックの内側でだけ参照できます。外に出ると見えません。これが「ブロックスコープ」です。
void demo() {
int x = 10; // メソッドブロックのスコープ
{ // 匿名の小ブロック
int y = 20; // このブロックの中だけ
System.out.println(x + y); // OK
}
// System.out.println(y); // NG: y は見えない
}
Javaライフタイムは「実行中の期間」
ローカル変数は、そのブロックが実行されている間だけメモリ上に生きます。ブロックを離れたら寿命が終わります。スコープ(静的な範囲)とライフタイム(動的な期間)は概念が異なる点を押さえておきましょう。
制御構文とブロック
if/else とブロック
if の本体を 1 行だけにするときでも、原則として { ... } を付けると安全です。将来の拡張時に意図しない動きを防げます。
if (ok) {
log("ok");
} else {
warn("ng");
}
Javafor/while とブロック
ループの本文はブロックです。初期化で宣言した変数(for のカウンタ)はループ内でのみ見えます。
for (int i = 0; i < 3; i++) {
System.out.println(i); // i はここだけ
}
// System.out.println(i); // NG
Javaswitch とブロック
各 case を独立させたいなら、明示的に小ブロックで囲みます。変数の衝突や意図しない再利用を防げます。
switch (kind) {
case 1: { String label = "ONE"; System.out.println(label); break; }
case 2: { String label = "TWO"; System.out.println(label); break; }
}
Javatry/catch/finally とリソースのブロック
try 本体と finally の保証
try は「ここで作業する」ブロック、finally は「必ず後始末する」ブロックです。早期 return や例外があっても finally は実行されます。
Resource r = acquire();
try {
if (!r.ready()) return; // 早期終了でも
use(r);
} finally {
r.close(); // 必ず呼ばれる
}
Javatry-with-resources のスコープ
リソース変数は try の括弧内で宣言するブロックに属します。ブロック終端で自動的にクローズされ、外側からは見えません。
import java.nio.file.*;
import java.io.*;
try (var br = Files.newBufferedReader(Path.of("a.txt"))) {
System.out.println(br.readLine());
}
// br はここでは参照不可
Javaクラスの初期化ブロック(static/インスタンス)
static 初期化子(クラス読み込み時)
クラスがロードされた瞬間に一度だけ実行されるブロックです。定数表やキャッシュの初期化に使います。
class Config {
static final java.util.Map<String, Integer> M;
static {
M = new java.util.HashMap<>();
M.put("A", 1);
M.put("B", 2);
}
}
Javaインスタンス初期化子(new のたび)
コンストラクタの前に、インスタンスごとに実行されるブロックです。複数コンストラクタに共通の準備をまとめたいときに有効です。
class Box {
int v;
{ v = 10; } // インスタンス初期化ブロック
Box() {}
Box(int x) { v = x; } // 上書きも可能
}
Java変数宣言・シャドーイング・整合性(重要ポイントの深掘り)
同スコープで同名再宣言は不可
見える範囲が重なるところで同名のローカル変数は宣言できません。衝突する場合はブロックを分けるか、変数名を変えます。
void bad() {
int a = 1;
// int a = 2; // NG: 同スコープで重複
}
Javaフィールドを隠す(シャドー)場合の明示
ローカルがフィールドと同名だとローカルが優先されます。フィールドを参照したいなら this.field を使って区別します。
class C {
int value = 10;
void f() {
int value = 99; // ローカルがフィールドを隠す
System.out.println(this.value); // 10
}
}
Java未初期化の使用はコンパイルで禁止(definite assignment)
ローカル変数は「使う前に必ず代入されている」ことが静的に保証されなければなりません。if の片方だけで代入しているとエラーになります。
int x;
if (flag) x = 1;
// System.out.println(x); // NG: 代入が保証されない
int y;
if (flag) y = 1; else y = 0; // 両枝で代入
System.out.println(y); // OK
Javaラムダ・パターン変数・同期ブロックのスコープ
ラムダが外側の変数を読む条件(実質的 final)
ラムダや匿名クラスは、外側のローカル変数を「再代入しない」場合だけキャプチャできます。
int base = 10; // 実質的 final
java.util.function.IntUnaryOperator f = x -> x + base; // OK
// base = 20; // NG: キャプチャ後の再代入は不可
Javainstanceof のパターン変数は「真の範囲だけ」
パターンマッチの変数は、条件が真だったブロック内でのみ有効です。
Object o = "hi";
if (o instanceof String s) {
System.out.println(s.toUpperCase()); // s はここだけ有効
}
Javasynchronized ブロックでの共有状態アクセス
共有資源を扱う処理をブロックで囲み、ロックの取得から解放までを「一つのまとまり」にします。
final Object lock = new Object();
synchronized (lock) {
// 共有状態へのアクセスはここに集約
}
Java例題で身につける
例 1: 小さな匿名ブロックで一時変数を閉じ込める
int total = 0;
{
int add = 5; // ここだけの一時変数
total += add;
}
System.out.println(total); // 5
// add は見えない → 衝突しない
Java例 2: ループの本文をブロックで区切って初期化を毎回行う
for (int i = 0; i < 3; i++) {
{ // 各周回で独立の一時領域
int base = i * 10;
System.out.println(base);
} // base はこの周回ごとに消える
}
Java例 3: try-with-resources で「作業の塊」を安全に閉じる
import java.nio.file.*;
import java.io.*;
try (var br = Files.newBufferedReader(Path.of("data.txt"))) {
String line = br.readLine();
System.out.println(line);
} // ブロック終端で自動クローズ
Java例 4: static 初期化子で一度きりのセットアップ
class App {
static final java.util.Set<String> ALLOWED;
static {
ALLOWED = new java.util.HashSet<>();
ALLOWED.add("READ");
ALLOWED.add("WRITE");
}
}
Java仕上げのアドバイス(重要部分のまとめ)
ブロックは「見える範囲・寿命・後始末」を切る単位です。ローカル変数はブロック内のみ可視、同名再宣言は重複スコープでは不可。if/for/while/switch/try の本文をブロックで明示すれば、拡張や安全性に強くなる。リソースは try-with-resources のブロックで閉じ、初期化は static/インスタンス初期化子のブロックへまとめる。ラムダのキャプチャは実質的 final、パターン変数は真の範囲のみ有効——この型が身につくと、見通しのよい安全なコードが自然に書けます。
