Java よくある面接問題完全解説

📚 Java 基礎

1. Javaの特徴とは?

Javaプログラミング言語のコア特性を理解する

  • オブジェクト指向:カプセル化、継承、ポリモーフィズムなどの機能をサポート
  • プラットフォーム独立性:Write Once, Run Anywhere (WORA)
  • 自動メモリ管理:ガベージコレクション機構が自動的にメモリを解放
  • マルチスレッドサポート:内蔵スレッド機構により、並行プログラミングが容易
  • セキュリティが強い:型チェック、例外処理などのセキュリティメカニズムを提供
  • 動的機能:実行時型チェックと動的ロード

2. JDK、JRE、JVMの違いは?

Java実行環境の3層構造を理解する

  • JVM (Java Virtual Machine):仮想マシン、バイトコード実行の抽象計算機、クロスプラットフォーム機能を実現
  • JRE (Java Runtime Environment):実行環境、JVMと実行に必要なクラスライブラリを含む
  • JDK (Java Development Kit):開発ツールキット、JRE + コンパイラ + デバッグツールなど開発ツールを含む
  • 包含関係:JDK > JRE > JVM

3. バイトコードとは?Javaがクロスプラットフォームである理由は?

Javaクロスプラットフォーム機構の原理を深く理解する

  • バイトコード定義:Javaソースコードのコンパイルで生成された中間コード、ファイル拡張子は .class
  • クロスプラットフォーム原理:Javaソースコード → バイトコード → 異なるプラットフォームのJVM実行
  • 優位性:一度コンパイルすれば、どこでも実行でき、開発者は異なるプラットフォーム向けに再度コードを書く必要がない
  • 実行フロー:javacコンパイラが .javaファイルを .classファイルにコンパイルし、各プラットフォームのJVMが解釈実行

4. Javaの基本データ型とは?

Javaの型システムをマスターする

  • 整数型:byte (1バイト), short (2バイト), int (4バイト), long (8バイト)
  • 浮動小数点型:float (4バイト), double (8バイト)
  • 文字型:char (2バイト、Unicodeサポート)
  • 論理型:boolean (1バイト、true/false)
  • デフォルト値:整数は0、浮動小数点は0.0、論理型はfalse、charは'\\u0000'
  • ラッパークラス:Integer、Long、Float、Double、Booleanなど対応する参照型

5. final、finally、finalizeの違いは?

似ているが完全に異なる3つの概念を区別する

  • final (キーワード):クラス修飾時は継承不可、メソッド修飾時はオーバーライド不可、変数修飾時は再代入不可
  • finally (キーワード):try-catch-finallyのコードブロック、例外発生の有無にかかわらず実行される。リソース解放に使用
  • finalize (メソッド):Objectクラスのメソッド、オブジェクトがガベージコレクション前に呼び出される。リソースクリーンアップ用(既に廃止)
  • 応用シーン:finalは不変性制御に使用、finallyは例外安全に使用、finalizeはtry-with-resourcesで置き換え

🎯 オブジェクト指向プログラミング

6. オブジェクト指向とは?4つの特性とは?

OOPのコアコンセプトを理解する

  • オブジェクト指向定義:オブジェクトを単位とするプログラミング思想。オブジェクトの属性と行動を強調
  • 4つの特性:
  • 抽象:事物の共通特性を抽出し、本質的でない詳細を無視
  • カプセル化:内部実装の詳細を隠し、必要なインターフェースのみ公開。セキュリティを向上
  • 継承:子クラスが親クラスの属性とメソッドを継承。コードの再利用を実現
  • ポリモーフィズム:同じインターフェースの異なる実装。実行時に動的にバインド

7. ポリモーフィズムとは?実装方法とは?

Javaのポリモーフィズム機構を深く理解する

  • ポリモーフィズム定義:一つのオブジェクトが複数の形態を持ち、同じメソッド呼び出しが異なるオブジェクトで異なる表現
  • 実装方式:
  • コンパイル時ポリモーフィズム(オーバーロード):同じ名前のメソッド、パラメータが異なり、コンパイル時に決定
  • 実行時ポリモーフィズム(オーバーライド):親クラスの参照が子クラスのオブジェクトを指す。実行時に実際に呼び出すメソッドを決定
  • 実行時ポリモーフィズム条件:継承、オーバーライド、アップキャスト
  • 優位性:コードの柔軟性と保守性を向上させ、インターフェースプログラミングをサポート

8. インターフェースと抽象クラスの違いは?

2つの抽象メカニズムを対比する

  • 抽象クラス:abstractで修飾。抽象メソッドと具体的なメソッドを持つ
  • インターフェース:interfaceで定義。デフォルトメソッドはpublic abstract(Java 8以降はデフォルト実装をサポート)
  • 継承関係:クラスは抽象クラスの単一継承のみ可能だが、複数のインターフェースを実装可能
  • アクセス修飾子:抽象クラスはprivate/protectedを使用可能。インターフェースメンバーはデフォルトでpublic
  • 変数:抽象クラスはインスタンス変数を持つ。インターフェースは静的定数のみ
  • 使用シーン:抽象クラスはコード共有用。インターフェースは仕様定義用

9. オーバーロード(Overload)とオーバーライド(Override)の違いは?

似ているが異なる2つの概念を区別する

  • オーバーロード (Overload):
  • 同一クラス内で、メソッド名は同じだが、パラメータの型/個数/順序が異なる
  • コンパイル時に決定される。静的バインディング
  • 戻り値の型は異なっていてもよい(ただし戻り値のみで区別することはできない)
  • オーバーライド (Override):
  • 子クラスが親クラスのメソッドを再実装。メソッドシグネチャは完全に同じ
  • 実行時に決定される。動的バインディング
  • 戻り値の型と例外は互換性がなければならない

10. アクセス修飾子とは?作用範囲は?

Javaのアクセス制御機構をマスターする

  • public:公開。すべてのクラスからアクセス可能
  • protected:保護。同じパッケージと子クラスからアクセス可能
  • default (修飾子なし):デフォルト。同じパッケージからアクセス可能
  • private:プライベート。クラス内部からのみアクセス可能
  • アクセス範囲対比:public > protected > default > private
  • ベストプラクティス:アクセス権限を最小化し、カプセル化原則に従う

💾 メモリ管理とガベージコレクション

11. Javaのメモリ構造とは?

JVMのメモリ配置を理解する

  • ヒープ (Heap):オブジェクトインスタンスを保存。すべてのスレッドで共有。ガベージコレクションの主要領域。サイズ設定可能
  • スタック (Stack):ローカル変数とメソッド呼び出しを保存。各スレッドが独立。自動的にメモリを解放
  • メソッド領域 (Method Area):クラスの構造情報、実行時定数プール、静的変数を保存。すべてのスレッドで共有
  • プログラムカウンタ:現在のスレッドが実行するバイトコード命令アドレスを記録
  • ネイティブメソッドスタック:ネイティブメソッド(C/C++コード)を実行

12. ガベージコレクションとは?GCアルゴリズムとは?

GC機構を深く理解する

  • ガベージコレクション定義:使用されなくなったオブジェクトが占用するメモリを自動回収
  • 主要アルゴリズム:
  • マーク削除:存活オブジェクトをマーク、ガベージを削除。フラグメントが発生
  • コピーアルゴリズム:メモリを2つに分割。清掃時に存活オブジェクトをコピー。フラグメントなしだがメモリ浪費
  • マーク整理:マーク後に圧縮整理。フラグメントなしだがパフォーマンスは悪い
  • 世代別アルゴリズム:オブジェクトを新生代と旧生代に分割。世代ごとに異なる戦略を使用
  • 優位性:自動メモリ管理。メモリリークを回避

13. ヒープとスタックの違いは?

2つの重要なメモリ領域を対比する

  • 保存内容:ヒープはオブジェクトを保存、スタックは基本型と参照を保存
  • スレッド:ヒープはすべてのスレッドで共有、各スレッドは独立したスタックを持つ
  • 管理:ヒープはガベージコレクタによって管理、スタックは自動的に解放
  • サイズ:ヒープは一般的に大きい、スタックは相対的に小さい
  • パフォーマンス:スタック割り当ては高速、ヒープ割り当ては相対的に遅い
  • 例外:ヒープオーバーフロー OutOfMemoryError、スタックオーバーフロー StackOverflowError

14. メモリリークとは?回避方法は?

一般的なメモリ問題を認識する

  • メモリリーク定義:プログラムが申請したメモリを解放できず、長期的にメモリを占有
  • 一般的な原因:
  • 長いライフサイクルのオブジェクトが短いライフサイクルのオブジェクトを参照
  • コレクション内のオブジェクトが適切にクリアされない
  • リスナーまたはコールバックが登録解除されない
  • 静的なコレクションが無制限に増加
  • 回避方法:参照をタイムリーに解放。try-with-resourcesを使用。メモリ使用を定期的にチェック

15. 強参照、弱参照、ソフト参照、ファントム参照とは?

Javaの4つの参照型を理解する

  • 強参照:通常の参照。オブジェクトは強参照がなくなるまで回収されない
  • ソフト参照 (SoftReference):メモリ不足時に回収される。キャッシュに使用
  • 弱参照 (WeakReference):次のGCで回収される。WeakHashMapに使用
  • ファントム参照 (PhantomReference):いつでも回収可能。参照キューと共に使用。オブジェクト回収の追跡に使用
  • 回収優先度:ファントム参照 > 弱参照 > ソフト参照 > 強参照

📦 コレクションフレームワーク

16. Javaコレクションフレームワークの構造は?

コレクションフレームワークの全体構想をマスターする

  • Collectionインターフェース:
  • List:順序付き可重複。ArrayList、LinkedListなど
  • Set:順序なし不重複。HashSet、TreeSetなど
  • Queue:キュー。LinkedList、PriorityQueueなど
  • Mapインターフェース:
  • キー値ペアマッピング:HashMap、TreeMap、ConcurrentHashMapなど
  • 全体階層:Iterable → Collection/Map → 具体実装クラス

17. ArrayListとLinkedListの違いは?

2つの一般的なリスト実装を対比する

  • データ構造:ArrayListは配列ベース、LinkedListは双方向リンクリストベース
  • ランダムアクセス:ArrayList O(1)、LinkedList O(n)
  • 挿入削除:ArrayList O(n)、LinkedList O(1)
  • メモリ占有:ArrayListは連続。LinkedListは分散(ポインタオーバーヘッド)
  • スレッド安全性:どちらも非同期。Collections.synchronizedList()またはCopyOnWriteArrayListを使用可能
  • 選択:頻繁なクエリはArrayList。頻繁な挿入削除はLinkedList

18. HashMapの原理とパフォーマンスは?

HashMapの動作メカニズムを深く理解する

  • データ構造:配列 + リンクリスト + 赤黒木(JDK 8以降)
  • 動作原理:hash(key) % table.length で配列インデックスを計算。衝突時はリンクまたは木化
  • ロード係数:デフォルト0.75。使用容量 ≥ 容量 × ロード係数の場合、拡張
  • 拡張メカニズム:容量を2倍にし、要素を再ハッシュして再配置
  • 時間計算量:平均 O(1)、最悪 O(n)(大量衝突時)
  • スレッド安全性:非同期。マルチスレッドではConcurrentHashMapまたはCollections.synchronizedMap()を使用

19. HashSetは重複をどう防ぐ?

Setの重複排除メカニズムを理解する

  • 基盤実装:HashMapに基づき、キーは要素、値は固定Object
  • 重複排除メカニズム:まずhashCode()を比較。次にequals()で相等性を判定
  • 追加フロー:ハッシュを計算 → 存在確認 → 存在しない場合は追加 → 存在する場合は無視
  • カスタムオブジェクト:equals()とhashCode()を必ずオーバーライドし、一貫性を保つ
  • パフォーマンス:追加、削除、検索の平均 O(1)。ハッシュ品質に依存

20. fail-fastとfail-safeとは?

コレクションのイテレータ安全メカニズムを理解する

  • fail-fast:
  • イテレーション中にコレクションを修正するとConcurrentModificationExceptionをスロー
  • modCountとexpectedModCountで検出
  • ArrayList、HashMap(非スレッド安全)など
  • fail-safe:
  • イテレーションはコレクションのスナップショットまたはコピーに基づく。元のコレクション修正はイテレーションに影響しない
  • CopyOnWriteArrayList、ConcurrentHashMapなど
  • 使用提案:イテレーション時にイテレータのremove()を使用。またはfail-safeコレクションを使用

⚠️ 例外処理

21. Java例外体系?

Java例外の分類を理解する

  • Throwable(ルートクラス):
  • Exception(例外):回復可能な例外
  • Error(エラー):仮想マシンレベルエラー、回復不可能
  • Exception分類:
  • チェック例外 (Checked):キャッチまたは宣言が必須。IOExceptionなど
  • 非チェック例外 (Unchecked):キャッチ不要。NullPointerException、IndexOutOfBoundsExceptionなど
  • 一般的な例外:NPE、ClassCastException、ArrayIndexOutOfBoundsExceptionなど

22. try-catch-finallyの実行順序は?

例外処理の実行フローをマスターする

  • 正常な場合:try → finally → 正常リターン
  • 例外の場合:try → 例外発生 → catch → finally → 例外伝播またはリターン
  • finally特性:必ず実行。catchでreturn、throw、System.exit()の場合でも
  • 例外状況:finallyのreturnはtry/catchのreturnをオーバーライド
  • リソース解放:try-with-resourcesの使用を推奨。自動的にリソースを閉じる
  • ベストプラクティス:finallyでリターン値を変更しない。例外の喪失につながる

23. throwsとthrowの違いは?

2つの例外処理キーワードを区別する

  • throw:
  • 例外インスタンスを手動で投げる。メソッド内で使用が必須
  • フォーマット:throw new Exception("message")
  • throws:
  • メソッドシグネチャで発生する可能性のある例外を宣言
  • フォーマット:public void method() throws IOException
  • 処理方法:throwはthrowsで宣言され、throwsは呼び出し元で処理
  • 応用:throwは具体的な例外処理に使用。throwsは例外伝播に使用

24. カスタム例外をどう定義する?

プロジェクト固有の例外クラスを作成する

  • 継承関係:Exception(チェック例外)またはRuntimeException(非チェック例外)を継承
  • 必要な部分:
  • 無引数コンストラクタを提供
  • messageを含むコンストラクタを提供
  • messageとcauseを含むコンストラクタを提供
  • コード例:public class CustomException extends Exception { ... }
  • ベストプラクティス:明確な命名。明確なドキュメント。適切な例外クラスを継承

25. try-with-resourcesの利点は?

リソース自動管理を理解する

  • シンタックス:try (InputStream is = ...) { ... } リソースを自動で閉じる
  • 要件:リソースはAutoCloseableインターフェースを実装する必要がある
  • 利点:
  • close()メソッドを自動的に呼び出し。手動管理不要
  • 例外が抑制された場合でも正しく処理可能
  • コードがより簡潔で、リソースリークを回避
  • 適用シーン:File、Stream、Connection、Statementなどのリソースクラス

🔤 文字列、StringBuilder、StringBuffer

26. 文字列の不変性と理由は?

Stringの設計の深い考慮を理解する

  • 不変性定義:Stringオブジェクト作成後は修正不可。修正操作は新しいオブジェクトを返す
  • 実装方法:value配列がfinalで修飾され、setterメソッドがない
  • 不変性の理由:
  • 文字列バッファプール最適化。重複作成を回避
  • スレッド安全。同期不要
  • hashCodeキャッシュをサポート。HashMapキーに適する
  • 欠点:頻繁な修正時は多くの中間オブジェクトを作成

27. 文字列、StringBuilder、StringBufferの違いは?

適切な文字列操作クラスを選択する

  • 文字列:不変。スレッド安全。パフォーマンス悪い(頻繁な修正)
  • StringBuilder:可変。非スレッド安全。パフォーマンス優秀(シングルスレッド)
  • StringBuffer:可変。スレッド安全(同期メソッド)。パフォーマンス平均的(マルチスレッド)
  • パフォーマンス対比:StringBuilder > StringBuffer > String
  • 使用提案:
  • シングルスレッド文字列連結にはStringBuilderを使用
  • マルチスレッド文字列連結にはStringBufferを使用
  • 修正不要な場合はStringを使用

28. 文字列定数プールとは?

文字列キャッシュメカニズムを理解する

  • 定義:JVMが維持する文字列キャッシュ。文字列リテラルを保存
  • 位置:JDK 7以降はヒープ。それ以前はメソッド領域
  • 作成メカニズム:
  • "abc"のようなリテラルは自動的に定数プール内に入る
  • new String("abc") で "abc"がプール内にない場合は追加
  • intern()メソッド:文字列を定数プールに追加するか既存の参照を返す
  • 最適化効果:メモリ節約。文字列比較を加速

29. 文字列相等をどう比較する?

文字列比較の方法をマスターする

  • ==比較:参照が同じかを比較。内容は比較しない
  • equals()比較:文字列内容が同じかを比較。使用を推奨
  • equalsIgnoreCase()比較:大文字小文字を無視して内容比較
  • compareTo()比較:辞書順比較。整数を返す
  • Objects.equals():null値を処理する安全な比較
  • ベストプラクティス:文字列内容比較にはequals()を使用。==を使用するのは避ける

30. 文字列連結のパフォーマンス問題は?

文字列連結のパフォーマンスを最適化する

  • 問題:String s = "a" + "b" + "c" は多くの中間オブジェクトとコピーを生成
  • 理由:Stringが不変のため、連結操作は毎回新しいオブジェクトを作成
  • パフォーマンスへの影響:ループ連結時 O(n²)時間計算量
  • 最適化方案:
  • ループ外で直接+を使用:コンパイラはStringBuilderに最適化
  • StringBuilderまたはStringBufferを明示的に使用
  • String.join()、StringJoinerなどツールを使用

🔄 マルチスレッドと並行処理

31. スレッドとは?スレッドをどう作成する?

スレッドの基本概念と作成方法をマスターする

  • スレッド定義:プロセス内の独立実行フロー。メモリを共有するが、独立したスタックを持つ
  • 作成方法:
  • Threadを継承:public class MyThread extends Thread { public void run() {} }
  • Runnableを実装:public class MyRunnable implements Runnable { public void run() {} }
  • Callableを実装:戻り値と例外をサポート
  • 違い:Runnableを推奨。単一継承の制限を回避
  • 起動:thread.start()を呼び出す。run()を直接呼び出さない

32. スレッドのライフサイクルと状態は?

スレッドのさまざまな状態遷移を理解する

  • NEW:スレッド作成後、未起動状態
  • RUNNABLE:実行可能状態(実行を待機中または実行中)
  • BLOCKED:ブロック状態。ロック獲得を待機
  • WAITING:待機状態。他のスレッドの通知を待機
  • TIMED_WAITING:指定時間の待機
  • TERMINATED:スレッド終了
  • 状態遷移:NEW → RUNNABLE → BLOCKED/WAITING → TERMINATED

33. synchronizedの動作原理は?

Javaの組み込みロック機構を理解する

  • 同期メカニズム:オブジェクトの組み込みロックを使用して同期を実現
  • 使用方法:
  • 同期メソッド:public synchronized void method() {}
  • 同期ブロック:synchronized(obj) { ... }
  • 動作原理:
  • 各オブジェクトは監視器 (monitor) を持つ
  • スレッドがロックを獲得して臨界区に進入。相互排他実行
  • バイトコード:monitorenter、monitorexit
  • 特性:再入可能。排他的。自動解放

34. volatileキーワードの作用は?

メモリ可視性と命令重排禁止を理解する

  • 作用:
  • 可視性:修正が他のスレッドに即座に可視化されることを保証
  • 重排禁止:コンパイラとCPUの重排最適化を阻止
  • 実装:メモリバリア。主メモリからの読み書きを強制
  • 制限:原子性を保証できず、synchronizedを替わりにできない
  • 応用シーン:フラグ、ダブルチェックシングルトン、状態フラグ
  • synchronized対比:volatileはより軽量だが、機能は限定的

35. wait()、notify()、notifyAll()の作用と違いは?

スレッド間通信メカニズムをマスターする

  • wait():
  • 現在のスレッドがロックを解放して待機状態に進入。唤醒されるまで
  • 同期ブロック内で呼び出す必須
  • notify():
  • 待機中の1つのスレッドを唤醒(ランダム選択)
  • ロックを解放しない。同期ブロック終了時に解放
  • notifyAll():
  • すべての待機スレッドを唤醒
  • 使用パターン:Producer-Consumerパターン、监护パターン

💡 面接準備提議

基礎を深める:概念を知るだけでなく、原理と実装詳細を理解する

多く練習する:コーディング実践で知識を定着させ、特にマルチスレッドと並行処理関連

ソースコードを読む:Javaライブラリソースコードを研究(コレクション、並行など)し、理解を深める

詳細に注目:一般的な落とし穴とベストプラクティスをマスター。文字列比較、コレクション修正など

具体例で説明:面接では具体的な例で概念を説明し、表現力を強化する