面接を受けていると、同じテーマが繰り返し出てくることに気づきますよね。Java/Spring 系のバックエンド面接でよく聞かれる質問をテーマ別にまとめてみました。実際に受けた質問38個をもとに、どう答えればよいかを一緒に整理していきます。
Java の基礎#
Java の特徴#
オブジェクト指向の3つの柱で答えるのが安全です。
- カプセル化(Encapsulation) — データと振る舞いを一つにまとめ、外部には必要なものだけを公開します。
- 継承(Inheritance) — 親クラスの属性と振る舞いを子クラスが受け継ぎます。
- ポリモーフィズム(Polymorphism) — 同じインターフェースで多様な実装を呼び出せます。
これに 抽象化(Abstraction) を加えて4つとして説明することもあります。さらに、JVM 上で動くため プラットフォーム独立(Write Once, Run Anywhere) であること、ガベージコレクションでメモリを自動管理することにも触れると良いです。
最近は面接で 最新の Java 機能もよく聞かれます。Java 21 (LTS) 以降の主要機能くらいは押さえておきましょう。
- Record (Java 16+) — 不変データクラスを一行で宣言
- Sealed Class (Java 17+) — 継承可能なクラスを明示的に制限
- Pattern Matching (Java 21+) —
switchとinstanceofのパターンマッチング - Virtual Threads (Java 21+) — Project Loom の成果。軽量スレッドで並行処理
クラスとオブジェクト#
- クラス(Class) — オブジェクトを作るための 設計図。属性(フィールド)と振る舞い(メソッド)を定義します。
- オブジェクト(Object) — クラスから作られた 実際にメモリに乗った実体。
たい焼きの型がクラスなら、その型で焼いたたい焼きの一つひとつがオブジェクトです。
データ型(プリミティブ vs 参照型)#
プリミティブ型(Primitive) — 値そのものをスタックに格納します。byte、short、int、long、float、double、char、boolean の8種類です。
参照型(Reference) — ヒープに格納されたオブジェクトの アドレスを持ちます。クラス、配列、インターフェースなどが参照型です。
int a = 10; // プリミティブ:値10そのものを格納
String s = "hello"; // 参照型:ヒープ上のオブジェクトのアドレスを格納インスタンス化#
クラスという設計図からオブジェクトを 実際にメモリ上に作る行為です。new キーワードを使います。
User user = new User(); // User クラスをインスタンス化このとき作られた user をクラスの インスタンスと呼びます。オブジェクトとほぼ同じ意味ですが、「どのクラスのインスタンスか」を強調したいときに「インスタンス」という表現を使います。
インターフェース vs 抽象クラス#
| 区分 | インターフェース | 抽象クラス |
|---|---|---|
| 目的 | 実装の強制(契約) | 共通機能 + 一部強制 |
| 多重継承 | 可能 | 不可(単一継承) |
| フィールド | public static final のみ |
あらゆる種類が可能 |
| メソッド | abstract + default + static | abstract + 通常 |
| 使い時 | 「何ができるか」 | 「何であるか(is-a)」 |
インターフェースは「このクラスはこういうことができる」という 契約です。抽象クラスは共通実装を子に継承させつつ、特定のメソッドの実装を子に強制したいときに使います。
ジェネリクス(Generic)を使う理由#
一言で言うと 型安全性 + コードの再利用です。
// ジェネリクスなし
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // キャスト必要、実行時エラーのリスク
// ジェネリクス使用
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // キャスト不要、コンパイル時に型チェック実行時に発生し得る ClassCastException を コンパイル時に検出できるのが最大のメリットです。
コレクションフレームワーク#
全体像#
大きく3つのインターフェースに分かれます。
- List — 順序あり、重複可(
ArrayList、LinkedList) - Set — 順序なし、重複排除(
HashSet、TreeSet、LinkedHashSet) - Map — キー-値形式(
HashMap、TreeMap、LinkedHashMap)
Map は Collection インターフェースを継承していませんが、コレクションフレームワークに含まれます。
Java 21 で Sequenced Collections が追加されました。SequencedCollection、SequencedSet、SequencedMap インターフェースが、先頭/末尾要素への一貫した API(getFirst()、getLast()、addFirst()、addLast()、reversed())を提供します。それ以前は LinkedList、LinkedHashSet、LinkedHashMap ごとに先頭/末尾へのアクセス方法がバラバラでした。
Array vs ArrayList#
| 項目 | Array | ArrayList |
|---|---|---|
| サイズ | 固定 | 可変 |
| 型 | プリミティブ/参照型ともに可 | 参照型のみ(ジェネリクス) |
| 性能 | 高速(軽量) | わずかなオーバーヘッド |
| 機能 | 最小限 | add、remove、contains など豊富 |
ArrayList も内部的には配列で実装されています。ただし 容量が足りなくなると、より大きな配列を新しく作ってコピーすることで可変サイズを実現しています。
キー-値 / 重複排除#
- キー-値:
Map系 →HashMap、TreeMap、LinkedHashMap - 重複排除:
Set系 →HashSet、TreeSet、LinkedHashSet
for-each で使える型#
Iterable インターフェースを実装したすべての型です。配列も使えます。
public interface Iterable<T> {
Iterator<T> iterator();
}Collection は Iterable を継承しているので、List、Set はそのまま使えます。Map は直接使えないので、entrySet()、keySet()、values() で変換して使います。
Spring & DI/IoC#
DI と IoC#
- IoC (Inversion of Control) — 制御の反転。オブジェクトの生成とライフサイクル管理を 開発者ではなくフレームワーク(コンテナ)が担当します。
- DI (Dependency Injection) — 依存性注入。オブジェクトが必要な他のオブジェクトを 自分で作らずに外部から注入してもらう仕組み。IoC を実現する一つの手段です。
// DI なし
public class OrderService {
private UserRepository repo = new UserRepository(); // 強結合
}
// DI 適用
public class OrderService {
private final UserRepository repo;
public OrderService(UserRepository repo) { // 外部から注入
this.repo = repo;
}
}メリットは 結合度の低下、テストしやすさ、コードの再利用性の向上です。
Spring Bean Scope#
- singleton(デフォルト) — コンテナごとに一つのインスタンス
- prototype — リクエストごとに新しいインスタンス
- request — HTTP リクエストごとに一つ(Web 専用)
- session — HTTP セッションごとに一つ(Web 専用)
- application — ServletContext ごとに一つ(Web 専用)
ほとんどのサービス Bean はステートレスに設計するので、singleton で十分です。
Spring のバージョン#
2026年時点での実務標準は Spring Boot 3.x / Spring Framework 6.x です。主な変更点:
- Java 17 以上が必須(Spring Boot 3.2 から Java 21 が正式サポート、Virtual Threads が活用可能)
javax.*→jakarta.*パッケージ変更(Jakarta EE 9 への移行)。最大の移行課題です。- GraalVM Native Image の正式サポート
- Observability の標準化(Micrometer + OpenTelemetry)
Spring Boot 2.x を使っているプロジェクトは、この移行が面接でよく話題になります。
REST 標準アノテーション(HATEOAS / OpenAPI)#
面接で「Spring の API アノテーションは標準に準拠していないが、標準化するにはどのライブラリを使うか?」という質問は、たいてい次のどちらかを聞いています。
- HATEOAS (Hypermedia As The Engine Of Application State) — Roy Fielding が定義した真の REST の最終段階。Spring では Spring HATEOAS ライブラリを使います。
- OpenAPI 3 / Swagger — API 仕様の標準。Spring では springdoc-openapi が事実上の標準(以前使われていた SpringFox は非推奨)。
HATEOAS はレスポンスに次のアクションへの リンク(href) を含めて、真の REST に近づけます。
{
"id": 1,
"name": "User",
"_links": {
"self": { "href": "/users/1" },
"orders": { "href": "/users/1/orders" }
}
}実務では HATEOAS のフル適用は稀で、OpenAPI で API 仕様を標準化する方がはるかに一般的です。
HTTP & REST#
HTTP#
- HyperText Transfer Protocol — Web でクライアント-サーバーが通信するプロトコル
- ステートレス(Stateless) — 各リクエストは独立し、サーバーは過去のリクエストを記憶しません
- リクエスト-レスポンス構造:メソッド、URL、ヘッダー、ボディから成る
- デフォルトポート 80、HTTPS は 443
バージョンごとの主な違いも知っておきましょう。
| バージョン | 特徴 | トランスポート |
|---|---|---|
| HTTP/1.1 | テキストベース、Keep-Alive | TCP |
| HTTP/2 | バイナリ、多重化(Multiplexing)、ヘッダー圧縮(HPACK)、Server Push | TCP + TLS |
| HTTP/3 | QUIC 上で動作、0-RTT 接続、TCP の Head-of-Line Blocking 解消 | UDP + TLS 1.3 |
2026年時点では、主要 CDN や大規模サービスのほとんどが HTTP/3 を有効化しています。
HTTP メソッドの安全性/冪等性#
| メソッド | Safe (安全) | Idempotent (冪等) | 用途 |
|---|---|---|---|
| GET | ○ | ○ | 取得 |
| HEAD | ○ | ○ | ヘッダーのみ取得 |
| OPTIONS | ○ | ○ | サポートメソッド確認 |
| PUT | × | ○ | 全更新/作成 |
| DELETE | × | ○ | 削除 |
| POST | × | × | 作成 |
| PATCH | × | × | 部分更新 |
- Safe — サーバーの状態を変更しません(取得のみ)
- Idempotent — 何度リクエストしても結果が同じ
HTTPS#
HTTP に TLS/SSL 暗号化を加えたプロトコルです。3つの保証を覚えておきましょう。
- 機密性(Confidentiality) — 通信データの暗号化
- 完全性(Integrity) — 途中でデータが改ざんされていないことを保証
- 認証(Authentication) — サーバーが本物であることを証明書で確認
ハンドシェイクは 非対称鍵でセッション鍵を安全に交換 → その後の通信は高速な対称鍵で進めます。
現在は TLS 1.3 が標準です。1-RTT ハンドシェイクで高速化され、脆弱とされた RSA 鍵交換、CBC、SHA-1 などのアルゴリズムは削除されました。TLS 1.0/1.1 はとっくに非推奨で、TLS 1.2 も段階的に整理される流れです。
RESTful API#
REST は REpresentational State Transfer の略です。主な原則は次の通りです。
- リソース(Resource)を URL で識別 —
/users/1 - 動作は HTTP メソッドで表現 — GET、POST、PUT、DELETE
- ステートレス性(Stateless) — サーバーはクライアントの状態を保存しません
- 表現(Representation) — JSON、XML などでリソースの状態を伝達
URL には 名詞だけ、動作は HTTP メソッドで。
悪い例: GET /getUser/1
良い例: GET /users/1
悪い例: POST /deleteUser/1
良い例: DELETE /users/1OSI 7階層と HTTP#
| 階層 | 名前 | 例 |
|---|---|---|
| 7 | アプリケーション | HTTP、FTP、SMTP、DNS |
| 6 | プレゼンテーション | SSL/TLS、JPEG、ASCII |
| 5 | セッション | NetBIOS、RPC |
| 4 | トランスポート | TCP、UDP |
| 3 | ネットワーク | IP、ICMP |
| 2 | データリンク | Ethernet、MAC |
| 1 | 物理 | ケーブル、信号 |
HTTP は 7階層(アプリケーション)、TCP は4階層、IP は3階層で動作します。HTTPS の TLS は通常6階層(プレゼンテーション)に分類されます。
Web サーバー & サーブレット#
Web サーバー vs WAS#
- Web サーバー — 静的コンテンツ(HTML、CSS、画像)を処理。Nginx、Apache HTTPD。
- WAS (Web Application Server) — 動的コンテンツの処理、ビジネスロジックの実行。Tomcat、Jetty。
実務では Web サーバー(Nginx)を前段、WAS(Tomcat)を後段に置く構成が多いです。静的リソースは Web サーバー、動的処理は WAS が担当することで負荷を分散します。
サーブレット(Servlet)の動作#
- クライアントからリクエストが来る
- サーブレットコンテナ(Tomcat など)がリクエストを受け取る
- URL マッピングで該当サーブレットを探す(なければインスタンス化)
- スレッドを一つ割り当てて
service()→doGet()/doPost()を呼ぶ - レスポンスを生成してクライアントに返す
サーブレットは シングルトンで動作するので、複数のリクエストが同じインスタンスを共有します。そのため インスタンス変数に状態を保持するのは危険です。
Jakarta EE 9 以降、パッケージは
javax.servlet→jakarta.servletに変わり、Spring Boot 3.x もこれに追随しています。また Spring Boot 3.2 から Virtual Threads を有効にすれば、リクエストごとにスレッドを割り当てるモデルのコストが大きく下がります。非同期/ノンブロッキングが必要なら Spring WebFlux(リアクティブ)という選択肢もあります。
サーブレットフィルタ(Filter)#
サーブレットの 手前でリクエストとレスポンスを横取りするコンポーネントです。認証、ロギング、エンコーディング変換、CORS 処理などに使います。
クライアント → Filter 1 → Filter 2 → サーブレット → Filter 2 → Filter 1 → クライアントSpring の Interceptor と似ていますが、適用タイミングが違います。Filter は DispatcherServlet の前、Interceptor は DispatcherServlet の後・コントローラの前で動きます。
Cookie vs セッション#
| 項目 | Cookie | セッション (Session) |
|---|---|---|
| 保存場所 | クライアント(ブラウザ) | サーバー |
| セキュリティ | 比較的弱い | 安全 |
| 容量 | 4KB 制限 | サーバーメモリ次第 |
| 有効期限 | 設定した時間 | ブラウザ終了時(または timeout) |
機密情報はセッション、単純な識別子や自動ログイントークンは Cookie に保存するのが一般的です。自動ログインを Cookie に置く理由は、ブラウザを閉じても保持される必要があるためです。セッションはブラウザ終了/timeout で消えてしまいます。
Cookie のセキュリティ属性も押さえておきましょう。
- HttpOnly — JavaScript からアクセス不可(XSS 対策)
- Secure — HTTPS でのみ送信
- SameSite —
Strict/Lax/None。CSRF 対策。Chrome のデフォルトはLaxが標準になりました。 - Partitioned (CHIPS) — サードパーティ Cookie の分離、トラッキング防止
最近はセッションの代わりに JWT(JSON Web Token) によるステートレス認証もよく使われます。サーバーメモリを消費しないので水平スケールに有利ですが、トークンの失効(revoke)が難しいという欠点があります。そのため、短命の access token + 長命の refresh token を組み合わせて運用します。
セキュリティ#
SQL インジェクション対策#
最も確実な方法は PreparedStatement(パラメータバインディング) です。
// 危険:文字列結合
String sql = "SELECT * FROM users WHERE id = '" + userId + "'";
// 安全:パラメータバインディング
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, userId);「MyBatis や JPA を使えば自動で防いでくれるのでは?」 — 部分的にはその通りですが、完全に安全とは言えません。
- MyBatis の
#{}は PreparedStatement に変換され安全、${}は文字列置換で危険 - JPA の JPQL は安全ですが、Native Query で文字列を結合すると同様に危険
結論:ORM を使っていても、動的に SQL の一部を文字列で組み立てるときは常に注意が必要です。
プロキシ vs ゲートウェイ#
- プロキシ(Proxy) — クライアントとサーバーの間に立つ 仲介者。同一プロトコル内で単純に要求を転送します。
- Forward Proxy — クライアント側(社内網 → 外部)
- Reverse Proxy — サーバー側(外部 → 内部サーバーへの分散、Nginx)
- ゲートウェイ(Gateway) — プロトコル変換、ルーティング、認証、ロギングなど 付加機能まで担います。API Gateway が代表的です。
要するに プロキシは単純な仲介、ゲートウェイはトラフィックの入口で多様なポリシーを適用します。
MSA 時代に入ってからは Service Mesh(Istio、Linkerd)もよく話題になります。サイドカープロキシ(Envoy など)を介して サービス間通信をインフラレベルで処理します。mTLS、トラフィック分散、可観測性といった横断的関心事をアプリケーションコードの外に出すのが要点です。
メッセージキュー & キャッシュ#
RabbitMQ の特徴#
- AMQP(Advanced Message Queuing Protocol) ベース
- Producer → Exchange → Queue → Consumer 構造
- メッセージの永続化に対応(ディスク保存)
- 柔軟なルーティング(Exchange タイプによる)
- 多様な言語クライアントをサポート
RabbitMQ Exchange タイプ(キュー以外の方式)#
キューに直接積むのではなく、Exchange がルーティングルールに従ってキューに振り分けます。
| Exchange タイプ | 動作 |
|---|---|
| Direct | ルーティングキーが完全一致するキューへ |
| Topic | ルーティングキーのパターンマッチ(*、#) |
| Fanout | バインドされた全てのキューにブロードキャスト |
| Headers | メッセージのヘッダーに基づくルーティング |
Topic Exchange が、よく言う「トピックに publish する」方式です。order.created、order.cancelled のようなパターンルーティングに便利です。
ちなみに RabbitMQ 4.x からは Streams という新しい永続ログ構造もサポートされ、Kafka のような 再処理・長期保持が必要なシナリオにも使えます。ただし大規模なイベントストリーミングはやはり Kafka が強く、RabbitMQ は 信頼性のあるワークキューとして採用されることが多いです。
Redis 以外のキャッシュ(インメモリキャッシュ)#
「RAM キャッシュ」と聞かれたら、ローカルなインメモリキャッシュのことです。
- Caffeine — Java で最もよく使われる高性能インメモリキャッシュ
- Ehcache — Java 界の老舗、分散キャッシュにも対応
- Guava Cache — Caffeine の前身。今は Caffeine 推奨
- Memcached — 分散インメモリキャッシュ(Redis よりシンプル)
Redis は 別サーバー + ネットワーク経由、Caffeine は JVM 内部で動きます。同一マシン内で高速なキャッシュが必要なら Caffeine、複数サーバーで共有するなら Redis が向いています。
JavaScript#
Minify する理由#
- ファイルサイズの削減 → ダウンロード速度向上
- ネットワーク費用の節約
- パース時間の短縮
空白、改行、コメントを削除し、変数名を短くします。セキュリティが目的ではありません(難読化は別の概念です)。
最近のビルドはほぼ esbuild、swc、Rolldown などの Go/Rust 製ツールが処理しています。従来の Webpack/Babel に比べ 数十倍速い バンドル/トランスパイルが可能なので、Vite や Turbopack といった次世代バンドラーの標準になりました。
インライン vs 外部ファイル#
| 項目 | インライン (<script>...</script>) |
外部ファイル (<script src="...">) |
|---|---|---|
| HTTP リクエスト | 追加なし | 追加リクエストが発生 |
| キャッシュ | 不可 | ブラウザキャッシュ可 |
| 再利用 | ページごとに重複 | 複数ページで共有 |
| 保守性 | 難しい | 容易 |
| 初期描画 | 速くなることも | 初回リクエストは遅い |
HTTP/1.1 時代は外部ファイルのリクエスト費用が重荷でしたが、HTTP/2 以降の多重化で外部ファイルを複数取得しても大きな損失はなくなりました。そのため モジュールを細かく外部ファイルに分割し、ファーストビューに必要な critical CSS/JS だけインライン化するのが推奨されます。
インラインスクリプトの配置#
<head>— ページ描画前に実行、DOM にはアクセス不可<body>の終わり — DOM ロード後に実行、一般的に推奨<head>+defer/async— ダウンロードは並列、実行タイミングを制御
DOM を操作するコードなら </body> 直前が安全です。または DOMContentLoaded イベントを使います。
最近は <head> に置いて defer または type="module" を付けるのがモダンなベストプラクティスです。どちらも HTML パースをブロックせず、DOM 準備後に順序通りに実行されます。
<!-- モダン推奨パターン -->
<script type="module" src="/app.js"></script>
<script defer src="/legacy.js"></script>協業とコミュニケーション#
意見が異なるときどう対応するか#
この種の質問は 態度と思考プロセスを見ています。模範解答は次の流れです。
- 相手の意見を最後まで聞く — 意図と根拠を正確に理解
- 共通の目標を確認 — 「お互いに X を望んでいるよね?」
- データ/根拠ベースで議論 — 感情ではなく客観的な根拠
- 決まった結論には従う(Disagree and Commit) — 決定後は100%協力
核心は 「相手に勝つことではなく、より良い結果を出すこと」 という姿勢です。
まとめ#
面接の質問は結局 「基本をどれだけ正確に理解しているか」 を見るための道具です。キーワードを並べるだけの答えより、なぜそうなのか + 実務でどう使うかを一緒に説明できるよう準備しておくのが良いです。
知らない質問が出たら、正直に「正確には分かりませんが、こういう文脈で耳にしたことはあります」と認めるのも大きな加点になります。知らないことを知ったかぶりするのが最も危険です。
People take different roads seeking fulfilment and happiness. Just because theyre not on your road doesn't mean they've gotten lost.
— Dalai Lama