Java Full Stack Developer Top 100 Interview Questions
100 Java full stack developer interview questions with structured answers, comparison tables, flow diagrams and runnable examples. Covers core Java, concurrency, backend architecture, REST APIs, SQL, persistence, HTML, CSS, JavaScript, security, testing, deployment and system design.
Questions 41-60 of 100
Q41.How should an API choose HTTP response status codes?
Choose a status that matches the outcome and the request’s semantics, then provide a consistent response representation where allowed. Returning success for every failure prevents clients and infrastructure from reacting correctly.
200 or 201 202 204 400 or 422 Completed successfully or created a resource Accepted for processing, not yet completed Successful response without a body Invalid request syntax or unacceptable content 401 or 403 404 or 409 5xx Authentication needed or access refused Resource absent or operation conflicts with state Server-side or dependency failure - Contract: define which conditions produce each code, including resource creation, asynchronous work, validation, and concurrency conflicts.
- Security: distinguish authentication from authorization without revealing resource or account existence contrary to the application’s access policy.
- Body and headers: honor method and status rules, include required authentication or resource-location information where appropriate, and avoid bodies for statuses that prohibit them.
- Retries: indicate recoverability through a documented policy and relevant headers rather than making clients guess from a message string.
Q42.How would you evolve an API without breaking existing clients?
I would treat the API as a versioned consumer contract and change it according to compatibility rules. The server’s ability to compile does not prove that deployed clients still work.
- Compatible changes: evaluate additions, optional fields, default values, and new enum values against actual client parsing behavior rather than assuming all additions are harmless.
- Breaking changes: changes to required fields, types, meanings, error behavior, or resource relationships may need a new supported version or a staged migration.
- Version mechanism: choose a consistent URL, media-type, or header policy, and configure caches and routing to distinguish variants when required.
- Migration: publish a deprecation plan, monitor version usage, test representative clients, and keep a defined overlap period before removing an old contract.
Versioning should cover semantics as well as structure. A field that still contains a number can be incompatible if its units or interpretation silently change.
Q43.How would you prevent a retried POST request from creating duplicate orders?
I would implement an idempotency contract backed by durable storage and database uniqueness. Disabling a button in the browser cannot prevent retries from networks, clients, or concurrent requests.
- Require or derive a stable idempotency key scoped to the authenticated caller and operation, and record a canonical representation or hash of the request meaning.
- Atomically reserve or check the key, rejecting reuse with a different request and defining how concurrent attempts observe in-progress work.
- Commit the business result and the durable idempotency outcome through a consistent transaction or recoverable coordination mechanism.
- Return the recorded outcome for a valid retry, with retention covering the supported retry horizon and tests for failures around commit and response delivery.
flowchart TD A["Validate caller request and key"] B{"Key already recorded?"} C{"Request meaning matches?"} D["Return or resolve recorded outcome"] E["Reject conflicting key reuse"] F["Create order and record outcome atomically"] G["Return committed result"] A --> B B -->|yes| C B -->|no| F C -->|yes| D C -->|no| E F --> GTrade-offs:
- Durable key tracking adds storage and lifecycle work but handles uncertainty after a committed operation loses its response.
- An overly short retention period can allow a delayed retry to create another order after the earlier key expires.
Example
The server commits an order, but the response times out before reaching the browser.
- The client retries the same operation using the original idempotency key.
- The server finds the stored key and confirms the request meaning matches.
- It returns the existing order outcome instead of inserting another order.
- A different payload using that same key is rejected according to the contract.
Q44.How do offset pagination and cursor pagination differ for an API?
Offset pagination skips a specified number of rows. Cursor pagination resumes from an ordered position or server-issued token, which can avoid repeatedly scanning or skipping deep result prefixes.
Offset Cursor Simple page-number navigation Efficient ordered continuation Concern: deep-page cost and shifting results Concern: stable ordering and the cursor contract When to use which:
- Use offsets for manageable, relatively stable lists when direct page navigation matters and the database can satisfy the expected access pattern efficiently.
- Use cursors for large or frequently changing feeds, with a deterministic ordering that includes a unique tie-breaker when the main sort key can repeat.
- Use a consistent snapshot or explicit result version when the user must traverse one fixed population instead of a changing feed.
Cap page size and validate cursor scope. A cursor is not an authorization token, and neither pagination style automatically prevents duplicates or omissions during concurrent updates without defined consistency semantics.
Q45.How would you implement cache-aside and handle stale data?
Cache-aside checks a cache before reading the authoritative store and fills the cache after a miss. Correctness depends on key design, expiry, and how writes interact with cached values.
- Identify data that tolerates caching, define freshness expectations, and include relevant tenant, authorization, query, and representation dimensions in the key.
- On a hit, serve an eligible cached value; on a miss, read the authoritative store and populate the cache with an appropriate lifetime.
- After a successful write, invalidate or update affected entries through a strategy that accounts for concurrent reads and possible stale repopulation.
- Monitor hit rate, staleness, memory, and failure behavior, and prevent hot-key misses from overwhelming the underlying database.
flowchart TD A["Read cache by scoped key"] B{"Usable value present?"} C["Return cached value"] D["Read authoritative store"] E["Populate bounded cache entry"] F["Return loaded value"] G["Committed write invalidates affected keys"] A --> B B -->|yes| C B -->|no| D D --> E E --> F G --> ATrade-offs:
- Longer lifetimes reduce database reads but increase the period during which an uncoordinated stale value may be served.
- Stricter consistency can require version checks or stronger coordination; cache deletion alone does not eliminate every race.
Example
A product description is updated while another request is filling the cache.
- Commit the authoritative change before reporting success.
- Invalidate the old entry, but consider whether the concurrent reader can repopulate an older value afterward.
- Use an agreed version or bounded-staleness strategy to address that race.
- Verify the result under concurrent reads and writes.
Q46.When should an API process work synchronously instead of using an asynchronous job?
Synchronous processing returns the operation’s outcome within the request. Asynchronous processing accepts durable work and lets the client observe completion later through a documented mechanism.
Synchronous Asynchronous job The response means the operation completed or failed The response means work was accepted for later completion Fits short, bounded user-facing work Fits long-running or bursty work When to use which:
- Use synchronous processing when the operation reliably fits the request deadline and the caller needs an immediate result to continue the interaction.
- Use a durable asynchronous job when processing is lengthy, bursty, or dependent on unreliable external services, with retry and progress behavior designed explicitly.
- Return a stable job identity and status contract for accepted work, including completion, failure, cancellation, and result retention where relevant.
Starting a background thread before returning success is not equivalent to durable job acceptance. The process can fail before the work is recorded or completed, leaving the client with no recoverable outcome.
Q47.When would you choose a modular monolith instead of microservices?
A modular monolith keeps clear internal boundaries within one deployable application. Microservices separate deployment and runtime ownership, introducing network and distributed-data concerns between services.
Modular monolith Microservices Simpler deployment and local coordination Independent deployment and scaling Internal boundaries must be kept deliberately Distributed failures and consistency must be managed When to use which:
- Use a modular monolith when one deployment meets current needs and clear modules can support team ownership without the overhead of many networked services.
- Use microservices when independently evolving business capabilities, scaling patterns, or ownership boundaries justify their operational and coordination costs.
- Define service boundaries around cohesive business responsibilities rather than splitting every database table or controller into a separate service.
Microservices do not remove coupling if releases or databases remain tightly coordinated. Evaluate observability, compatibility, transaction design, local development, and incident ownership alongside potential scaling benefits.
Q48.How does the transactional outbox pattern prevent a lost business event?
The outbox stores the business change and an event record in the same local database transaction. A separate publisher delivers committed events, avoiding an uncoordinated database-write and message-send pair.
- Write the business update and a uniquely identified outbox record in the same transaction so either both commit or neither becomes visible.
- Have a publisher read committed outbox entries and deliver them with retry, monitoring, and an appropriate ordering policy.
- Record delivery progress safely, recognizing that a crash after publication but before acknowledgment can lead to repeated delivery.
- Make consumers handle duplicate event identities and reconcile stuck entries, while managing outbox retention and failed delivery explicitly.
flowchart TD A["Begin business transaction"] B["Write business state"] C["Write identified outbox event"] D["Commit both records"] E["Publish committed event"] F["Consumer applies duplicate safe effect"] G["Record delivery progress"] A --> B B --> C C --> D D --> E E --> F E --> GTrade-offs:
- The pattern makes local state and event intent atomic, but it does not automatically provide exactly-once delivery to every external consumer.
- A separate publisher adds delay and operational work, so monitor event age as well as ordinary request success.
Example
An order is committed just before the application process crashes.
- Its outbox event was committed in the same transaction.
- The publisher finds the event after recovery and delivers it.
- If delivery repeats, the consumer recognizes the stable event identifier.
- The order’s downstream processing remains recoverable without guessing whether a send occurred before the crash.
Q49.How would you use timeouts, retries, and circuit breakers for a failing dependency?
I would bound waiting, retry only suitable failures, and stop repeatedly calling a dependency that is clearly unhealthy. These mechanisms must fit one overall request deadline and duplicate-safety policy.
- Define connection, response, and overall deadlines based on the user operation, preserving enough budget for cleanup and a meaningful response.
- Retry selected transient failures with bounded backoff and jitter only when the operation is safe to repeat or has a reliable idempotency mechanism.
- Use a circuit breaker to reject or reroute calls after a measured failure threshold, allowing controlled probes before returning to normal traffic.
- Limit concurrent work, expose failure and recovery metrics, and provide a fallback only when it is valid for the business operation.
flowchart TD A["Request dependency operation"] B{"Circuit permits call?"} C["Call within remaining deadline"] D{"Recoverable failure and budget?"} E["Back off and retry safely"] F["Return failure or valid fallback"] G["Return successful result"] A --> B B -->|no| F B -->|yes| C C -->|success| G C -->|failure| D D -->|yes| E D -->|no| F E --> BTrade-offs:
- Retries can improve success during brief failures but can also multiply traffic and make an overloaded dependency recover more slowly.
- A fallback that invents success for a failed write is worse than a clear unavailable or pending outcome.
Example
A pricing read repeatedly times out during a dependency outage.
- Bound each attempt and the total checkout deadline.
- Retry only within the agreed read policy and remaining budget.
- Open the circuit when the observed failures justify it.
- Use a still-valid cached quote only if business rules allow it; otherwise return a clear recoverable failure.
Q50.How would you diagnose a slow Java API endpoint?
I would measure where end-to-end time is spent before optimizing code. A slow endpoint can be waiting for resources rather than consuming CPU in business logic.
- Reproduce the affected route with representative input, concurrency, and response size, comparing typical and tail latency against earlier healthy behavior.
- Trace request time across queuing, authentication, application code, database queries, dependency calls, serialization, and network transfer.
- Inspect the dominant stage using suitable evidence such as query plans, thread state, allocation or CPU profiles, pool waits, and downstream timing.
- Apply a targeted change and verify result correctness, throughput, tail latency, and resource use under realistic load before accepting it.
flowchart TD A["Measure endpoint latency distribution"] B["Trace time across stages"] C{"Waiting dominates?"} D["Inspect pools locks and dependencies"] E["Profile computation and allocation"] F["Apply targeted correction"] G["Verify under representative load"] A --> B B --> C C -->|yes| D C -->|no| E D --> F E --> F F --> GTrade-offs:
- More threads can increase database contention instead of reducing delay when connections or query capacity are already saturated.
- Caching may reduce repeated work but adds freshness and authorization-key requirements that must remain correct.
Example
An endpoint spends most of its time waiting for a connection rather than executing its query.
- Compare active connections, hold time, queueing, and database capacity.
- Find a slow external call occurring inside the transaction.
- Refactor the boundary where correctness permits so the connection is released earlier.
- Re-test concurrency and confirm reduced waiting without breaking the business invariant.
Q51.How do database isolation levels affect concurrent transactions?
Isolation determines which effects of concurrent work a transaction may observe. It is one part of ACID and must be chosen around the business invariant being protected.
Read uncommitted Read committed Repeatable read Serializable May expose uncommitted changes Avoids dirty reads Protects repeated reads of existing values Equivalent to some serial execution Some engines give stronger behavior Later reads may see newer committed data Other anomalies depend on the implementation May need transaction retries When to use which:
- Use an isolation level that supports the actual operation, checking the database’s documented behavior rather than relying on a portable-sounding name alone.
- Use stronger isolation, conditional writes, or suitable locks when an invariant spans several reads and writes that another transaction could invalidate.
- Handle serialization failures and deadlock victims with a bounded safe retry of the whole logical transaction when the application can support it.
A transaction wrapper does not automatically prevent lost updates or write skew at every isolation level. External side effects also remain outside a local database transaction unless separately coordinated.
Q52.When should you normalize a relational schema, and when is denormalization useful?
Normalization separates facts according to keys and dependencies to reduce redundant updates. Denormalization intentionally duplicates or precomputes selected information to serve a measured access need.
Normalization Denormalization Clear ownership and fewer update anomalies Simpler or faster selected reads Costs more relationships or joins Costs extra synchronization and storage When to use which:
- Normalize operational data when several copies of the same fact would otherwise need coordinated updates, especially for independently changing entities.
- Denormalize selectively when profiling shows a useful read improvement and the team can define how derived copies are refreshed, reconciled, and corrected.
- Keep the authoritative source of each fact clear even when summaries, caches, or reporting tables contain convenient copies.
A table with fewer joins is not automatically a better design. Evaluate write complexity, consistency, index behavior, query patterns, and the cost of maintaining derived values under concurrent changes.
Q53.How would you list every customer with an order count, including customers with no orders?
I would start from the customer table and use a left join to orders, then count a non-null order identifier. Counting all joined rows would incorrectly count the placeholder row for a customer without matches.
- Population: the left side must contain every customer that should appear in the output, even if the order table has no corresponding record.
- Counting: use
COUNT(order_id)when that identifier is guaranteed non-null for real orders;COUNT(*)counts the unmatched outer-join row too. - Filtering: place appropriate order conditions in the join or a prefiltered input when customers with zero qualifying orders must remain visible.
- Grain: group by the customer identity and avoid additional one-to-many joins that would multiply orders before aggregation.
Define whether canceled or incomplete orders belong in the business count rather than letting an implicit filter decide it.
Example
This standalone query retains the third customer and gives that customer a zero count.
WITH customers(customer_id) AS ( VALUES (1), (2), (3) ), orders(order_id, customer_id) AS ( VALUES (10, 1), (11, 1), (12, 2) ) SELECT c.customer_id, COUNT(o.order_id) AS order_count FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id GROUP BY c.customer_id ORDER BY c.customer_id; -- Expected rows: (1, 2), (2, 1), (3, 0)Q54.How would you choose an index for a slow database query?
I would begin with the query’s filtering, join, and ordering needs and inspect the execution plan. Creating an index on every mentioned column can increase cost without reducing the important work.
- Identify the actual query parameters, returned columns, data distribution, expected selectivity, and relevant write workload.
- Inspect scans, estimated versus actual rows, sorting, joins, and time or buffer use, distinguishing missing access paths from blocking or incorrect query logic.
- Evaluate an index whose key order and optional covered columns fit the common access pattern, checking whether expressions or conversions prevent useful access.
- Measure representative reads and writes after the change, retaining the index only when the overall benefit justifies its storage and maintenance cost.
flowchart TD A["Inspect query and data distribution"] B["Read execution plan"] C{"Access path is the bottleneck?"} D["Design suitable index"] E["Investigate other query costs"] F["Measure read and write effects"] A --> B B --> C C -->|yes| D C -->|no| E D --> FTrade-offs:
- A covering index may reduce table lookups but consumes more space and adds write work.
- A full scan may be correct for a query reading most rows, so index usage alone is not a success metric.
Example
A query filters one customer’s orders and sorts them by creation time.
- Inspect the plan and the number of qualifying rows.
- Evaluate a composite access path beginning with the customer key and supporting the required ordering.
- Verify actual query latency under representative parameters.
- Measure insert and update overhead before keeping the new index.
Q55.How would you find the second highest distinct salary in SQL?
I would rank distinct salary levels rather than employee rows and select the second level. Tied employees must not cause the highest salary to be mistaken for the second highest distinct value.
- Missing data: exclude NULL salaries unless the question explicitly defines an alternate business treatment for unknown values.
- Ranking: use DENSE_RANK over salary descending so ties share a rank and each new distinct amount receives the next consecutive rank.
- Result shape: return a distinct salary when the question asks for the amount, or return all employee rows at that rank when it asks for the employees.
- Boundary: if the dataset contains fewer than two distinct salaries, define whether the application expects no row or an explicitly missing result.
Using a simple offset over ungrouped employees answers a different question whenever several employees share a salary.
Example
The highest salary occurs twice, but the second distinct salary is still 90.
WITH employees(employee_id, salary) AS ( VALUES (1, 100), (2, 100), (3, 90), (4, 80), (5, NULL) ), ranked AS ( SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank FROM employees WHERE salary IS NOT NULL ) SELECT DISTINCT salary FROM ranked WHERE salary_rank = 2; -- Expected row: (90)Q56.How would you select the top two employees by salary in each department?
I would rank employees separately within each department and filter the result in an outer query. A global row limit cannot return an independent top group for every department.
- Partition: use the department identity as the window partition so each department starts its own ranking sequence.
- Tie policy: use ROW_NUMBER with a stable employee identifier when the requirement is at most two employees; use a shared ranking when all ties must qualify.
- Ordering: make salary direction and treatment of unknown salaries explicit, and add the final presentation order separately from the window calculation.
- Validation: test departments with one employee, equal salaries, and missing values to ensure the selected behavior matches the question.
If the query starts from joined data, first ensure there is one input row per employee rather than ranking duplicated rows.
Example
This query returns at most two employees per department and breaks salary ties by employee identifier.
WITH employees(employee_id, department, salary) AS ( VALUES (1, 'A', 100), (2, 'A', 100), (3, 'A', 80), (4, 'B', 95), (5, 'B', 90) ), ranked AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY department ORDER BY salary DESC, employee_id ) AS rn FROM employees WHERE salary IS NOT NULL ) SELECT department, employee_id, salary FROM ranked WHERE rn <= 2 ORDER BY department, rn; -- Expected rows: ('A', 1, 100), ('A', 2, 100), ('B', 4, 95), ('B', 5, 90)Q57.How would you calculate a running total for each account in SQL?
I would use a windowed SUM partitioned by account with an explicit ordering and row frame. This preserves transaction rows while calculating each account’s cumulative amount independently.
- Scope: use compatible units and currency, and determine whether the requested amount includes a separate opening balance before interpreting it as an account balance.
- Sequence: order by the authoritative transaction sequence or timestamp plus a unique tie-breaker so intermediate totals are deterministic.
- Frame: specify all preceding rows through the current row when the result should advance one transaction at a time.
- Presentation: apply a final ORDER BY for returned rows, because the ordering inside a window defines the calculation rather than a general result-order guarantee.
Be careful when filtering history: calculating after excluding older transactions changes the starting total unless that omission is intentional.
Example
This example calculates cumulative transaction amounts from a zero starting point for each account.
WITH transactions(account_id, transaction_id, amount) AS ( VALUES (1, 1, 20), (1, 2, -5), (1, 3, 10), (2, 4, 7) ) SELECT account_id, transaction_id, SUM(amount) OVER ( PARTITION BY account_id ORDER BY transaction_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS running_total FROM transactions ORDER BY account_id, transaction_id; -- Expected rows: (1, 1, 20), (1, 2, 15), (1, 3, 25), (2, 4, 7)Q58.Why do prepared statements help prevent SQL injection?
Prepared statements bind data values separately from SQL structure, so a supplied value is not interpreted as newly introduced SQL syntax. The protection depends on actually using parameter binding rather than concatenating untrusted text.
- Values: bind identifiers, search terms, dates, and other data through the supported parameter API, preserving their intended types where practical.
- Structure: table names, column names, sort directions, and other SQL syntax generally require controlled construction from an allowlist rather than ordinary value placeholders.
- Coverage: apply the same discipline to raw queries inside persistence layers; using an object-mapping abstraction does not make concatenated query fragments safe.
- Defense: validate business inputs, use least-privilege database access, and return safe errors as complementary controls rather than substitutes for binding.
Escaping strings manually is easy to get wrong across encodings and dialects. Prefer a supported binding mechanism and inspect any remaining dynamically assembled SQL structure.
Q59.What does a database connection pool do, and how do connection leaks happen?
A connection pool manages reusable database connections so requests do not repeatedly establish expensive physical sessions. It also limits the amount of concurrent database work the application can admit.
- Borrowing: a request acquires a connection, uses it within a bounded operation, and returns it promptly through the documented close or release mechanism.
- Leaks: omitted cleanup on exceptional paths, retained connection references, or unfinished transactions can prevent resources from returning to the pool.
- Contention: long connection hold times or excessive concurrency create wait queues even when individual queries are reasonably fast.
- Operations: monitor active and idle connections, acquisition latency, timeouts, transaction duration, and database capacity together rather than increasing the pool blindly.
Use structured resource ownership and avoid slow unrelated work while holding a connection. A pool’s configured maximum across all application instances must still fit the database’s sustainable capacity.
Q60.What are the main entity lifecycle states in a Java persistence context?
A persistence context tracks managed entity instances and coordinates their changes with database operations. Knowing an entity’s state explains why changing an object may or may not lead to a stored update.
- New or transient: the object has not become managed through the relevant persistence operation and is not automatically tracked for insertion merely because it exists in memory.
- Managed: the context tracks the entity, and eligible changes can be detected and synchronized according to flush and transaction rules.
- Detached: the object is no longer associated with that context, so changing it does not automatically update the database through the old context.
- Removed: the managed entity has been marked for deletion, with database effects synchronized according to the configured lifecycle and transaction semantics.
A merge-style operation generally returns a managed representation; callers should not assume the original detached object itself becomes the tracked instance.