【2026年7月最新】Javaの「==」とequalsの違い完全ガイド|比較の落とし穴とClaude Code活用術
この記事の内容
「Javaでif(a == b)と書いたのに、なぜかtrueにならない」——Javaを学び始めた人が必ず一度はハマる罠が、この「==」と「equals」の混同問題です。
結論から言うと、「==」はメモリ上のアドレスを比較し、「equals」は値の内容を比較します。この違いを正確に理解していないと、文字列比較で意図しないバグが発生し続けます。特にStringクラスを扱う際は、ほぼ100%「equals」を使うべき場面で「==」を使ってしまうケースが多発します。
この記事では、「==」とequalsの違いをメモリの仕組みレベルから図解し、nullPointerException対策・Objectクラスのオーバーライド方法・実務で使える比較演算子一覧まで網羅します。さらに後半では、Claude Codeを使ってJava比較バグを即座に発見・修正する方法も解説します。
この記事を読むと、以下が明確になります。
01 FUNDAMENTAL DIFFERENCE 「==」と「equals」の根本的な違い|メモリの仕組みから理解する なぜ同じ文字列なのに == でtrueにならないのか
Javaで「==」と「equals」が混同される最大の原因は、「同じ文字に見えるのに、なぜ比較結果が違うのか」が直感的に分かりにくいからです。この疑問を解消するには、Javaがメモリをどう扱うかを理解する必要があります。
📚 用語解説
参照型(Reference Type):Javaでは、String・配列・自作クラスなどのオブジェクトは「参照型」として扱われます。変数に格納されるのはオブジェクトの「データそのもの」ではなく、「メモリ上のアドレス(参照)」です。例えば「String a = new String("hello")」は、"hello"というデータがメモリのどこかに置かれ、変数aはそのアドレスを持っています。
1-1. 「==」はアドレスを比較する
「==」演算子は、左辺と右辺が「同じメモリアドレス」を指しているかどうかを比較します。プリミティブ型(int・boolean等)の場合は値そのものを格納しているため「==」でも正しく動きます。しかし参照型(StringやInteger等)の場合は、アドレスを比較するため、「中身が同じ文字列でも別々に作ったオブジェクトはtrueにならない」という問題が起きます。
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b); // false ← 異なるアドレス
System.out.println(a.equals(b)); // true ← 内容が同じ
上記コードで、aとbはどちらも"hello"という内容を持っていますが、new String()で別々に作られた別オブジェクトです。「==」はアドレスを比較するため、異なるアドレスを持つaとbは「false」と判定されます。一方、equalsは内容を比較するため「true」が返ります。
1-2. 文字列リテラルとString Poolの例外に注意
「==」と「equals」の違いを説明する上で、文字列リテラル(""で直接書く方法)には特殊な動作がある点も重要です。
String a = "hello"; // リテラル String b = "hello"; // リテラル System.out.println(a == b); // true ← String Poolのため同じアドレス
Javaには「String Pool」という仕組みがあります。文字列リテラル(""で囲んだもの)は、同じ内容なら同じメモリ領域(String Pool)を再利用します。そのため、この例では「a == b」でもtrueになります。
リテラルでは「a == b」がtrueになる場合がありますが、これに頼るのは絶対NGです。「new String()」で生成したり、メソッドの返り値として受け取ったStringは別のアドレスになるため、== で比較するとfalseになります。Stringの比較は「常にequals」を使うのが鉄則です。
📚 用語解説
String Pool(文字列プール):Javaが文字列リテラルを効率的に管理するためのメモリ領域。同じ内容の文字列リテラルは1つのオブジェクトとして共有されます。intern()メソッドを使えば任意の文字列をString Poolに追加できますが、業務では通常意識する必要はありません。
02 STRING COMPARISON equalsメソッドで文字列を比較する正しい方法 基本形・大文字小文字無視・部分一致の3パターン
Javaで文字列を比較する際のequalsメソッドの使い方を、実務でよく使う3つのパターンに分けて解説します。
2-1. 基本形:equals()で完全一致を確認する
String name = "山崎";
String input = "山崎";
if (name.equals(input)) {
System.out.println("名前が一致しました");
} else {
System.out.println("名前が一致しません");
}
// 出力: 名前が一致しました
equals()は完全一致を確認するメソッドです。大文字・小文字・スペースの有無を含む全ての文字が一致する場合のみtrueを返します。最もシンプルで一番使う比較方法です。
2-2. equalsIgnoreCase():大文字小文字を無視した比較
String a = "Hello"; String b = "hello"; System.out.println(a.equals(b)); // false ← 大文字小文字を区別 System.out.println(a.equalsIgnoreCase(b)); // true ← 大文字小文字を無視
ユーザーが入力するメールアドレスや英語の検索キーワードなど、大文字小文字を同一視したい場面では「equalsIgnoreCase()」を使います。例えばログイン認証でメールアドレスを比較するケースでは、「USER@EXAMPLE.COM」と「user@example.com」を同一として扱う必要があります。
Webアプリケーション開発では、フォームからユーザーが入力する値の比較でequalsIgnoreCaseが頻繁に登場します。特に「英語のカテゴリ名」「ユーザーIDの検索」など、表記揺れを許容したい場面で積極的に使いましょう。
2-3. contains()・startsWith()・endsWith():部分一致の比較
String email = "user@example.com";
System.out.println(email.contains("example")); // true ← 部分一致
System.out.println(email.startsWith("user")); // true ← 先頭一致
System.out.println(email.endsWith(".com")); // true ← 末尾一致
System.out.println(email.startsWith("admin")); // false
equalsは完全一致ですが、部分的な一致を確認したい場面ではcontains()・startsWith()・endsWith()を使います。いずれもStringクラスのメソッドで、equalsと同じく「参照ではなく内容」を比較します。
| メソッド | 用途 | 例 |
|---|---|---|
| equals(str) | 完全一致確認 | "hello".equals("hello") → true |
| equalsIgnoreCase(str) | 大文字小文字無視の完全一致 | "HELLO".equalsIgnoreCase("hello") → true |
| contains(str) | 指定文字列が含まれるか | "hello world".contains("world") → true |
| startsWith(prefix) | 先頭文字列の確認 | "http://".startsWith("http") → true |
| endsWith(suffix) | 末尾文字列の確認 | "image.png".endsWith(".png") → true |
| matches(regex) | 正規表現でのパターン一致 | "12345".matches("[0-9]+") → true |
03 NUMERIC COMPARISON 数値型(int・Integer)の比較:== が使える場合・使えない場合 プリミティブ型と参照型の違いを正確に理解する
数値の比較でも「==」とequalsの使い分けは重要です。特にプリミティブ型(int)とラッパークラス(Integer)の違いを理解していないと、数値比較でもバグが生じます。
📚 用語解説
プリミティブ型とラッパークラス:Javaには「int・long・double・boolean」などのプリミティブ型と、それをオブジェクトとして扱う「Integer・Long・Double・Boolean」などのラッパークラスがあります。プリミティブ型は値そのものを格納しますが、ラッパークラスは参照型のため、Stringと同じく「==」では参照が比較されます。
3-1. int(プリミティブ型)は「==」でOK
int a = 100; int b = 100; System.out.println(a == b); // true ← プリミティブ型は値を直接比較 System.out.println(a == 100); // true
「int」などのプリミティブ型は変数に値そのものが格納されるため、「==」で正しく値比較ができます。これはプリミティブ型に限った特例で、参照型(Integer・String等)では同じことはできません。
3-2. Integer(ラッパークラス)の「==」はキャッシュ範囲に注意
Integer a = 127; Integer b = 127; System.out.println(a == b); // true ← キャッシュ範囲内 Integer c = 128; Integer d = 128; System.out.println(c == d); // false ← キャッシュ範囲外!
Integerラッパークラスには-128〜127の値をキャッシュする仕組みがあります。この範囲内の値は同じオブジェクトを再利用するため、「==」でもtrueになります。しかし128以上になると別オブジェクトが作られるため、「==」でfalseになります。この挙動は非常に見つかりにくいバグの原因になります。
キャッシュ範囲(-128〜127)内では「==」が動いてしまうため、テスト時には問題ないように見えて、本番で128以上の値を扱った途端にバグが発覚するケースがあります。Integerの比較は必ずequals()を使ってください。プリミティブ型のintに変換してから「==」で比較する方法も安全です。
// 安全な比較方法 Integer a = 200; Integer b = 200; // 方法1: equalsを使う(推奨) System.out.println(a.equals(b)); // true // 方法2: intValue()でプリミティブに変換してから== System.out.println(a.intValue() == b.intValue()); // true // 方法3: Objects.equals(null安全) System.out.println(java.util.Objects.equals(a, b)); // true
04 NULL SAFETY nullPointerExceptionを防ぐequals比較のベストプラクティス 呼び出す順番とObjects.equalsで完全対策
equalsメソッドを使う際の最大の落とし穴は「NullPointerException」です。equals()を呼び出すオブジェクトがnullだと、実行時に例外が発生してプログラムが止まります。
📚 用語解説
NullPointerException(NPE):nullを参照しているオブジェクトに対してメソッドを呼び出したときに発生するJavaの実行時例外。Javaプログラムのバグの中で最も多い原因の1つで、「null.equals(...)」のようにnullオブジェクトからメソッドを呼ぼうとすると発生します。
4-1. NPEが発生するパターン
String input = null; // ユーザー入力がnullの場合
// 危険な書き方
if (input.equals("admin")) { // NullPointerException発生!
System.out.println("管理者です");
}
「input」がnullの場合、「input.equals()」を呼び出すと即座にNullPointerExceptionが発生します。特にWebアプリでユーザーの入力値・DBからの取得値・外部APIの返り値など、nullになり得る値を扱う場面では必ず対策が必要です。
4-2. 対策①:リテラル・定数を左辺に置く
String input = null;
// 安全な書き方①: リテラルを左辺に
if ("admin".equals(input)) { // NullPointerExceptionが発生しない
System.out.println("管理者です");
}
// inputがnullの場合、false が返る(例外なし)
最もシンプルな対策は、「定数やリテラルを左辺(メソッド呼び出し側)に置く」ことです。nullでない方の値からequals()を呼び出すことで、右辺がnullでも例外が発生しません。比較対象が固定の文字列や定数の場合は、この書き方が最もシンプルです。
4-3. 対策②:null判定を先に行う
String input = getUserInput(); // nullになりうる
// 安全な書き方②: null先チェック
if (input != null && input.equals("admin")) {
System.out.println("管理者です");
}
// または三項演算子で
boolean isAdmin = input != null && input.equals("admin");
変数の値が動的に変わる場合や、右辺も変数の場合は「&&の前でnull確認をする」パターンが使われます。「&&」(AND演算子)はショートサーキット評価(左辺がfalseなら右辺を評価しない)のため、input != null がfalseなら input.equals() は実行されません。
4-4. 対策③:Objects.equalsでnull安全に比較する(最も推奨)
import java.util.Objects;
String a = null;
String b = "hello";
// Objects.equals は両辺ともnullでも安全
System.out.println(Objects.equals(a, b)); // false(例外なし)
System.out.println(Objects.equals(null, null)); // true
System.out.println(Objects.equals("hello", "hello")); // true
Java 7以降で使える「Objects.equals()」が最も推奨される方法です。両辺のどちらがnullでも例外が発生せず、「Objects.equals(a, b)」は「a == null ? b == null : a.equals(b)」と同等の動作をします。null安全を意識したモダンなJavaコードではObjects.equalsが標準になっています。
| 対策パターン | コード例 | おすすめ度 | 適した場面 |
|---|---|---|---|
| リテラルを左辺に | "admin".equals(input) | ◎ | 比較対象が固定文字列・定数の場合 |
| null判定先行 | input != null && input.equals("admin") | ○ | 右辺が変数で動的な場合 |
| Objects.equals | Objects.equals(a, b) | ◎◎ | どちらもnullになりうる・モダンな書き方 |
Objects.equalsはJava 7以降で使えます。現在ほとんどの企業環境ではJava 11・17・21が標準になっているため、新規開発ではObjects.equalsを積極的に採用することをお勧めします。古いシステムでJava 6以前の環境なら、リテラルを左辺に置く方法が最もシンプルです。
05 OVERRIDE EQUALS Objectクラスのequalsをオーバーライドする方法 自作クラスでの比較を正確に実装するための必須スキル
StringやIntegerはJavaが最初からequalsメソッドを正しく実装していますが、自分で作ったクラス(自作クラス)はデフォルトでは参照比較になります。自作クラスのオブジェクト同士を「中身で比較」したい場合は、equalsメソッドをオーバーライドする必要があります。
📚 用語解説
オーバーライド(Override):親クラス(スーパークラス)で定義されているメソッドを、子クラスで再定義すること。JavaのすべてのクラスはObjectクラスを暗黙的に継承しており、ObjectクラスのequalsはデフォルトでReferenceを比較します。これをオーバーライドして「値の比較」に変更します。
5-1. デフォルトのequalsが参照比較になる問題
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
}
User u1 = new User("山崎", 30);
User u2 = new User("山崎", 30);
System.out.println(u1.equals(u2)); // false ← デフォルトは参照比較
Userクラスにequalsを定義していない場合、u1.equals(u2)は「u1 == u2」(参照比較)と同じです。同じ名前・年齢を持つ別インスタンスでも「false」になります。
5-2. equalsを正しくオーバーライドする
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 同一インスタンス
if (obj == null) return false; // null チェック
if (getClass() != obj.getClass()) return false; // クラス確認
User other = (User) obj;
return age == other.age &&
java.util.Objects.equals(name, other.name);
}
@Override
public int hashCode() {
return java.util.Objects.hash(name, age);
}
}
User u1 = new User("山崎", 30);
User u2 = new User("山崎", 30);
System.out.println(u1.equals(u2)); // true ← 内容で比較
equalsをオーバーライドする際は、必ず以下の4ステップのチェックを含めます。①同一インスタンス確認、②null確認、③クラス確認、④フィールドの値比較。また、equalsをオーバーライドしたら必ずhashCode()も同時にオーバーライドするのが鉄則です。
JavaのHashMap・HashSet等のコレクションは、オブジェクトの検索にhashCode()を使います。equalsのみをオーバーライドしてhashCodeを変えないと、「equals()でtrueのはずのオブジェクトが同じキーとして認識されない」バグが発生します。IDEを使えば両方を自動生成できます。
this == obj
→ return true
obj == null
→ return false
getClass()!=
→ return false
各フィールドを
equals/==で比較
06 COMPARISON METHODS 比較演算子一覧|==・equals・Objects.equals・compareTo 用途別の使い分けを一覧で把握する
ここまで「==」とequalsを中心に解説してきましたが、Javaの比較には他にも「Objects.equals」「compareTo」「instanceof」など複数の手段があります。用途別の使い分けを一覧で整理します。
| 比較方法 | 用途 | null安全 | 使用場面 |
|---|---|---|---|
| == | アドレス比較(参照型)・値比較(プリミティブ) | × | int/boolean等のプリミティブ型のみ |
| equals() | 内容比較(参照型) | ×(呼び出し側がnullならNPE) | String・Integer等の参照型 |
| Objects.equals(a,b) | null安全な内容比較 | ◎ | 両辺がnullになりうる場合 |
| equalsIgnoreCase() | 大文字小文字無視の文字列比較 | ×(呼び出し側がnullならNPE) | メールアドレス・英語検索など |
| compareTo() | 大小関係の比較(負/0/正を返す) | × | 文字列ソート・数値の大小比較 |
| instanceof | 型の確認 | ○(nullにはfalseを返す) | ダウンキャスト前の型チェック |
実務で最も重要なのは、「プリミティブ型は==、参照型はequals(もしくはObjects.equals)」というシンプルなルールを徹底することです。このルール1つを守るだけで、Javaの比較バグの90%以上を防ぐことができます。
// 実務でよく使う比較パターンまとめ
// 1. int同士の比較 → ==
int count = 10;
if (count == 0) { /* ... */ }
// 2. String比較 → equals
String status = getStatus();
if ("active".equals(status)) { /* ... */ }
// 3. null安全なString比較 → Objects.equals
String input = getUserInput();
if (Objects.equals(input, "expected")) { /* ... */ }
// 4. Integerの比較 → equals
Integer score = getScore();
if (Objects.equals(score, 100)) { /* ... */ }
// 5. 文字列ソート → compareTo
String[] names = {"山崎", "菅澤", "田中"};
Arrays.sort(names); // compareTo内部で使用
📚 用語解説
compareTo():2つのオブジェクトを比較して、「負の数(左辺が小さい)」「0(等しい)」「正の数(左辺が大きい)」を返すメソッド。ソート処理(Arrays.sortやCollections.sort)で内部的に使われます。文字列のアルファベット順・数値の大小を比較したい場合に直接使うこともあります。
07 CLAUDE CODE POWER 【独自】Claude CodeでJava比較バグを即修正する方法 コードレビューも修正も、Claude Codeなら対話形式で完結
ここまで「==」とequalsの違いを詳しく解説してきました。理解できても、実際のコードレビューや既存コードの修正では「どこで==を使っているか一気にチェックしたい」「equalsに一括変換したい」という場面が出てきます。この章ではClaude Codeを使ってJava比較バグを自動発見・修正する方法を紹介します。
7-1. Claude Codeに「== vs equalsのバグ」を探させる
Claude Codeのターミナル上で以下のような指示を投げると、コード全体を読み込んでバグを自動検出してくれます。
// Claude Codeへの指示例(コピペしてそのまま使えます) 「このJavaプロジェクトの全ファイルを確認して、 参照型(String・Integer・自作クラス)に対して 「==」を使って比較しているバグを全て見つけてください。 見つかったら、ファイル名・行番号・問題のあるコード・ 修正後のコードをリスト形式で教えてください。」
Claude Codeはプロジェクト全体を読み込み、問題のある比較パターンを一覧化してくれます。さらに「修正して」と指示すれば、全ファイルのバグを一括修正するコード変更を自律的に実行します。手作業でgrepして1つずつ修正するより、何十倍も速く完了します。
7-2. equalsオーバーライドの自動生成
自作クラスへのequals/hashCodeオーバーライド実装も、Claude Codeに任せることができます。
// Claude Codeへの指示例
「以下のUserクラスに、equalsとhashCodeを適切にオーバーライドして。
equalsは name と age フィールドを使って比較するようにして。
Objects.equalsとObjects.hashを使って、null安全な実装にすること。」
class User {
String name;
int age;
// ... コンストラクタ・ゲッターなど
}
この指示を投げると、Claude Codeは①4ステップのequals実装、②Objects.hashを使ったhashCode実装、③IDEで生成した場合と同等のコード品質で自動生成します。さらに「このequals実装のテストコードも書いて」と続けると、JUnit5のテストケースまで自動生成してくれます。
既存Javaファイルを
貼り付け
「==のバグを探して」
と指示
修正コードを
自動生成
JUnitテストも
自動生成
7-3. Claude Codeでできる他のJava開発支援
equals/hashCodeの修正だけでなく、Claude Codeは以下のJava開発業務全般を支援できます。
Claude Codeを使った開発支援は、Proプラン(月$20≒3,000円)から利用できます。エンジニアの開発工数を大幅に削減できるため、月数時間の工数削減だけでも十分に投資対効果が出ます。
08 SUMMARY まとめ|「==」とequalsの使い分けチートシート 全パターンを1枚で把握できる早見表
この記事では、Javaの「==」とequalsの違いをメモリの仕組みから解説し、文字列・数値・自作クラスそれぞれの正しい比較方法とNullPointerException対策、Claude Codeを使った開発効率化まで網羅しました。
| 比較ケース | 使うべき方法 | 注意点 |
|---|---|---|
| int・boolean等のプリミティブ型 | == OK | 参照型には使わない |
| String同士の比較(nullなし) | equals() | リテラルを左辺に置くと安全 |
| String同士の比較(nullあり) | Objects.equals() | Java 7以降推奨 |
| 大文字小文字を無視した文字列比較 | equalsIgnoreCase() | nullに注意 |
| Integer(ラッパークラス)の比較 | equals() | == は絶対NG(128以上でバグ) |
| 自作クラスの比較 | equalsをオーバーライド | hashCodeも必ずセットで |
| null安全・モダンな書き方 | Objects.equals() | 全ケースに使える万能手段 |
Java開発の工数をClaude Codeで半減させる方法
比較バグの自動検出・修正からテストコード生成・コードレビューまで、
Claude Codeを使ったJava開発効率化の無料相談を承ります。
NEXT STEP
この記事の内容を、あなたのビジネスで
実践してみませんか?
AI活用を自社で回せるようになりたい方へ
AI鬼管理
Claude Code・Cowork導入支援から業務設計・社内浸透まで実践ベースで伴走。「自社で回せる組織」を90日で作る経営者向けトレーニング。
よくある質問
Q. JavaのStringで「==」と「equals」、どちらを使えばいいですか?
A. String(文字列)の比較は必ずequals()またはObjects.equals()を使ってください。「==」はメモリアドレスを比較するため、同じ内容の文字列でも別々に作ったオブジェクトはfalseになります。「"hello" == "hello"」がtrueになるケースがありますが、これはString Poolの特殊動作に依存した偶然であり、信頼できません。
Q. Integerのラッパークラスをequalでなく==で比較したらどうなりますか?
A. -128〜127の範囲内では「==」でもtrueになることがありますが、128以上の値では必ずfalseになります。Integerは参照型のため、同じ値でも「new Integer(200) == new Integer(200)」はfalseです。Integerの比較はObjects.equals()を使うことを強く推奨します。
Q. equalsをオーバーライドしないとどうなりますか?
A. ObjectクラスのデフォルトのequalsはReferenceを比較(== と同等)します。自作クラスでequalsをオーバーライドしないと、「内容が同じオブジェクト同士でもequals()==falseになる」問題が発生します。特にHashMapやHashSetに自作クラスのオブジェクトをキーとして使う場合は必ずオーバーライドが必要です。
Q. Objects.equalsとequals()の違いは何ですか?
A. Objects.equals(a, b)はa.equals(b)のnull安全バージョンです。aがnullの場合でもNullPointerExceptionを投げず、「bもnullならtrue、そうでなければfalse」を返します。Java 7以降で使えるため、新規コードではObjects.equalsを積極的に採用することを推奨します。
Q. Claude CodeでJavaのバグを見つけてもらうにはどうすればいいですか?
A. Claude Code(Pro $20/月以上)をインストールして、プロジェクトフォルダで「このJavaプロジェクトの文字列比較のバグを全て見つけて、修正コードも提案して」と指示するだけです。プロジェクト全体を読み込んでバグを一覧化し、修正コードを提案・実行まで自律的に行います。コードレビューを毎回AI任せにすることで、ヒューマンエラーが大幅に削減されます。
Q. equalsとhashCodeを必ずセットでオーバーライドしなければならない理由は?
A. JavaのHashMap・HashSetはオブジェクトの検索にまずhashCode()を使い、一致するhashCodeのオブジェクト同士をequals()で比較します。equalsのみをオーバーライドしてhashCodeを変えないと、「equals()でtrueのはずなのにHashMapで別のキーとして扱われる」バグが発生します。Javaの仕様として「equals()がtrueなら同じhashCodeを返す」という契約があるためです。
Q. Javaの比較でcompareTo()はどういう場面で使いますか?
A. compareTo()はオブジェクトの大小関係を比較し、「負の数(左が小さい)」「0(等しい)」「正の数(左が大きい)」を返します。Arrays.sort()やCollections.sort()内部で使われるほか、文字列のアルファベット順比較や数値の大小チェックに直接使います。equalsが「等しいか否か」を確認するのに対し、compareToは「どちらが大きいか」を確認する場面で使います。
Claude Codeで業務自動化を90日で叩き込む
経営者向けの伴走型パーソナルトレーニング
Claude Code を業務に落とし込む
専門研修コース一覧
受講者本人の業務を題材に、「使いこなせる」状態になるまで伴走する研修プログラム。1対1特化型・ハンズオン・法人講座の3コースを展開中。業務特化・実装まで踏み込むタイプのClaude Code研修です。
研修コース一覧を見る →AI鬼管理へのお問い合わせ
この記事を読んで気になった方へ。
AI鬼管理の専門スタッフが、御社に最適な
業務自動化プランを無料でご提案します。




