PR

Javaの変数の基本を1分で(ローカル変数/インスタンス変数/クラス変数)

Java

Javaの勉強を進めている方が「意外と理解できてそうでできていない」基本知識が、変数の種類と使い分けです。

Javaには大きく分けて、ローカル変数インスタンス変数クラス変数の3つが存在するのですが、そもそもなんで変数が3種類に分かれるのか?という根本的な理由を理解すると使い分けが自然に身につきやすくなります。ここでは初心者の方にもイメージしやすいように、なるべく平易な言葉で解説していきます。

スポンサーリンク

変数とは「データを一時的にしまっておく箱」

そもそも「変数」とは何でしょうか?

プログラムでは、何かしらデータ(数値、文字、文字列、オブジェクトなど)を扱うときに、そのデータを一時的にメモリに保存し、取り出したり書き換えたりします。この「データを置いておく場所」のことを変数と呼びます。

Javaではこの「変数をどこで定義するか?」で3つのパターンに分かれます。それが、ローカル変数インスタンス変数クラス変数です。

Java:ローカル変数

ローカル変数とは、メソッドの中で宣言し、そのメソッド内でだけ使える変数のことです。

public class LocalVariableExample {
    public static void main(String[] args) {
        int number = 10;  // ← ローカル変数(mainメソッド内だけで使える)
        System.out.println("Number is: " + number);
    }
}

// Number is: 10

↑のnumberはメソッドが呼び出されて処理が走り始めるとメモリ上に生まれ、メソッドの処理が終わると一緒に消えていきます。(参考 Javaのメソッドとは?基本を1から

Q
なぜ、ローカル変数はメソッドの中だけでしか使えない?
A

プログラムはメソッドごとに処理をまとめて呼び出します。呼ばれたときに必要な情報だけ一時的に用意して、終わったら捨てるほうが、メモリを効率的に使えます。そこでメソッド内だけで使い終わるデータは「局所的(ローカル)」に保持するという仕組みになっているわけです。

逆にローカル変数がどこでも使えてしまうとすると、その変数分メモリが使われる=処理全体が重くなりますし、変数名の重複も頻繁に発生してしまうでしょう。

必要なときだけ使って、終わったら忘れる。それがローカル変数の役目です✨

ローカル変数は必ず初期化する必要がある

Javaでは、ローカル変数は自動で初期化されません。これはメモリの仕組み上、明確に値が入るとわからない変数を使うとバグになりやすいため、あえてコンパイラレベルで強制しているものです。

void sampleMethod() {
    int count; // 初期化しないまま...
    // System.out.println(count); // コンパイルエラー: 初期化されていない変数は使えない
}

明示的に初期値を指定しないと「値が不定」という状態を避けられないので、コンパイルエラーになります。これは「安全対策」だと捉えてください。

varが使える理由

Java 10以降、ローカル変数に対してvarでの型推論(参考 「var」宣言の基本)が使えるようになりました。これは宣言と同時に代入することで、「入ってくるデータ型がその場で決まっている」からこそ可能になった仕組みです。

void example() {
    var num = 10; // ここでは自動的にint型と推論される
}

一方で、インスタンス変数やクラス変数ではvarが使えません。それは、クラスが読み込まれる段階で型推論する情報がないからと理解すると自然です。

Java:インスタンス変数

次はインスタンス変数です。インスタンス変数はクラスの中で宣言され、オブジェクト(=インスタンス)ごとに持つ変数のことです。

メソッドの外、クラスの中で定義されます。

public class Person {
    int age;       // インスタンス変数
    String name;   // インスタンス変数

    public Person() {
        age = 20;
        name = "Unknown";
    }
}

↑のagenameは、それぞれのPersonオブジェクトが生まれたときにメモリ領域が確保されます。たとえば、Person p1 = new Person();Person p2 = new Person();を実行すると、p1p2それぞれが自分専用のagenameを持つイメージです。

Javaの解説書や参考書によっては、「インスタンス変数」という言い方を使わずに「フィールド」という用語で統一して説明している場合があります。つまり、

インスタンス変数 = フィールドの一種

という関係です。

そのため、書籍によっては「フィールド」という言葉だけで、インスタンス変数や後述するクラス変数(static)などをまとめて説明するケースが多くあります。

実務や学習の中では、「フィールド」という言葉で理解しておけば、ほとんど問題ありません。

用語範囲staticの有無説明
フィールドクラスの中にあるすべての変数あり or なし一番広い言い方
インスタンス変数フィールドの一種なしオブジェクトごとに値が違う
クラス変数(static)フィールドの一種あり(static付き)クラス全体で1つだけ値を持つ

インスタンス変数の初期化は必須ではない

インスタンス変数は自動的に0やnullなどのデフォルト値が設定されます。これはヒープ領域(オブジェクトを管理するためのメモリ領域)を確保するときにJVM(Java Virtual Machine)が「ゴミが残らないように必ず初期状態を揃える」仕組みを用意しているためです。

逆に、ローカル変数のように局所的に使われるものは「明示的に初期化しないなら使わせない」という安全策が働きます。これが変数の種類による「自動初期化」の差につながっています。

デフォルト値の設定→ある変数が生成されたタイミングで、自動的に0やfalsenullなどの値が割り当てられることを指します。具体的には下記のような初期値が入ります:

  • 整数系(int, long, short, byte):0
  • 浮動小数点系(float, double):0.0
  • booleanfalse
  • 参照型(String, その他のクラス型):null

ポイント デフォルト値が設定される理由

1:ヒープ領域の初期化による恩恵

Javaのインスタンス変数やクラス変数は、ヒープ領域やメタスペース(クラスの情報を管理する領域)などで管理されます。ヒープ領域に確保したメモリは、「予期せぬゴミデータが残らないように、あらかじめ安全な値に初期化する」という仕組みになっています。

  • インスタンス変数newキーワードでオブジェクトを生成した際、ヒープ領域に確保された変数は自動的にデフォルト値がセットされます。
  • クラス変数(static変数):クラスをロードするときに同様の仕組みでメモリが初期化されます。

2:バグの軽減と安全性の向上

もしヒープ領域にランダムなゴミデータが残った状態でオブジェクトを生成してしまうと、プログラムは意図しない動きをする恐れがあります。C/C++など一部の言語では、自分で明示的に初期化しないとゴミデータを参照してしまうケースがあるため、バグが起こりやすいという問題点がありました。

Javaでは、こうした事故を減らすため、インスタンス変数やクラス変数には必ず安全なデフォルト値を設定する設計になっているのです。結果として「どんな変数でも、一貫した初期状態が保証される」メリットがあります。

3:ローカル変数は自動初期化されない

一方、メソッド内で使うローカル変数はデフォルト値がつきません。これは逆に「初期化のし忘れによるバグ」をコンパイラで検出する」ために、あえて自動初期化を行わない仕様です。ローカル変数は一時的に使い捨てる性質のため、明示的に初期化をしなければコンパイルエラーにしてしまうことで、プログラマのミスを強制的に防いでいます。

Java:クラス変数(static変数)

staticを付けて宣言した変数は、クラス変数(static変数)と呼びます。これはインスタンスを作らなくても、クラスに対して1つだけ存在する変数です。

public class Counter {
    static int totalCount = 0;

    public Counter() {
        totalCount++;
    }
}

↑のコードだと、new Counter()するたびにtotalCountがインクリメントされます。インスタンスの数とは関係なく、クラスとしてひとつだけ存在するグローバルな変数というイメージです。

クラス変数については、staticの意味を知っていれば理解は簡単なはずです。

Javaにはローカル変数(=メソッド内で定義)とフィールド(=メソッド外で定義)の2種類があり、後者をstaticかどうかで、インスタンス変数・クラス変数と言い分けている、と理解してもOKです。

クラス変数もデフォルト値が設定される

クラス変数もヒープではなくメソッド領域(またはメタスペース)と呼ばれる領域で管理されます。このときも、自動で0やnullといったデフォルト値が割り当てられます。クラスがロードされるタイミングで「このクラス変数には必ず何らかの初期値をセットしておこう」という仕組みが働くためです。

ローカル変数/インスタンス変数/クラス変数の使い分け

変数の種類と動作原理を理解すると、「この場面ではどの変数を使うべきか?」が自然とイメージしやすくなります。

  1. メソッドの内部だけで値を扱いたいローカル変数
    • 例:計算途中の一時的な値、メソッドのパラメータやループカウンタなど。
    • 使用後は破棄されるので、メモリ効率が良くなる。
  2. オブジェクトごとに異なる値を持たせたいインスタンス変数
    • 例:Personごとの年齢や名前、Carごとの車種。
    • それぞれのインスタンスにひも付いてデータを保持する。
  3. クラス全体で共有したい情報を持たせたいクラス変数(static)
    • 例:生成したオブジェクトの総数、アプリ全体で使う定数値など。
    • インスタンスに関係なく、クラスに1つだけ存在するデータ。

このように役割がはっきりしているので、「どの範囲で値を使う必要があるか?」が最優先の判断基準となります。

public class Counter {

    // クラス変数:全インスタンスで共通のカウント
    static int totalCount = 0;

    // インスタンス変数:個別のインスタンスの名前
    String name;

    // コンストラクタ
    public Counter(String name) {
        this.name = name;
    }

    public void countUp(int times) {
        // ローカル変数:このメソッドの中だけで使う
        int localCount = 0;

        for (int i = 0; i < times; i++) {
            localCount++;
            totalCount++;
        }

        System.out.println(name + " counted " + localCount + " times.");
    }

    public static void main(String[] args) {
        Counter a = new Counter("A");
        Counter b = new Counter("B");

        a.countUp(3);  // A counted 3 times.
        b.countUp(2);  // B counted 2 times.

        System.out.println("Total count: " + Counter.totalCount);  // 5
    }
}
変数の種類用途↑のコードでの例
ローカル変数一時的に使う値、メソッドの中だけ有効localCount
インスタンス変数オブジェクトごとの情報を持たせるname
クラス変数全体で共有したい値(回数や設定など)totalCountstatic付き)

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