【Java】Javaのスコープと変数の生存期間を初心者向けにわかりやすく解説

Java

こんにちは、かつコーチです。

「if文の中で作った変数を、外側で使おうとしたらエラーになった」という経験はありませんか。
これはスコープ(変数が有効な範囲)を理解していないとつまずきやすいポイントです。
この記事では、スコープの基本ルールと、変数の生存期間について解説します。

スコープとは

変数が有効な範囲

スコープとは、変数を参照できる範囲のことです。
Javaでは、変数は{ }(波括弧)で囲まれたブロックの中で宣言され、そのブロックの中でだけ有効になります。

public class Sample {
    public static void main(String[] args) {
        int x = 10; // このブロック(mainメソッド)の中で有効

        if (x > 5) {
            int y = 20; // このifブロックの中でだけ有効
            System.out.println(x + y); // 30(xもyも参照できる)
        }

        System.out.println(x); // 10(xは参照できる)
        // System.out.println(y); ← コンパイルエラー:yはここでは無効
    }
}

yifブロックの中で宣言されているため、そのブロックを抜けた時点で参照できなくなります。

なぜスコープが必要なのか

スコープがあることで、変数の影響範囲を必要最小限に限定できます。
もしスコープの概念がなければ、プログラムのどこか一箇所で変数名を変更すると、意図せず遠く離れた別の処理に影響が及ぶ可能性があります。

スコープを狭く保つことは、「その変数がどこで使われているか」を追いやすくし、バグの原因調査を容易にするというメリットにもつながります。

基本の書き方・実装手順

手順1:メソッドのスコープ

メソッド内で宣言した変数(ローカル変数)は、そのメソッドの中でだけ有効です。

public class Sample {
    public void methodA() {
        int value = 1;
        System.out.println(value);
    }

    public void methodB() {
        // System.out.println(value); ← コンパイルエラー:methodAのvalueは見えない
    }
}

methodAvalueと、仮にmethodBで同名のvalueを宣言しても、それぞれ完全に別の変数として扱われます。

手順2:ブロックのスコープ(if文・for文)

if文やfor文などの制御構文も、それぞれ独自のスコープを作ります。

public class Sample {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            System.out.println(i);
        }
        // System.out.println(i); ← コンパイルエラー:forの外ではiは無効
    }
}

for文のカウンタ変数iは、forブロックの中だけで有効なスコープを持ちます。

手順3:フィールド(インスタンス変数)のスコープ

クラス直下で宣言した変数(フィールド)は、そのクラス内のどのメソッドからでもアクセスできます。

public class Counter {
    int count = 0; // フィールド:クラス全体で有効

    public void increment() {
        count++; // どのメソッドからでもアクセス可能
    }

    public void show() {
        System.out.println(count);
    }
}

フィールドのスコープは、ローカル変数よりも広い「クラス全体」になります。

つまずきやすい設定・注意点:変数の生存期間との関係

スコープとよく混同されるのが生存期間(変数がメモリ上に存在している期間)です。
ローカル変数は、そのメソッドの呼び出しが終わると同時にメモリから消えます。
一方、フィールドはインスタンスが存在する限りメモリ上に残り続けます。

つまり、スコープは「どこから参照できるか」、生存期間は「いつまでメモリに存在するか」という、似ているけれど別の概念です。

よくあるつまずきポイント・エラー対処

ブロックの外で変数を使おうとしてエラーになる

初心者が最初につまずきやすいのが、条件分岐の中で宣言した変数を外で使おうとするケースです。

❌Before

public class Sample {
    public static void main(String[] args) {
        int score = 75;

        if (score >= 60) {
            String result = "合格";
        } else {
            String result = "不合格";
        }

        System.out.println(result); // コンパイルエラー
    }
}
error: cannot find symbol
  symbol: variable result

私が実際にこのエラーに遭遇したのは、if/elseそれぞれの中で結果を保持する変数を作り、最後にまとめて出力しようとしたときでした。
resultifブロックとelseブロックそれぞれのスコープの中でしか有効ではないため、ブロックの外にあるprintlnからは見えません。

✅After

ブロックの外側で変数を先に宣言し、ブロックの中で値を代入する形にすれば解決します。

public class Sample {
    public static void main(String[] args) {
        int score = 75;
        String result; // 外側のスコープで宣言

        if (score >= 60) {
            result = "合格";
        } else {
            result = "不合格";
        }

        System.out.println(result); // 合格
    }
}

ifelseの両方で必ず代入されることが保証されていれば、宣言時に初期値を入れなくてもコンパイルエラーにはなりません。

同じスコープ内で変数名が重複する

同じスコープの中で同名の変数を二重に宣言すると、コンパイルエラーになります。

public void method() {
    int value = 1;
    int value = 2; // コンパイルエラー:variable value is already defined
}

内側のブロックで外側と同じ変数名を使うことも、Javaでは許可されていません(言語によっては許可される場合がありますが、Javaは禁止しています)。

int value = 1;
if (true) {
    int value = 2; // コンパイルエラー:外側のvalueと重複
}

応用・一歩先の使い方

スコープを意識した設計の勘所

実務では、「変数のスコープはできるだけ狭くする」のが良いコードの原則の1つとされています。
フィールドとして広いスコープを持たせるべきか、メソッド内のローカル変数として狭く保つべきかを都度検討することで、後から見返したときに「この変数がどこまで影響するか」が把握しやすくなります。

まとめ

この記事のポイント

  • スコープとは変数が有効な範囲のことで、{ }のブロック単位で区切られる
  • ローカル変数はそのメソッド・ブロックの中でだけ有効
  • フィールドはクラス全体で有効になり、ローカル変数よりスコープが広い
  • スコープ(参照できる範囲)と生存期間(メモリに存在する期間)は似て非なる概念

次に読むべき記事

  • クラスの構成要素であるフィールドについては、次章のクラスとオブジェクトの基本の記事で詳しく解説します
  • コマンドライン引数の受け取り方については、次の記事で解説します

タグ: Java, 初心者向け, 基本文法

タイトルとURLをコピーしました