Javaのstaticメソッドについて調べてみた

## はじめに


新卒サーバエンジニアのRenです。
研修でJavaを学習している際、同じMainクラス内のメソッドをmainメソッドから呼び出そうとしたところ、コンパイルエラーが発生しました。
理由は呼び出し先のメソッドにstaticをつけていなかったこと。
この機会にstaticについて調べてみたので共有します。

---


> **TL;DR**  
> - `main` は「まだオブジェクトが無い状態」で呼ばれるため **static** 
> - static コンテキストでは `this` が存在しない → 非-static メソッドは直呼びできずエラー  
> - 解決策は「① 呼び出し先も static 」「② オブジェクトを生成して呼ぶ」の 2 つ 
> - static は便利だが乱用は設計とテストを壊す。**必要条件を満たすときだけ使う**

---

### 目次
1. [症状の再現コードとエラーメッセージ](#症状の再現コードとエラーメッセージ)  
2. [static コンテキストとは？](#static-コンテキストとは)  
3. [解決策](#解決策)  
4. [そもそも static とは何者か](#そもそも-static-とは何者か)  
5. [static のメリット／デメリット](#static-のメリットデメリット)  
6. [実践での static の使いどころ 5 選](#実践での-static-の使いどころ-5-選)  
7. [まとめ ― “必要なときだけ static” のススメ](#まとめ--必要なときだけ-static-のススメ)  

---

## 症状の再現コードとエラーメッセージ

```java
public class Main {
    public static void main(String[] args) {
        hello();              // ← コンパイルエラー！
    }

    void hello() {
        System.out.println("こんにちは");
    }
}
```

```text
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 はプログラム起動時に  

1. クラスをロード  
2. **オブジェクトを作らずに** `Main.main` を呼び出す  

という手順を踏みます。  
つまり **`main` が実行される瞬間 `this` は存在しない** → 非-static メソッドを解決できずエラーになります。

---

## 解決策

### ① 呼び出し先も static にする

```java
public class Main {
    public static void main(String[] args) {
        hello();           // OK
    }

    static void hello() {  // ← クラスメソッド化
        System.out.println("こんにちは");
    }
}
```

### ② インスタンスを生成して呼ぶ

```java
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” のススメ

1. **`main` は static コンテキスト** → `this` 不在 → 非-static を直呼びするとコンパイルエラー。  
2. 解決策は  
   - 呼び出し先を static にする  
   - インスタンスを生成して呼ぶ  
3. static は **クラス単位で共有したい振る舞い・データ** に限定して使う。  
4. **乱用すると** 密結合・テスト困難・スレッド競合など副作用が噴出するため要注意。  


> 「そのメソッドはオブジェクト固有の状態に触れるか？」  
> - **Yes** → インスタンスメソッド  
> - **No**  → static メソッド  

この記事が「main と static のモヤモヤ」を晴らす一助になれば幸いです。