はじめに
Javaを書いていると、static というキーワードをよく目にします。
public static void main(String[] args) { ... }
private static int count = 0;
public static final String NAME = "Java";
この記事では static の意味をインスタンスの概念と合わせて、初心者にもわかりやすく解説します。
インスタンスとクラスの関係をおさらい
static を理解するには、まずクラスとインスタンスの違いを押さえておく必要があります。
// クラス = 設計図
public class Car {
String color; // 各車の色(インスタンスごとに違う)
}
// インスタンス = 設計図から作った実体
Car car1 = new Car(); // 赤い車
Car car2 = new Car(); // 青い車
クラスは設計図、インスタンスはその設計図から作られた実体です。new を使うたびに新しいインスタンスが生まれます。
インスタンス変数とは?
インスタンスごとに独立して持つ変数です。
public class Car {
String color; // インスタンス変数
int speed; // インスタンス変数
}
Car car1 = new Car();
car1.color = "赤";
car1.speed = 100;
Car car2 = new Car();
car2.color = "青";
car2.speed = 80;
System.out.println(car1.color); // 赤
System.out.println(car2.color); // 青(car1とは独立している)
car1 ──→ [ color="赤", speed=100 ]
car2 ──→ [ color="青", speed=80 ]
car1 と car2 はそれぞれ自分の color と speed を持っています。一方を変えても他方には影響しません。
クラス変数(staticフィールド)とは?
static をつけたフィールドはクラス変数と呼ばれ、すべてのインスタンスで共有される変数です。
public class Car {
static int totalCount = 0; // クラス変数(全インスタンスで共有)
String color; // インスタンス変数
public Car(String color) {
this.color = color;
totalCount++; // 車が1台作られるたびにカウントアップ
}
}
Car car1 = new Car("赤");
Car car2 = new Car("青");
Car car3 = new Car("白");
System.out.println(Car.totalCount); // 3
Car クラス ──→ [ totalCount=3 ](全インスタンスで共有)
car1 ──→ [ color="赤" ]
car2 ──→ [ color="青" ]
car3 ──→ [ color="白" ]
クラス変数はインスタンスではなくクラス自体に紐づくため、Car.totalCount のようにクラス名でアクセスします。
インスタンスメソッドとstaticメソッドの違い
インスタンスメソッド
インスタンスを生成してから呼び出すメソッドです。インスタンスの状態(フィールド)を参照・変更できます。
public class Car {
String color;
// インスタンスメソッド
public void printColor() {
System.out.println("この車の色は " + color + " です");
}
}
Car car1 = new Car();
car1.color = "赤";
car1.printColor(); // この車の色は 赤 です
staticメソッド
インスタンスを生成しなくても呼び出せるメソッドです。クラス名から直接呼び出します。
public class MathUtils {
// staticメソッド
public static int add(int a, int b) {
return a + b;
}
}
// インスタンス化不要
int result = MathUtils.add(3, 5);
System.out.println(result); // 8
Math.abs() や Collections.sort() など、Javaの標準ライブラリでよく使うメソッドはstaticメソッドが多いです。
staticメソッドの制約
staticメソッドには重要な制約があります。インスタンス変数やインスタンスメソッドに直接アクセスできません。
public class Car {
String color; // インスタンス変数
static int count; // クラス変数
public static void showInfo() {
System.out.println(count); // ✅ クラス変数はOK
System.out.println(color); // ❌ コンパイルエラー!
}
}
staticメソッドはインスタンスが存在しない状態でも呼べるため、「どのインスタンスの color か」が特定できません。そのためインスタンス変数にはアクセスできないのです。
staticメソッドが呼ばれるとき
→ インスタンスが存在しない可能性がある
→ 「どのインスタンスの color ?」が決まらない
→ コンパイルエラー
static定数(static final)
static final を組み合わせると、変更不可のクラス定数を定義できます。
public class AppConfig {
public static final String APP_NAME = "MyApp";
public static final int MAX_RETRY = 3;
}
System.out.println(AppConfig.APP_NAME); // MyApp
System.out.println(AppConfig.MAX_RETRY); // 3
定数名はすべて大文字・アンダースコア区切り(スネークケース)が慣習です。
staticブロック
クラスが最初にロードされたときに一度だけ実行される初期化処理を書けます。
public class DatabaseConfig {
static String url;
static {
// クラスが読み込まれたときに一度だけ実行
url = System.getenv("DB_URL");
System.out.println("DB設定を読み込みました");
}
}
まとめ比較表
| 種類 | キーワード | 所属 | アクセス方法 | 用途 |
|---|---|---|---|---|
| インスタンス変数 | なし | インスタンス | インスタンス.変数名 |
各インスタンス固有の状態 |
| クラス変数 | static |
クラス | クラス名.変数名 |
全インスタンスで共有する状態 |
| インスタンスメソッド | なし | インスタンス | インスタンス.メソッド名() |
インスタンスの状態を操作 |
| staticメソッド | static |
クラス | クラス名.メソッド名() |
インスタンス不要の処理・ユーティリティ |
| 定数 | static final |
クラス | クラス名.定数名 |
変更不可の共有値 |
よくあるアンチパターン
なんでも static にしない
「インスタンス化が面倒だから」という理由でなんでも static にするのは避けましょう。static を多用するとオブジェクト指向の設計が崩れ、テストもしにくくなります。
// ❌ インスタンスの状態を持つのにstaticにしている
public class UserService {
private static String userName; // 全ユーザーで共有されてしまう!
public static void setName(String name) {
userName = name;
}
}
// ✅ インスタンス変数として持つ
public class UserService {
private String userName; // インスタンスごとに独立
public void setName(String name) {
this.userName = name;
}
}
まとめ
- インスタンス変数:インスタンスごとに独立した値を持つ
- クラス変数(static):全インスタンスで共有する値を持つ
- インスタンスメソッド:インスタンスの状態にアクセスできる
- staticメソッド:インスタンス不要で呼べるが、インスタンス変数にはアクセスできない
- static final:変更不可のクラス定数
static の本質は「インスタンスではなくクラス自体に紐づく」という一言に尽きます。使いどころを正しく理解することで、設計の質も上がります。