Skip to content

Hard Java Full Stack with React Interview Questions

42 hard questions with answers. Design, trade-off and deep-dive questions asked for senior roles and at top product companies.

Topics in the hard set

JPA and Hibernate: 7 REST APIs, Security and Microservices: 6 SQL and Databases: 6 React Frontend: 6 Spring Boot: 5 Core Java: 4 Collections and Streams: 4 JavaScript, HTML and CSS: 4
Solved — hardStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

What is the difference between volatile and synchronized?

Save to ▾
Show answer
volatile guarantees visibility: a write by one thread is immediately visible to others and reordering around it is restricted, but it does not make compound operations like count++ atomic. synchronized gives both mutual exclusion and visibility by acquiring a monitor lock, so only one thread runs the block at a time. Use volatile for simple flags such as a shutdown signal, synchronized or locks for check-then-act logic, and AtomicInteger or LongAdder for counters.
02

Use CompletableFuture to call two services in parallel and combine the results.

Save to ▾
Show answer
CompletableFuture<User> userF = CompletableFuture.supplyAsync(() -> userClient.get(id), executor); CompletableFuture<List<Order>> ordersF = CompletableFuture.supplyAsync(() -> orderClient.byUser(id), executor); Profile profile = userF .thenCombine(ordersF, Profile::new) .orTimeout(2, TimeUnit.SECONDS) .exceptionally(ex -> Profile.empty()) .join(); // Pass a dedicated executor instead of the common pool for blocking I/O.
03

Your Spring Boot service crashes with OutOfMemoryError: Java heap space in production. How do you investigate?

Save to ▾
Show answer
First capture evidence: enable -XX:+HeapDumpOnOutOfMemoryError and check GC logs and container memory limits. Open the heap dump in Eclipse MAT or VisualVM and look at the dominator tree for the largest retained objects. Common causes are unbounded caches, loading full tables into lists, static collections that keep growing and large file reads. Fix the leak, add pagination or streaming, set a sensible -Xmx below the container limit and add memory alerts.
04

CPU is at 100 percent and requests are hanging. How would you find the cause in a Java application?

Save to ▾
Show answer
Take several thread dumps a few seconds apart using jstack, jcmd Thread.print or the Actuator threaddump endpoint. Threads stuck in the same RUNNABLE frame point to a hot loop or heavy regex, while threads BLOCKED on each other's locks show a deadlock, which jstack reports directly. Match native thread IDs from top -H to the dump. Profilers such as async-profiler or Java Flight Recorder confirm hot methods before you fix the code.
05

When should you use parallel streams and when should you avoid them?

Save to ▾
Show answer
Parallel streams split work across the common ForkJoinPool. They help for CPU-heavy, stateless operations on large in-memory data with cheap splitting, such as ArrayList or arrays. Avoid them for small lists, I/O or database calls, operations with shared mutable state, or inside web servers where the common pool is shared by all requests. Always measure with JMH or real load before and after, because overhead often makes them slower.
06

Implement a simple LRU cache in Java.

Save to ▾
Show answer
class LruCache<K, V> extends LinkedHashMap<K, V> { private final int capacity; LruCache(int capacity) { super(16, 0.75f, true); // true = access order this.capacity = capacity; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; } } // Not thread-safe: wrap with Collections.synchronizedMap // or use Caffeine in production.
07

An object put into a HashMap cannot be found later even though it looks equal. What could be wrong?

Save to ▾
Show answer
Most likely a field used in hashCode was changed after insertion, so the object now hashes to a different bucket and get looks in the wrong place. It can also happen when equals is overridden without hashCode. Fix it by using immutable keys, such as records, Strings or IDs, and by basing equals and hashCode on fields that never change. Never mutate an object while it is a key in a hash-based collection.
08

A teammate replaced a simple for loop in a performance-critical path with a long stream pipeline. Is this a concern?

Save to ▾
Show answer
Streams are usually fine and readable, and the JIT optimises them well, but in very hot loops they can add allocation, boxing and lambda overhead. Check for boxing with Stream<Integer> instead of IntStream, and for repeated sorting or collecting. Measure with a JMH benchmark or profiler rather than guessing. Keep streams where readability matters and use a plain loop only where profiling proves a real bottleneck.
09

How does Spring Boot auto-configuration work internally?

Save to ▾
Show answer
Spring Boot reads candidate configuration classes listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports inside starter jars. Each class is guarded by conditions such as @ConditionalOnClass, @ConditionalOnMissingBean and @ConditionalOnProperty, so beans are created only when the right library is present and you have not defined your own. You can inspect the decisions with the --debug flag or the Actuator conditions endpoint and exclude classes with exclude attributes.
10

How does @Transactional work, and why does calling it from the same class not work?

Save to ▾
Show answer
Spring wraps the bean in a proxy. When an external caller invokes a @Transactional method, the proxy starts a transaction, calls the method and commits, or rolls back on a RuntimeException or Error. If a method in the same class calls another @Transactional method through this, the call skips the proxy, so the annotation is ignored. Fix it by moving the method to another bean or restructuring the boundary. The annotation also has no effect on private methods.
11

Write a controller slice test with @WebMvcTest and MockMvc.

Save to ▾
Show answer
@WebMvcTest(ProductController.class) class ProductControllerTest { @Autowired MockMvc mvc; @MockitoBean ProductService service; // @MockBean before Boot 3.4 @Test void returnsProduct() throws Exception { when(service.getProduct(1L)).thenReturn(new ProductDto(1L, "Pen", 20.0)); mvc.perform(get("/api/products/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Pen")); } }
12

A Spring Boot service takes nearly a minute to start. How would you speed it up?

Save to ▾
Show answer
Measure first with the startup Actuator endpoint or ApplicationStartup to see which beans are slow. Common causes are heavy component scanning, eager database or remote calls in @PostConstruct, Hibernate validating a large schema and unneeded auto-configurations. Narrow scan packages, defer work with lazy initialisation or ApplicationReadyEvent, exclude unused starters and use Class Data Sharing. For serverless workloads, consider GraalVM native images or CRaC.
13

Your @Transactional method threw an exception but the data was still saved. Why?

Save to ▾
Show answer
By default Spring rolls back only on unchecked exceptions and Errors, so a checked exception such as IOException commits the transaction. Use @Transactional(rollbackFor = Exception.class) or wrap it in a runtime exception. Other causes are catching the exception inside the method, calling the method from the same class so the proxy is bypassed, a non-public method, or a table engine that does not support transactions such as MyISAM.
14

Explain the first-level and second-level cache in Hibernate.

Save to ▾
Show answer
The first-level cache is the persistence context itself. It is enabled by default and scoped to a session or transaction, so loading the same entity twice returns the same object without a second query. The second-level cache is optional and shared across sessions, configured with a provider like Ehcache or Infinispan through JCache. It suits read-mostly reference data. In clustered systems many teams prefer an explicit Redis cache at the service layer instead.
15

How does optimistic locking work with @Version?

Save to ▾
Show answer
Add a numeric or timestamp field annotated with @Version. Hibernate includes it in the UPDATE where clause and increments it. If another transaction already changed the row, no rows match, and an OptimisticLockException is thrown. This prevents lost updates without holding database locks, which suits web apps with low conflict. The API can return HTTP 409 and ask the user to reload. Pessimistic locking with SELECT FOR UPDATE suits high-conflict cases like seat booking.
16

Build a dynamic search with optional filters using JPA Specifications.

Save to ▾
Show answer
public class ProductSpecs { public static Specification<Product> hasCategory(String c) { return (root, q, cb) -> c == null ? null : cb.equal(root.get("category").get("name"), c); } public static Specification<Product> priceBetween(BigDecimal min, BigDecimal max) { return (root, q, cb) -> (min == null || max == null) ? null : cb.between(root.get("price"), min, max); } } // Repository extends JpaSpecificationExecutor<Product> repo.findAll(Specification.where(hasCategory(cat)) .and(priceBetween(min, max)), pageable);
17

A product listing page fires hundreds of SQL queries for one request. How do you fix it?

Save to ▾
Show answer
Enable SQL logging or Hibernate statistics to confirm it is the N+1 problem, usually from lazy associations accessed while mapping entities to JSON. Fetch the required associations in one query with JOIN FETCH or @EntityGraph, or select a DTO projection with only the needed columns. For collections combined with pagination, page the parent IDs first and then fetch children with an IN query, or use batch fetch size. Add a test that asserts the query count.
18

Two admins edit the same product price at the same time and one change is silently lost. How do you prevent this?

Save to ▾
Show answer
This is a lost update. Add a @Version column for optimistic locking, send the version to the frontend with the entity, and include it in the update request. If the version no longer matches, Hibernate throws an optimistic lock exception, and the API returns HTTP 409 Conflict so the user can reload and retry. For short, high-contention operations like stock decrement, use a pessimistic lock or an atomic UPDATE with a condition.
19

Inserting one lakh rows with saveAll takes many minutes. How do you speed it up?

Save to ▾
Show answer
Enable JDBC batching with hibernate.jdbc.batch_size of 50 or so, plus order_inserts and order_updates. IDENTITY ID generation disables insert batching in Hibernate, so use a SEQUENCE generator with an allocation size on PostgreSQL or Oracle. Flush and clear the persistence context every batch to keep memory low. For very large loads, use JdbcTemplate batchUpdate or the database's bulk loader like COPY or LOAD DATA, which is much faster than ORM.
20

Describe a time you improved the performance of an application.

Save to ▾
Show answer
Situation: our dashboard API took eight seconds and users complained. Task: I was asked to bring it under one second. Action: using SQL logs and a profiler, I found N+1 queries and a missing index. I rewrote the query with a DTO projection, added a composite index and cached reference data with a short expiry. Result: response time dropped to about 400 milliseconds, and I added the query-count check to our test suite.
21

How does the Spring Security filter chain process a request?

Save to ▾
Show answer
Every request passes through a DelegatingFilterProxy into a FilterChainProxy, which picks the matching SecurityFilterChain. Filters run in order: CORS, CSRF, authentication filters such as BearerTokenAuthenticationFilter, exception translation and finally the authorization filter. Authentication filters build an Authentication object, validate it through the AuthenticationManager and store it in the SecurityContextHolder. The authorization filter then checks rules such as hasRole("ADMIN") before the controller runs.
22

What is the circuit breaker pattern and how is it implemented in Spring?

Save to ▾
Show answer
A circuit breaker stops calling a failing downstream service so failures do not cascade. It is closed during normal operation, opens when the failure rate crosses a threshold, returns a fallback immediately while open, and moves to half-open after a wait to test recovery. In Spring Boot it is implemented with Resilience4j using @CircuitBreaker(name = "payment", fallbackMethod = "fallback"), usually combined with timeouts, retries with backoff and bulkheads.
23

Configure Spring Security 6 for a stateless JWT-secured REST API.

Save to ▾
Show answer
@Configuration @EnableMethodSecurity public class SecurityConfig { @Bean SecurityFilterChain api(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .cors(Customizer.withDefaults()) .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**", "/actuator/health").permitAll() .requestMatchers(HttpMethod.DELETE, "/api/**").hasRole("ADMIN") .anyRequest().authenticated()) .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults())); return http.build(); } }
24

A user's JWT is stolen and you need to invalidate it before expiry. How would you design for this?

Save to ▾
Show answer
Pure JWTs cannot be revoked, so keep access tokens short-lived, around 10 to 15 minutes, and use refresh tokens stored server-side and rotated on each use. On logout or theft, revoke the refresh token. For immediate blocking, keep a denylist of token IDs or a per-user token version in Redis and check it in the filter. Send refresh tokens in HttpOnly, Secure, SameSite cookies to reduce theft through XSS.
25

An order service and a payment service have separate databases. How do you keep data consistent when placing an order?

Save to ▾
Show answer
Avoid distributed two-phase commit. Use the saga pattern: each service performs a local transaction and publishes an event, and failures trigger compensating actions, such as cancelling the order if payment fails. Use the transactional outbox pattern so the database write and the event are saved atomically, then a relay publishes to Kafka or RabbitMQ. Make consumers idempotent because events can be delivered more than once.
26

A downstream service is slow, and your API threads are all stuck waiting. What do you do?

Save to ▾
Show answer
Set connection and read timeouts on every client call, because the defaults can wait far too long. Add a Resilience4j circuit breaker with a fallback and a bulkhead to limit concurrent calls to that service, plus retries only with backoff and only for idempotent requests. Consider caching the downstream data or making the call asynchronous through a queue. Monitor latency percentiles and thread pool usage with Micrometer to confirm the fix.
27

Explain transaction isolation levels and the problems each one prevents.

Save to ▾
Show answer
Read Uncommitted allows dirty reads. Read Committed, the default in PostgreSQL, Oracle and SQL Server, prevents dirty reads but allows non-repeatable reads. Repeatable Read, the default in MySQL InnoDB, keeps rows read stable within a transaction, and InnoDB also blocks most phantoms with gap locks. Serializable prevents all anomalies but reduces concurrency. Higher isolation means more locking or retries, so choose per use case and set it with @Transactional(isolation = ...).
28

How does column order matter in a composite index?

Save to ▾
Show answer
A composite B-tree index on (customer_id, status, created_at) is sorted by the first column, then the second, and so on. It can serve queries filtering on customer_id, on customer_id and status, or all three, following the leftmost prefix rule, but it cannot efficiently serve a filter on status alone. Put equality columns first and range or sort columns last. A covering index includes all selected columns so the table need not be read.
29

How do you use EXPLAIN to analyse a slow query?

Save to ▾
Show answer
EXPLAIN ANALYZE SELECT o.id, o.total FROM orders o WHERE o.customer_id = 42 AND o.status = 'PAID' ORDER BY o.created_at DESC LIMIT 20; -- Look for: Seq Scan / type=ALL (full scan), large 'rows' estimates, -- 'Using filesort' or a Sort node, and actual time per step. -- Fix with: CREATE INDEX ix_orders_cust_status_date -- ON orders (customer_id, status, created_at DESC);
30

Your application logs show database deadlock errors during peak hours. How do you resolve them?

Save to ▾
Show answer
A deadlock happens when two transactions lock rows in opposite order and wait for each other; the database kills one. Read the deadlock report, such as SHOW ENGINE INNODB STATUS, to find the statements. Fix by updating rows in a consistent order, keeping transactions short, avoiding user interaction or remote calls inside transactions and adding indexes so updates lock fewer rows. Also add a retry for the deadlock error because occasional ones are normal.
31

Under load, requests fail with a connection timeout from HikariCP. What do you check?

Save to ▾
Show answer
This means all pool connections are busy. Check for long-running queries, transactions held open during remote calls, and connection leaks from code that never closes resources; set leakDetectionThreshold to find them. Compare maximumPoolSize with the database's max connections across all instances, since a bigger pool is not always better. Monitor Hikari metrics through Actuator, fix slow queries and keep @Transactional boundaries short.
32

Paging through a large table with OFFSET becomes very slow on later pages. What is a better approach?

Save to ▾
Show answer
With OFFSET 500000, the database still reads and discards half a million rows. Use keyset or seek pagination instead: remember the last seen sort key and query WHERE (created_at, id) < (:lastDate, :lastId) ORDER BY created_at DESC, id DESC LIMIT 20, backed by a matching index. This is fast at any depth and stable when new rows arrive. The trade-off is no direct jump to page N, which suits infinite scroll and APIs.
33

What triggers a re-render in React, and how does reconciliation decide what to update?

Save to ▾
Show answer
A component re-renders when its state changes, its parent re-renders, or a context it uses changes. Props changing is not itself the trigger; parent re-render is. During reconciliation React compares element types: a different type at the same position destroys and recreates the subtree, while the same type updates props. Keys identify list children. React.memo skips a child re-render when its props are shallowly equal.
34

Create a protected route in React Router that redirects unauthenticated users to login.

Save to ▾
Show answer
function ProtectedRoute({ roles }) { const { user } = useAuth(); const location = useLocation(); if (!user) { return <Navigate to="/login" replace state={{ from: location }} />; } if (roles && !roles.includes(user.role)) { return <Navigate to="/unauthorized" replace />; } return <Outlet />; } // Routes <Route element={<ProtectedRoute roles={['ADMIN']} />}> <Route path="/admin" element={<AdminDashboard />} /> </Route> // The backend must still enforce authorization on every API.
35

Add the JWT to every API call and handle 401 responses using an Axios interceptor.

Save to ▾
Show answer
const api = axios.create({ baseURL: import.meta.env.VITE_API_URL }); api.interceptors.request.use(config => { const token = getAccessToken(); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }); api.interceptors.response.use( res => res, async error => { const original = error.config; if (error.response?.status === 401 && !original._retry) { original._retry = true; await refreshToken(); // calls /api/auth/refresh with cookie return api(original); } return Promise.reject(error); });
36

A table showing 10,000 rows makes the page very slow. How would you improve it?

Save to ▾
Show answer
Rendering thousands of DOM nodes is costly. Use server-side pagination or infinite scroll so the API returns only one page. If all rows must be scrollable, use list virtualization with a library like TanStack Virtual or react-window, which renders only visible rows. Memoize row components with React.memo, keep stable keys, avoid inline heavy calculations, and debounce filters. Profile with React DevTools Profiler to confirm which components are slow.
37

Where should a React app store the JWT, and what are the trade-offs?

Save to ▾
Show answer
localStorage is simple but any XSS script can read it. In-memory storage is safer but is lost on refresh. The commonly recommended pattern is a short-lived access token kept in memory and a refresh token in an HttpOnly, Secure, SameSite cookie that JavaScript cannot read, used to get new access tokens. Cookies need CSRF protection for state-changing calls. Whatever you choose, prevent XSS with proper escaping and a Content Security Policy.
38

In a search box, older API responses sometimes overwrite newer results. How do you fix this race condition?

Save to ▾
Show answer
Each keystroke starts a request, and responses can arrive out of order. Debounce the input by about 300 milliseconds so fewer requests are sent. In the effect, cancel the previous request with AbortController in the cleanup function, or keep an ignore flag so stale responses are discarded. Libraries like TanStack Query handle this automatically by keying queries on the search term and ignoring outdated results.
39

Explain the JavaScript event loop, including microtasks and macrotasks.

Save to ▾
Show answer
JavaScript runs on a single thread with a call stack. Asynchronous work is handled by the browser or Node, and callbacks wait in queues. After the stack empties, the event loop runs all microtasks first, such as promise callbacks and queueMicrotask, then one macrotask, such as setTimeout or an I/O callback, then renders if needed. So Promise.resolve().then(a) runs before setTimeout(b, 0). Long synchronous code blocks everything, including UI updates.
40

Your React app's home page takes 6 seconds to load on mobile. How would you improve it?

Save to ▾
Show answer
Measure with Lighthouse and the browser Performance tab, and check Core Web Vitals such as LCP and INP. Then reduce JavaScript with route-based code splitting and removing heavy libraries, compress and lazy-load images using modern formats like WebP or AVIF with proper sizes, enable gzip or Brotli and long-term caching through a CDN, and avoid render-blocking resources. Server-side rendering or pre-rendering, for example with Next.js, can improve first paint further.
41

A security test shows a stored XSS vulnerability in a comments feature. How do you fix it?

Save to ▾
Show answer
Stored XSS happens when user input containing script is saved and later rendered as HTML. React escapes text by default, so look for dangerouslySetInnerHTML, direct innerHTML or unsafe URL attributes like javascript: links. If rich text is required, sanitise it with a library like DOMPurify, and validate on the server too. Add a Content Security Policy header and keep tokens out of JavaScript-readable storage to limit damage.
42

Memory usage of a single-page app keeps growing the longer it stays open. What could cause this?

Save to ▾
Show answer
Common causes are event listeners, intervals or WebSocket subscriptions that are never removed when a component unmounts, growing global caches or arrays, and detached DOM nodes still referenced by JavaScript. In React, return cleanup functions from useEffect to remove listeners and clear timers. Use the Chrome DevTools Memory tab with heap snapshots to compare before and after navigation and find retained objects.

Frequently asked questions

Who should practise hard Java Full Stack with React questions?

Candidates targeting senior roles or top product companies, and anyone who finds the medium set easy.

How should I practise these?

Answer each question aloud before opening the answer, mark it as solved only when your answer matches the key idea, and save misses to Last-Minute Revision.

Other difficulty levels