はじめに
新卒サーバエンジニアのRenです。
研修でJavaを学習している際、同じMainクラス内のメソッドをmainメソッドから呼び出そうとしたところ、コンパイルエラーが発生しました。
理由は呼び出し先のメソッドにstaticをつけていなかったこと。
この機会にstaticについて調べてみたので共有します。
TL;DR
mainは「まだオブジェクトが無い状態」で呼ばれるため static- static コンテキストでは
thisが存在しない → 非-static メソッドは直呼びできずエラー- 解決策は「① 呼び出し先も static 」「② オブジェクトを生成して呼ぶ」の 2 つ
- static は便利だが乱用は設計とテストを壊す。必要条件を満たすときだけ使う
目次
- 症状の再現コードとエラーメッセージ
- static コンテキストとは?
- 解決策
- そもそも static とは何者か
- static のメリット/デメリット
- 実践での static の使いどころ 5 選
- まとめ ― “必要なときだけ static” のススメ
症状の再現コードとエラーメッセージ
public class Main {
public static void main(String[] args) {
hello(); // ← コンパイルエラー!
}
void hello() {
System.out.println("こんにちは");
}
}
error: non-static method hello() cannot be referenced from a static context
main は呼べるのに hello は呼べない――この謎を解く鍵が static です。
static コンテキストとは?
| 視点 | static コンテキスト | 非-static コンテキスト |
|---|---|---|
| 存在するもの | クラスのみ(this なし) |
this(インスタンス参照) |
| 呼び出せるメソッド | static メソッド | static + 非-static |
| 代表例 | public static void main |
toString(), equals()
|
JVM はプログラム起動時に
- クラスをロード
-
オブジェクトを作らずに
Main.mainを呼び出す
という手順を踏みます。
つまり main が実行される瞬間 this は存在しない → 非-static メソッドを解決できずエラーになります。
解決策
① 呼び出し先も static にする
public class Main {
public static void main(String[] args) {
hello(); // OK
}
static void hello() { // ← クラスメソッド化
System.out.println("こんにちは");
}
}
② インスタンスを生成して呼ぶ
public class Main {
public static void main(String[] args) {
new Main().hello(); // OK
}
void hello() {
System.out.println("こんにちは");
}
}
そもそも static とは何者か
定義: インスタンス(
this)ではなく クラスそのもの に属するメンバ。
メモリとライフサイクル
┌─ クラスロード時(1回だけ) ─────────────┐
│ メタ領域 … static フィールド/メソッド │
└──────────────────────────┘
│ (プログラム実行中)
▼
┌──────── ヒープ ────────┐
│ new Main() ごとに │
│ インスタンス領域 │
└────────────────────┘
主な性質
| 項目 | static | 非-static |
|---|---|---|
| 帰属 | クラス | インスタンス |
| 個数 | 1 クラス 1 個 |
new するたび |
| 参照方法 | Main.count |
obj.count |
| オーバーライド | 不可(隠蔽のみ) | 可能(ポリモーフィズム) |
static のメリット/デメリット
| メリット | デメリット | |
|---|---|---|
| 1 | インスタンス不要 → 軽量に即呼び出し | グローバル変数化で依存が散乱 |
| 2 | 全オブジェクト間でデータ共有 | 可変データは競合条件の温床 |
| 3 |
Math.max などユーティリティ API をまとめやすい |
動的ディスパッチ不可 → テストがしにくい |
| 4 | クラス単位で一度だけ初期化 | クラスがアンロードされるまで GC 不可 |
実践での static の使いどころ 5 選
| 用途 | 例 | ポイント |
|---|---|---|
| エントリポイント | public static void main |
JVM から直接叩かれる |
| 定数 | public static final String VERSION |
final とセットで不変に |
| ユーティリティ関数 |
Math.sqrt, Collections.sort
|
状態を持たない純関数 |
| シングルトン保持 | private static final Logger LOG |
全クラスで 1 個だけ共有 |
| ファクトリメソッド |
List.of(...) (Java 9+) |
new を隠蔽し可読性向上 |
まとめ ― “必要なときだけ static” のススメ
-
mainは static コンテキスト →this不在 → 非-static を直呼びするとコンパイルエラー。 - 解決策は
- 呼び出し先を static にする
- インスタンスを生成して呼ぶ
- static は クラス単位で共有したい振る舞い・データ に限定して使う。
- 乱用すると 密結合・テスト困難・スレッド競合など副作用が噴出するため要注意。
「そのメソッドはオブジェクト固有の状態に触れるか?」
- Yes → インスタンスメソッド
- No → static メソッド
この記事が「main と static のモヤモヤ」を晴らす一助になれば幸いです。