Java Skills Test for Hiring (2026): What to Test and Sample Questions

What to assess in Java developers at each level, with original questions for Java 21 and later and a 1 to 4 rubric.

Use this Java skills test guide when you need a fair, repeatable way to check Java developers before the technical loop. It is written for hiring managers and technical recruiters, and it targets modern Java: Java 21 as the baseline, with Java 25 (the newest LTS release) as a bonus. Below you'll find what to measure at each level, 11 sample questions with answers, a formats comparison and a scoring rubric. None of the questions repeat our Java interview questions, so the two sets work together.

What a Java skills test should measure

AreaJuniorMidSenior
Language and typesClasses, interfaces, basic genericsRecords, sealed types and enums used wellAPI design and immutability rules for a team
Collections and streamsList, Map and iterationComparators, collectors, picking the right structurePerformance trade-offs, judgment on parallel streams
ConcurrencyKnows what a thread isExecutors and CompletableFutureThread-safety reviews, contention, virtual threads
JVM and performanceReads a stack traceBasic heap and GC awarenessProfiles and fixes production issues
Data accessSimple JDBC or JPA useTransactions and query behaviorSpots N+1 queries, designs data access
TestingJUnit testsMockito and integration testsTest strategy across services

Pick the three or four rows that match the role. A platform team cares most about concurrency and the JVM, while a product team building REST services needs collections, data access and testing.

Sample Java test questions

Questions run from junior to senior. A balanced 45-minute test uses about six: two concept checks, two debugging questions and two scenarios.

1. With Integer a = 127, b = 127; the check a == b is true, but with 128 it's false. Why?

Junior, reasoning

What good looks like: autoboxing calls Integer.valueOf, which caches values from -128 to 127 by default. So 127 gives the same object twice, while 128 creates two objects. == compares references, so compare with equals() or use primitive int. A strong answer notes the cache can be widened with a JVM flag, which makes code that relies on it fragile.

2. Removing items from an ArrayList inside a for-each loop throws ConcurrentModificationException. Fix it.

Junior, debugging

What good looks like: the enhanced for loop uses an iterator, and the iterator detects that the list changed underneath it. Use list.removeIf(item -> item.isExpired()), or call remove() on an explicit Iterator. Strong candidates add that this check is best effort and is not a thread-safety guarantee.

3. What is the difference between a static nested class and an inner class, and how can an inner class leak memory?

Junior to mid, concept

What good looks like: an inner (non-static) class instance holds a hidden reference to the outer instance, and a static nested class doesn't. If an inner or anonymous class object, such as a listener or a task in a long-lived queue, outlives its outer object, it keeps that whole object reachable. Prefer static nested classes unless you need the outer state.

4. Java 21 added sequenced collections. What do they change?

Mid, concept

What good looks like: the new interfaces SequencedCollection, SequencedSet and SequencedMap give ordered types one common API: getFirst(), getLast(), addFirst(), addLast(), removeFirst(), removeLast() and reversed(), which returns a view. List, Deque, LinkedHashSet, LinkedHashMap and the sorted collections gain these methods. Before Java 21, reading the last element of a LinkedHashSet meant walking the whole set.

5. Sort employees by department, then by salary from highest to lowest, using a Comparator.

Mid, practical

What good looks like: Comparator.comparing(Employee::department).thenComparing(Employee::salary, Comparator.reverseOrder()). The trap is adding .reversed() at the end of the chain, which reverses the whole comparator, department included. thenComparing(Comparator.comparingInt(Employee::salary).reversed()) also works and avoids boxing.

6. A stream that builds a map with Collectors.toMap fails in production with IllegalStateException. What happened?

Mid, debugging

What good looks like: toMap throws on duplicate keys, and the production data held two records with the same key. The fix depends on intent: pass a merge function such as (a, b) -> b, or use groupingBy to keep every value. A strong candidate also knows toMap throws NullPointerException on null values.

7. When do you use thenApply and when thenCompose on a CompletableFuture?

Mid, concept

What good looks like: thenApply maps the result with a plain function. thenCompose chains a function that itself returns a CompletableFuture, flattening the result so you don't end up with a future of a future (like map vs flatMap). Errors go through exceptionally or handle. join() throws an unchecked CompletionException, while get() throws a checked ExecutionException.

8. Why is building a large string with += in a loop slow, and what do you use instead?

Mid, reasoning

What good looks like: strings are immutable, so each += creates a new string and copies everything so far, which is quadratic over the loop. Since Java 9, javac compiles a single concatenation expression with invokedynamic, which helps one-liners but not loops. Use a StringBuilder, String.join or Collectors.joining.

9. Why can new BigDecimal(0.1) surprise you, and why might two equal-looking BigDecimals not be equal?

Mid to senior, reasoning

What good looks like: new BigDecimal(0.1) captures the exact binary double, 0.1000000000000000055511151231257827 and more digits. Use BigDecimal.valueOf(0.1) or new BigDecimal("0.1"). equals() compares value and scale, so 2.0 and 2.00 aren't equal, while compareTo returns 0. That matters for HashSet keys and test assertions.

10. An endpoint that lists 50 orders runs 51 database queries. Diagnose and fix it.

Senior, practical scenario

What good looks like: this is the N+1 problem. JPA or Hibernate loads the orders, then lazily loads each order's customer or lines one query at a time. Confirm it with SQL logging or a query count in a test. Fixes include a fetch join, an entity graph, batch fetching or a DTO projection. Switching everything to eager fetching just moves the problem elsewhere.

11. A nightly job is slow, and a teammate wants to add .parallel() to its streams. What do you check first?

Senior, judgment

What good looks like: parallel streams run on the shared ForkJoinPool.commonPool(), sized from the core count by default. Blocking I/O inside them, such as database or HTTP calls, starves everything else that uses the pool. They help CPU-bound work on large data that splits cheaply (arrays, ArrayList) with no shared mutable state. Measure first, and for I/O-heavy work use virtual threads or a dedicated executor.

Test formats compared

FormatWhat it showsWeaknessBest for
Multiple choice quizKnowledge of language rules and APIsRewards memorized trivia; easy to searchA quick check on junior applicants
Take-home projectProject layout, tests, build files and code styleLong for the candidate; authorship is hard to confirmFinalists for mid roles, with a strict time cap
Live codingHow they reason and recover from a mistakeStressful; one engineer per sessionSenior roles where collaboration matters
Work sampleReal work, such as reviewing a pull request or fixing a slow queryNeeds upkeep as your codebase changesTeams with a well-defined daily workload
Adaptive AI interviewHow far a candidate's understanding goes, with follow-up on each answerNo full build; confirm it covers the Java topics you needScreening every applicant the same way, around the clock

A common pairing is an adaptive first round to find real depth, then a short work sample for finalists. Keep the rubric the same across both so scores mean the same thing.

Scoring rubric

ScoreWhat you see
1Answers are wrong or vague. Can't say why a collection or concurrency choice matters.
2Names the right API but misses the trap, such as reversing the whole comparator or ignoring duplicate keys.
3Correct, practical answers that fit modern Java. Explains trade-offs when asked.
4Raises failure modes and measurement unprompted, and connects the answer to production behavior.

Running a Java test with NTRVSTA

Java is in NTRVSTA's exam library. The live AI interviewer runs the test as a real-time conversation, by video avatar or audio, and changes difficulty after each answer. Someone who explains sequenced collections well gets a harder concurrency question next, and someone who stalls gets a simpler one or a hint. It follows up on the reasoning behind an answer, which is where Java depth shows. For senior roles, add a system-design question on the interactive whiteboard.

You get a score breakdown across technical skills, English proficiency and communication, plus video snippets and a shareable report for the hiring team. Pair this test with our Java interview questions for later rounds, compare specialist platforms in the best coding assessment tools, and see the software engineer job description or our Python skills test for related hiring.

Questions

How long should a Java assessment test take?

About 45 minutes for a first-round screen. If you add a take-home project, cap it at 3 hours and tell candidates the limit before they start.

Is a take-home or a live test better for Java roles?

Neither is better for every role. A take-home shows project structure and tests, while a live format shows reasoning and how someone takes feedback. Many teams screen live first and save the take-home for finalists.

How do I keep a Java test from favoring one framework?

Test core Java first: collections, concurrency, data access and debugging. Add framework questions only for the frameworks the role uses every day, and score them as a separate row so a strong engineer new to your framework is not filtered out.

Want to watch the AI interviewer run a Java round? Book a 20-minute demo, or start a sample interview.

NTRVSTA
‍