PR

Java:static の意味と使い方を3分でわかりやすく解説

Java

Javaを学ぶ上で避けて通れないのが、staticキーワードです。

staticクラス全体で共有される変数やメソッドを定義するために使われるキーワードで、このキーワードの使い方を学習することでクラスやインスタンスの動作原理/Javaにおけるメモリ管理の仕組みまで理解が進むようになります。

この記事では、Java初心者でも理解できるように、staticの使い方とその背後にある動作原理をステップバイステップで解説します。

スポンサーリンク

Java static修飾子の基本

staticは、クラス(反対:インスタンス)に属するメンバーを定義するためのキーワードです。(※メンバーとはクラスの中に定義される要素のこと。)

メンバーの種類説明
フィールドクラスやインスタンスのデータを保持する変数String name;(インスタンス用)
static int count;(クラス全体で共有)
メソッドクラスやインスタンスで使われる処理void run()(インスタンス用)
static void show()(クラス名で呼び出し)

通常、オブジェクト指向言語では、インスタンス(参考 クラスとインスタンス)ごとに変数やメソッドが存在します。しかし、staticを使うと、クラス全体で共有されるメンバーを作成することができます。仮にクラスが「学校」だとすると、通常の変数は「生徒1人ひとりの持ち物」、static変数は「学校全体で共有する校則」のようなものだと説明することができるかもしれません。

いずれにせよ、staticを理解するためには、まずはクラスとインスタンスの関係の理解が必須となります。逆に言えば、クラスとインスタンスの違いさえ分かっていればここまでの説明でstaticの意味や使い方はある程度理解できるはずです。もし、まだピンと来ていない方は↓のページをご覧ください。

ポイント1 通常の変数やメソッド

  • インスタンスごとに持つもの。例えば、「車」クラスのインスタンスであるカローラとプリウスはそれぞれ別々の色を持ちます。インスタンスを作るたびにメモリに割り当てられます。

ポイント2 static変数やメソッド

  • クラス全体で共有するもの。例えば、車クラス全体で共通の情報(セダン/SUV・・・)を持つときに使います。
  • クラスが最初に使われるとき(例えば、最初のインスタンスが作られるとき)に一度だけメモリに割り当てられます。その後、どのインスタンスからでも同じメモリを共有します。
Q
クラスとインスタンスの違いは?
A
  • クラス: 車の設計図のようなもの。車の特性(例えば、メーカーやモデル)や動作(例えば、走るや止まる)を定義します。
  • インスタンス: クラスに基づいて作られる実際の「もの」。車クラスから作られた具体的な車(カローラやプリウス)がインスタンスです。

static修飾子の利用シーン

staticはフィールド、メソッドに加えて、ブロックの3つで使われます。ここからは具体的な利用シーンについて1個1個サンプルコードを用いながらその動作原理を解説していきます。

利用シーン概要
フィールドクラス全体で共有するデータを持たせたいときに使います。インスタンスごとではなく、1つの変数を全体で使い回します。static int totalCars; (全ての車の数を管理)
メソッドインスタンスを作らなくても、クラスから直接使いたい処理に使います。static void printInfo() (共通情報を表示するメソッド)
ブロッククラスが初めて使われたときに一度だけ実行したい初期化処理に使います。static { System.out.println("初期化完了"); }

static変数の使い方

static変数は、クラス全体で1つだけ存在する変数です。通常のインスタンス変数は各オブジェクトが個別に持ちますが、static変数は全てのオブジェクトで共有されます。

例としてクラスを「自動車メーカー」、オブジェクトを「個々の車」と考えてみましょう。インスタンス変数は各車が持つ「カラー」や「エンジンタイプ」のようなものです。一方、static変数はメーカー全体で共有する「生産台数」のようなもので、メーカー全体/メーカー共通で保持する変数になります。

staticフィールドの宣言方法

public class Car {
    static String brand = "トヨタ"; // static変数の宣言
    String color; // インスタンス変数の宣言

    public Car(String color) {
        this.color = color;
    }
}

上記ではstaticキーワードを使って、brandというクラス全体で共有される変数を宣言しています。「色」は車個別ですが、ブランド名はそれらすべての車に共通するプロパティのため、staticキーワードの利用が適切です。

staticフィールドへのアクセス方法

  • クラス名で直接アクセス
public class Car {
    static String brand = "トヨタ"; // static変数の宣言
    String color; // インスタンス変数の宣言

    public Car(String color) {
        this.color = color;
    }
}

System.out.println(Car.brand); // 出力: トヨタ
  • インスタンスからもアクセス可能
public class Car {
    static String brand = "トヨタ"; // static変数の宣言
    String color; // インスタンス変数の宣言

    public Car(String color) {
        this.color = color;
    }
}

Car myCar = new Car("赤");
System.out.println(myCar.brand); // 出力: トヨタ

上記のコード:static変数はクラスに属するので、myCar.brandと書いても動作しますが、JVMは内部的に Car.brand に置き換えて扱います。実際、Javaの文法上はインスタンスからアクセス可能ですが、意味的には「クラスからアクセスしている」と解釈されます。

つまりこのコードの最後の行は以下のように解釈される、と理解しておきましょう。

System.out.println(Car.brand);
動作原理をイメージする

static変数は、プログラムの実行開始時にメモリ上に1つだけ確保されます。全てのインスタンスでこの1つの変数を共有するため、値の変更は全てのインスタンスに影響します。

システム的な視点

  • メモリ領域static変数はメモリの「メソッドエリア」に配置されます。この領域は、クラス情報やstaticメンバーが保存される場所です。

動作原理をサンプルコードで

public class Sample {
    // staticフィールド(クラス全体で1つだけ存在する)
    static int count = 0;

    // コンストラクタが呼ばれるたびにcountを増やす
    public Sample() {
        count++;
        System.out.println("インスタンス生成!現在のcount = " + count);
    }

    public static void main(String[] args) {
        // インスタンスを3つ作る
        Sample a = new Sample(); // → countは1になる
        Sample b = new Sample(); // → countは2になる
        Sample c = new Sample(); // → countは3になる

        // どのインスタンスから見てもcountは同じ
        System.out.println("a.count = " + a.count); // → 3
        System.out.println("b.count = " + b.count); // → 3
        System.out.println("c.count = " + c.count); // → 3

        // クラス名からもアクセスできる(推奨)
        System.out.println("Sample.count = " + Sample.count); // → 3
    }
}

/* 出力結果
インスタンス生成!現在のcount = 1  
インスタンス生成!現在のcount = 2  
インスタンス生成!現在のcount = 3  
a.count = 3  
b.count = 3  
c.count = 3  
Sample.count = 3  */

↑のコードから、staticフィールドは「インスタンスごと」ではなく「クラス全体で1つ」だけ保持されることが分かります。

staticメソッドの使い方

staticメソッドは、インスタンスを生成しなくてもクラス名で直接呼び出せるメソッド(クラス全体で定義されているメソッドなので)です。

例え話:自動車メーカーが提供する「ブランド紹介ページ」のようなものです。特定の車(インスタンス)を持っていなくても、誰でもメーカー(クラス)のブランド情報(staticメソッド)を見ることができま

staticメソッドの宣言方法

public class Car {
    static String brand = "トヨタ";

    static void showBrand() { // staticメソッドの宣言
        System.out.println("この車のブランドは " + brand + " です。");
    }
}

staticメソッドへのアクセス方法

  • クラス名で直接呼び出し
public class Car {
    static String brand = "トヨタ";

    static void showBrand() { // staticメソッドの宣言
        System.out.println("この車のブランドは " + brand + " です。");
    }
}

Car.showBrand(); // 出力: この車のブランドは トヨタ です。

staticブロックとは?その役割

staticブロックは、クラスがロードされるときに一度だけ実行されるコードブロックです。初期設定や複雑な初期化処理が必要な場合に使用します。

コードブロックとは、複数の処理を{ }で囲んだ部分のことです。Javaでは、ブロック内に書かれたコードが順に実行されます。

staticブロックの宣言方法

public class Car {
    static String brand;

    static {
        // staticブロック内での初期化
        brand = getBrandFromConfig();
    }

    static String getBrandFromConfig() {
        // ここでは固定値を返します
        return "トヨタ";
    }
}

staticブロックのポイント

  1. クラスが初めてロードされたときに一度だけ実行されます(newでインスタンスを作らなくても実行されます)。
  2. 複数のstaticブロックがある場合は、上から順に実行されます。
  3. static変数の初期化設定ファイルの読み込みなどに使われます。
利用シーン概要
変数クラス全体で共有するデータを持たせたいときに使います。インスタンスごとではなく、1つの変数を全体で使い回します。static int totalCars; (全ての車の数を管理)
メソッドインスタンスを作らなくても、クラスから直接使いたい処理に使います。static void printInfo() (共通情報を表示するメソッド)
ブロッククラスが初めて使われたときに一度だけ実行したい初期化処理に使います。static { System.out.println("初期化完了"); }

システム観点から見るstaticの動作原理

これまでの解説で基本は終了です。が、ここからはより詳しくstaticの動作原理を深堀して解説していきます。

メモリとクラスローダー

Javaではプログラム実行時に必要となったタイミングでクラスファイルを読み込んで実行していきます。これを「ロード」と呼びます。このとき、クラスファイルの中身はstaticメンバーとそれ以外とで格納されるメモリ領域に違いがあります。

クラス,ロード,static
図1:クラスのロード(staticメンバー)

メモリ領域の理解

  • static領域static変数やメソッド、クラスの情報が保存される領域。
  • ヒープ領域:インスタンス(オブジェクト)が保存される領域。

ポイントは、staticメンバーはインスタンスが保存される領域とは別の部分に格納されるということ。そして、この分離格納はロード後すぐのタイミングで行われるという点(=なので、staticメンバーはインスタンスを作らなくても利用できるようになるということです。)

クラスローダーの役割

  • クラスローディングJVM(Java仮想マシン)が必要なクラスをメモリに読み込むプロセス。
  • ロードのタイミング:クラスが初めて使用されるとき。

staticの動作フロー

  1. クラスのロード:JVMがCarクラスをメモリに読み込みます。
  2. staticブロックの実行:クラスがロードされる際にstaticブロックが一度だけ実行されます。
  3. staticメンバーの利用可能static変数やメソッドが使用可能になります。

例え話で理解

  • 新しい車種(クラス)を生産するために、メーカーが工場を設立するイメージです。
    1. クラスのロード:工場の設立。
    2. staticブロックの実行:工場の初期設定や機械の調整。
    3. staticメンバーの利用可能:車の生産開始。

staticを使うメリット

staticキーワードを使うことで、クラス全体で共有するデータやメソッドを定義することができるようになる←これが1番のメリットで、この結果メモリの効率化やコードの簡潔さが向上します。

参考 メモリとは?

メリット1:メモリの効率化

static変数やメソッドは一度だけメモリに割り当てられ、全てのインスタンスで共有されます。これにより、同じデータを何度も作らずに済み、メモリの使用量を減らすことができます。

public class Car {
    public static String type = "Vehicle"; // static変数
    public String model; // インスタンス変数

    public Car(String model) {
        this.model = model;
    }
}

public class Main {
    public static void main(String[] args) {
        Car car1 = new Car("Corolla");
        Car car2 = new Car("Prius");

        System.out.println(Car.type); // "Vehicle"
        System.out.println(car1.model); // "Corolla"
        System.out.println(car2.model); // "Prius"
    }
}

メリット2:インスタンスを作らずに使える

staticメソッドや変数は、インスタンスを作らずに直接クラス名を使ってアクセスすることができる←これも大きなメリットになります。

public class MathUtils {
    public static int add(int a, int b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        int result = MathUtils.add(5, 3);
        System.out.println(result); // 8
    }
}

MathUtils.addは、MathUtilsのインスタンスを作らずに使えます。

メリット3:共通データの管理が簡単

全てのインスタンスで共通のデータを管理する場合、staticを使うことでデータの一貫性を保ちやすくなります。例えば、あるクラス全体で共通の設定やカウンタを持たせる場合に便利です。

public class Config {
    public static String appName = "MyApp"; // static変数
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Config.appName); // "MyApp"
        
        // 別の場所で変更しても全てに反映される
        Config.appName = "NewAppName";
        System.out.println(Config.appName); // "NewAppName"
    }
}

Config.appNameはどこからでもアクセス可能で、一度変更すれば全てに反映されます。

staticキーワードを使うことで、メモリの効率化、インスタンスを作らずに使える便利さ、共通データの一貫した管理が可能になる、という点がポイントです。

まとめ staticキーワードとは?

staticキーワードは、クラス全体で共有される変数やメソッドを定義するために機能です。このページでは車の例えを用いて、その動作原理をイメージしやすく説明してきました。

  • static変数:全ての車(インスタンス)で共有されるメーカーの情報(例:ブランド名)。
  • staticメソッド:特定の車を必要とせずに利用できる機能(例:ブランド紹介ページ)。
  • staticブロック:工場の初期設定を行う一度だけの処理。

システム的な理解

  • staticメンバーはメモリの特定の領域に保存され、プログラムの開始時にロードされます。
  • クラスローダーがクラスをロードするときにstaticブロックが実行され、static変数やメソッドが利用可能になります。
タイトルとURLをコピーしました