Java Module 5
Classes, objects, records and enums interview questions
Interview questions on classes, objects, records and enums: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What does Java do when a class declares no constructor at all?
Answered in the lesson Classes, objects, fields and constructors
-
Fresher
What does a compact source file import automatically, and what must you add when you turn it into a named class?
Answered in the lesson Growing a compact file into classes and files
-
Fresher
What is the difference between package-private and protected access in Java?
-
Fresher
Can a static method use an instance field? Why or why not?
Answered in the lesson Static members and initialisation order
-
Fresher
What is the class path, and what is it when you set nothing?
-
Fresher
What is the contract between equals and hashCode in Java?
Answered in the lesson Java equals, hashCode and toString contracts
-
Fresher
What makes a Java class immutable?
Answered in the lesson Immutable classes and defensive copies in Java
-
Fresher
Why can you compare Java enum constants with == instead of equals?
Answered in the lesson Java enums with fields, methods and EnumMap
-
Fresher
What does the compiler generate for a Java record?
-
Fresher
What is the difference between a static nested class and an inner class in Java?
Answered in the lesson Java nested, inner, local and anonymous classes
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Fresher to mid
A class Account declares public void Account(String id, long balance) and assigns both fields in it. New code calling new Account("A1", 500) does not compile, and old code calling new Account() gets an account whose id is null. Explain what the compiler saw, and how you would stop this mistake reaching the main branch.
- What it tests
- Telling a constructor from a method, the default-constructor rule, reading a compiler message, and turning a silent mistake into a compile error or a failing check.
- First step
- Point at the void. A constructor has no return type, so void Account(...) is an ordinary method that happens to share the class's name. The class therefore declares no constructor, and the compiler added the default one, which explains both symptoms.
- Follow-ups
-
- Why did the compiler not warn about it?
- A teammate proposes keeping the method and calling it right after new Account(). What is wrong with that?
- How would a test have caught it?
From the lesson Classes, objects, fields and constructors
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to mid
Design the constructors of a Booking class: a passenger name and a seat count from 1 to 6 are required, and a coach preference is optional. How many constructors do you write, where does the validation live, and what can callers never create?
- What it tests
- Delegating with this(...), keeping every check in one constructor, class invariants, defaults that are values rather than null, and knowing when overloaded constructors stop scaling.
- First step
- Write one full constructor that takes every field and checks every rule, and make each shorter constructor delegate to it with this(...), passing a named default for what the caller left out. Then no way of creating a Booking can skip a check.
- Follow-ups
-
- Two more optional fields arrive next month. What changes?
- Booking now extends a Reservation class whose constructor needs a valid name. Where does the check go?
- Should a constructor or a static factory method create bookings?
From the lesson Classes, objects, fields and constructors
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
java Main.java works on your laptop, but the CI job, which compiles every .java file in the folder with javac, fails with errors in a file the program never uses. Why do the two disagree, and what do you change so that they agree?
- What it tests
- How the multi-file source launcher chooses files, how javac chooses them, and keeping local runs and CI builds consistent on purpose.
- First step
- The launcher compiles the file you name and then only the files whose classes the program uses, found by name. javac compiles every file the shell gives it, so an unused broken file only ever matters to javac.
- Follow-ups
-
- Should CI copy the launcher with --source-path?
- How do you keep experiments out of the build?
- What else differs between running from source and running compiled classes?
From the lesson Growing a compact file into classes and files
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A BankAccount class exposes getBalance() and setBalance(long), and code across the service does account.setBalance(account.getBalance() - amount). What can go wrong, and how do you redesign the class and move the callers over without a big-bang change?
- What it tests
- Invariants owned by the class, lost updates from read-modify-write, the audit trail an operation gives, and migrating a widely used API safely.
- First step
- Name the rule the setter cannot protect: the balance never goes below zero and never changes without a reason. Each caller now re-implements that rule, so one forgotten check, or two callers at the same moment, is enough to break it.
- Follow-ups
-
- Two threads withdraw at the same moment. What happens with the setter, and with withdraw()?
- Fifty call sites use setBalance. How do you move them over?
- Is a getter for the balance still fine?
From the lesson Encapsulation and access modifiers
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your team ships a library. A helper class is public only because two of your own packages need it, and other teams have started calling it directly. How do you hide it with Java's access levels, and when do you need the module system as well?
- What it tests
- What package access can and cannot do, designing packages around access, and module exports, including qualified exports and reflection.
- First step
- Ask why two packages need it. If they can become one package, the helper becomes package-private and the problem is gone. If not, public is the only access level that crosses packages, so only a module can stop outside code.
- Follow-ups
-
- How do you move the other teams off the helper?
- What does a qualified export do?
- Can reflection still reach the class?
From the lesson Encapsulation and access modifiers
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A class keeps a static HashMap that caches results by their input. In production, memory grows until the service is restarted; in tests, results from one test leak into the next. Explain both symptoms, and redesign the cache.
- What it tests
- The lifetime of static state, unbounded caches, thread safety of a shared map, test isolation, and giving a cache an owner, a size limit and an eviction rule.
- First step
- A static field lives as long as its class, which in a service means as long as the JVM. Nothing ever removes an entry, so the map grows with every new input, and every test in the same run shares the same map.
- Follow-ups
-
- How large may the cache grow, and how do you decide?
- Two threads fill the cache at the same time. What can break?
- How do you test code that uses the cache?
From the lesson Static members and initialisation order
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A base class used by four teams calls an overridable init() hook from its constructor. One team's subclass reads its own endpoint field in init() and sends it to a server, which receives null. Explain the failure, and redesign the base class without breaking the other three subclasses.
- What it tests
- The order of construction across a class hierarchy, dynamic dispatch while an object is being built, and evolving a base class that other teams extend.
- First step
- Follow the creation order: the subclass constructor begins with super(), so the base constructor runs before the subclass's field initialisers. Its call to init() is dispatched to the override, which reads the field while it still holds its default value, null.
- Follow-ups
-
- Would declaring the field final fix that subclass?
- How do Java 25 constructors help the subclass?
- How do you change the base class step by step without breaking the other subclasses?
From the lesson Static members and initialisation order
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to mid
Your program compiles, but at run time it stops with NoClassDefFoundError for a class from a library. What is different between compile time and run time, and how do you find and fix the cause?
- What it tests
- How javac and the JVM each find classes, telling NoClassDefFoundError from ClassNotFoundException, and checking the class path a program really runs with.
- First step
- Compile time and run time use separate class paths. javac found the library through its own -cp, but the java command that runs the program does not list that JAR, or lists another version of it. Look at the exact run command before changing any code.
- Follow-ups
-
- How is NoClassDefFoundError different from ClassNotFoundException?
- The JAR is on the class path and the error remains. What next?
- How does a build tool prevent this?
From the lesson Packages, imports and the class path
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Two JAR files on the class path both contain com.acme.util.Strings, in different versions. What does the JVM do, how would you notice, and how do you fix it?
- What it tests
- The search order of the class path, the errors a version clash produces, finding which JAR a class came from, and resolving conflicting dependencies.
- First step
- The class path is searched in order and the first Strings found is loaded; the other copy is never used and nothing warns about it. The symptom appears later, as an error such as NoSuchMethodError where code expects the other version.
- Follow-ups
-
- How do you tell which JAR a class was loaded from?
- Why does it work on one machine and fail on another?
- How do build tools decide which version to keep?
From the lesson Packages, imports and the class path
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A service keeps Order objects in a HashSet to drop duplicates. The set keeps growing, and contains() returns false for orders you can see in it when you print the set. What could cause this, and how do you find out which cause it is?
- What it tests
- How hash collections use hashCode and equals, telling an overload, a missing hashCode and a changed key apart by their symptoms, and fixing the class instead of its callers.
- First step
- Name the three causes that fit: equals(Order) overloading equals instead of overriding it, equals overridden without hashCode, and a field read by hashCode that changes after the order is added. Then read Order for @Override, for a hashCode method and for setters on the fields that equals compares.
- Follow-ups
-
- The printed set shows an order whose status differs from the one it was added with. Which cause is it?
- How do you clean up the orders that are already lost in the set?
- How do you stop this kind of bug from reaching production again?
From the lesson Java equals, hashCode and toString contracts
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A key class returns 42 from hashCode "to be safe". Every answer is still right, but a cache keyed by it got slower and slower as it grew. Explain why, estimate the work for 10,000 lookups, and say what changes if the key class implements Comparable.
- What it tests
- The difference between a legal and a useful hash code, how HashMap buckets and tree bins behave under total collision, and estimating cost from the number of keys.
- First step
- The contract only demands equal hash codes for equal objects, so 42 is legal. But every key now lands in one bucket, and a lookup has to compare its key with the entries there one by one, so the cost of one lookup grows with the size of the cache instead of staying constant.
- Follow-ups
-
- Why does Comparable help, and why is it still not enough?
- What hashCode would you write for a key with a String name and an int version?
- Could an attacker cause the same slowdown in a correct program?
From the lesson Java equals, hashCode and toString contracts
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to mid
In a code review, a Config class has private final Map<String, String> settings, assigned from the constructor argument, and a getSettings() that returns the field. The author says it is immutable because the field is final and there are no setters. Do you agree? What would you change, and what does each change cost?
- What it tests
- Telling a final reference from an immutable object, defensive copies on the way in and out, views against copies, and weighing copy cost against safety.
- First step
- Disagree: final only fixes which map the field refers to. The caller who passed the map can still change it, and so can every caller of getSettings(), so the object changes without a setter. Show it with a three-line test before proposing the fix.
- Follow-ups
-
- Why not return Collections.unmodifiableMap(settings) and keep the constructor as it is?
- The map holds 50,000 entries and Config objects are created per request. Is copying too slow?
- One value in the map is a mutable object, not a String. What changes?
From the lesson Immutable classes and defensive copies in Java
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A pricing service builds a new immutable PriceTable every minute and stores it in a plain static field; request threads read the field without locks. What exactly does the Java memory model guarantee the readers, what does it not guarantee, and what would you change?
- What it tests
- Final-field semantics, the difference between the safety of an object and the visibility of the reference to it, and safe publication with volatile or an atomic reference.
- First step
- Separate the two questions. Because PriceTable is immutable with final fields, a reader that sees a reference to it sees a fully built table. But the plain field gives no promise about when, or whether, a reader sees the newest reference at all.
- Follow-ups
-
- What happens if the PriceTable constructor registers this with a listener before it finishes?
- Is volatile enough, or do you need an AtomicReference?
- A reader uses two prices from the table in one calculation. Can it mix two tables?
From the lesson Immutable classes and defensive copies in Java
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
An orders table stores the status as the enum's ordinal(). A developer adds ON_HOLD between PLACED and PACKED and deploys. What breaks, how would you repair the existing rows, and what should the table store instead?
- What it tests
- What ordinal() is, why persisted values must not depend on declaration order, planning a data migration, and choosing a stable representation.
- First step
- ordinal() is the constant's position. Inserting ON_HOLD at position 1 moves PACKED, SHIPPED and the rest up by one, so every stored row from position 1 on is now read as the state before it, with no error anywhere.
- Follow-ups
-
- Some rows were written after the deploy and some before. How do you tell them apart?
- Is name() safe forever, or can it break too?
- The table must stay small. Is a short code better than the name?
From the lesson Java enums with fields, methods and EnumMap
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A shared library adds RETURNED to its public Status enum in a minor release. Services that upgraded the library without recompiling start failing with MatchException in code that has not changed. Explain the failure, and design how the library and its users should handle new constants.
- What it tests
- Exhaustive switch expressions, separate compilation and binary compatibility, and evolving a published enum without surprising its users.
- First step
- The services' switch expressions were checked as exhaustive against the old four constants. Code compiled then is not checked again, so when RETURNED reaches such a switch at run time no label applies, and the specification says that throws MatchException.
- Follow-ups
-
- Would adding a default branch to every switch have been better?
- How can the library ship the new constant without breaking anyone?
- What changes if Status is a sealed interface of records instead of an enum?
From the lesson Java enums with fields, methods and EnumMap
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
Your team wants to turn a widely used value class, with getters and hand-written equals and hashCode, into a record. Which behaviours can change for existing callers, and how would you check them before merging?
- What it tests
- Exactly what a record generates, which of those members differ from a typical hand-written class, and planning a change that many callers and stored data depend on.
- First step
- List what the record will generate and compare it member by member with the class: accessor names, which fields equals and hashCode read, the toString format, finality, constructors and setters, and how the object is serialized. Each difference is a possible break for somebody.
- Follow-ups
-
- The old equals ignored a lastUpdated field. What do you do with it?
- Logs are parsed by a tool that reads the old toString. What now?
- Some callers subclass the old class in tests. Can they keep doing that?
From the lesson Java records for immutable data
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
record Percentage(double value) rejects values below 0 or above 100 in its compact constructor. Tests pass, but in production a HashSet holds both Percentage[value=0.0] and Percentage[value=-0.0], and a NaN slipped through validation. Explain both, and fix the record.
- What it tests
- How a record compares primitive components, IEEE 754 signed zero and NaN, why comparisons with NaN are always false, and validating floating-point input.
- First step
- Look at the two comparisons separately. value < 0 is false for -0.0 and for NaN, so both pass the check. The generated equals compares a double component with Double.compare, which treats -0.0 and 0.0 as different, so the set keeps both.
- Follow-ups
-
- Should the record normalise -0.0 to 0.0, or reject it?
- Why does NaN equal NaN in the record, when NaN == NaN is false?
- Would a long number of basis points have been a better component?
From the lesson Java records for immutable data
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A desktop app's screens each register an inner-class listener with an event bus that lives as long as the app. After an hour of opening and closing screens, a heap dump shows hundreds of closed screens still in memory. Explain the leak, how you would confirm it, and two ways to fix it.
- What it tests
- The hidden reference from an inner object to its outer object, reading a reference chain in a heap dump, and fixing a leak through lifecycle or through the type of reference.
- First step
- Follow the chain from a garbage-collection root: the long-lived bus holds the listener, the listener is an inner object whose hidden field this$0 refers to its screen, and the screen holds its whole view tree. Nothing ever unregisters the listener, so every closed screen stays reachable.
- Follow-ups
-
- Would turning the listener into a lambda fix it?
- Should the bus hold listeners through weak references?
- How do you write a test that fails while the leak exists?
From the lesson Java nested, inner, local and anonymous classes
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to mid
You are writing a RingBuffer class and its iterator. Would you make the iterator an inner class or a static nested class, and how would you make it fail fast when the buffer changes while someone is iterating?
- What it tests
- When a nested class needs its enclosing object, iterator state per iteration, and the fail-fast pattern with a modification counter and ConcurrentModificationException.
- First step
- The iterator needs the buffer's array, head and size, and every iterator needs its own position, so an inner class that reads the outer fields and holds only its position is the natural fit. For fail-fast behaviour, count structural changes in the buffer and compare.
- Follow-ups
-
- Two threads iterate the same buffer at once. Is that safe?
- What should the iterator remove method do, and how does it avoid tripping the check?
- Is a static nested iterator with an explicit buffer field ever better?
From the lesson Java nested, inner, local and anonymous classes
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes