Javaを学ぶ上で避けて通れないのが、static
キーワードです。結論から言うと、static
はクラス全体で共有される変数やメソッドを定義するために使われるキーワードで、このキーワードの使い方を学習することでクラスやインスタンスの動作原理/Javaにおけるメモリ管理の仕組みまで理解が進むようになります。
この記事では、Java初心者でも理解できるように、static
の使い方とその背後にある動作原理をステップバイステップで解説します。
関連 Javaの1stステップ:基本的な構文ルールを1分で復習!
Java:staticとは何か?
static
は、クラスに属するメンバーを定義するためのキーワードです。(※メンバーとはクラスの中に定義される要素のこと。)
メンバーの種類 | 説明 | 例 |
---|---|---|
フィールド | クラスやインスタンスのデータを保持する変数 | String name; (インスタンス用)static int count; (クラス全体で共有) |
メソッド | クラスやインスタンスで使われる処理 | void run() (インスタンス用)static void show() (クラス名で呼び出し) |
通常、オブジェクト指向言語では、オブジェクト(インスタンス)ごとに変数やメソッドが存在します。しかし、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
変数はメーカー全体で共有する「生産台数」のようなもので、メーカー全体/メーカー共通で保持する変数になります。
宣言方法
public class Car { static String brand = "トヨタ"; // static変数の宣言 String color; // インスタンス変数の宣言 public Car(String color) { this.color = color; } }
上記ではstatic
キーワードを使って、brand
というクラス全体で共有される変数を宣言しています。「色」は車個別ですが、ブランド名はそれらすべての車に共通するプロパティのため、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メソッドの使い方
static
メソッドは、インスタンスを生成しなくてもクラス名で直接呼び出せるメソッド(クラス全体で定義されているメソッドなので)です。
例え話:自動車メーカーが提供する「ブランド紹介ページ」のようなものです。特定の車(インスタンス)を持っていなくても、誰でもメーカー(クラス)のブランド情報(static
メソッド)を見ることができま
宣言方法
public class Car { static String brand = "トヨタ"; static void showBrand() { // staticメソッドの宣言 System.out.println("この車のブランドは " + brand + " です。"); } }
アクセス方法
- クラス名で直接呼び出し
public class Car { static String brand = "トヨタ"; static void showBrand() { // staticメソッドの宣言 System.out.println("この車のブランドは " + brand + " です。"); } } Car.showBrand(); // 出力: この車のブランドは トヨタ です。
staticブロックとは?その役割
static
ブロックは、クラスがロードされるときに一度だけ実行されるコードブロックです。初期設定や複雑な初期化処理が必要な場合に使用します。
※コードブロックとは、複数の処理を{ }で囲んだ部分のことです。Javaでは、ブロック内に書かれたコードが順に実行されます。
宣言方法
public class Car { static String brand; static { // staticブロック内での初期化 brand = getBrandFromConfig(); } static String getBrandFromConfig() { // ここでは固定値を返します return "トヨタ"; } }
staticブロックのポイント
- クラスが初めてロードされたときに一度だけ実行されます(
new
でインスタンスを作らなくても実行されます)。 - 複数のstaticブロックがある場合は、上から順に実行されます。
- static変数の初期化や設定ファイルの読み込みなどに使われます。
システム観点から見るstaticの動作原理
メモリとクラスローダー
メモリ領域の理解
- メソッドエリア:
static
変数やメソッド、クラスの情報が保存される領域。 - ヒープ領域:インスタンス(オブジェクト)が保存される領域。
クラスローダーの役割
- クラスローディング:JVM(Java仮想マシン)が必要なクラスをメモリに読み込むプロセス。
- ロードのタイミング:クラスが初めて使用されるとき。
staticの動作フロー
- クラスのロード:JVMが
Car
クラスをメモリに読み込みます。 - staticブロックの実行:クラスがロードされる際に
static
ブロックが一度だけ実行されます。 - staticメンバーの利用可能:
static
変数やメソッドが使用可能になります。
例え話で理解:
- 新しい車種(クラス)を生産するために、メーカーが工場を設立するイメージです。
- クラスのロード:工場の設立。
- staticブロックの実行:工場の初期設定や機械の調整。
- 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
キーワードを使うことで、メモリの効率化、インスタンスを作らずに使える便利さ、共通データの一貫した管理が可能になる、という点がポイントです。