Java and OOP Fundamentals

Question 1: Why use private fields with public getters/setters?

Encapsulation hides internal state and enforces controlled access. Getters/setters let objects manage their own data while enabling validation, formatting, or side effects when data is accessed or modified.

Question 2: Abstract Class vs Interface

Abstract classes allow partial implementation and are used when there’s a strong hierarchical “is-a” relationship.

Interfaces define a contract that classes agree to implement, supporting multiple inheritance of behavior.

Use an interface when behavior is shared but classes aren’t tightly related.

Question 3: What is Polymorphism?

Polymorphism allows objects to take many forms. It supports:

  • Overloading – same method name, different parameters (compile time)
  • Overriding – subclass redefines behavior of parent method (runtime)
  • Dynamic dispatch – correct method is chosen at runtime based on actual object type

Question 4: equals() and hashCode()

If you override equals(), always override hashCode().

equals() checks logical state, while hashCode() enables efficient lookup in hash-based structures like HashMap or HashSet.

Failing to override both can cause incorrect behavior in collections.

Question 5: final Keyword

  • final variable → cannot be reassigned
  • final method → cannot be overridden
  • final class → cannot be subclassed

Question 6: Composition vs Inheritance

Use composition when one object “has-a” relationship with another (e.g., a Car has an Engine).

Use inheritance for an “is-a” relationship (e.g., Car is a Vehicle).

Composition promotes flexibility and loose coupling.

Question 7: this Keyword

this refers to the current instance.

Used to disambiguate between fields and parameters or pass the current object to another method or constructor.

Question 8: == vs .equals()

  • == compares references (memory addresses)
  • .equals() compares content/state (when overridden)

For objects like String, use .equals() for value comparison.

Question 9: Checked vs Unchecked Exceptions

  • Checked (e.g., IOException) – must be caught or declared
  • Unchecked (e.g., NullPointerException) – runtime-only, optional to catch

Checked exceptions represent recoverable errors; unchecked indicate programming bugs.

Question 10: static Keyword

static means the member belongs to the class, not an instance.

Used for shared constants, utility methods, or static inner classes.

Top-level classes cannot be static.

Question 11: Library System Design (Example)

Use a Library class to manage a List<Book> or Map<String, List<Book>> to support:

  • Adding/removing books
  • Searching by title (case-insensitive)
  • Handling duplicate titles by grouping them under the same key Normalize titles to lowercase for consistent lookups.

Question 12: What is Immutability?

An immutable class cannot have its internal state changed after creation.

To create one:

  • Use final fields
  • Make class final
  • No setters
  • Initialize via constructor
  • Return defensive copies of mutable fields

Java’s String class is immutable to support thread safety, caching, and security.

Question 13: Access Modifiers

ModifierSame ClassSame PackageSubclass (diff pkg)Everywhere
public
protected
default
private

Question 14: Reflection

Reflection allows runtime inspection and manipulation of classes, methods, fields, and constructors.

Useful for frameworks (e.g., Spring), serialization, and testing — but can break encapsulation and impact performance.

Question 15: Streams

Streams provide a declarative, functional-style API to process collections.

Support operations like filter, map, sorted, and collect.

  • Intermediate ops (lazy): map(), filter(), limit()
  • Terminal ops: collect(), forEach(), count(), findFirst()

Question 16: Lazy Evaluation

Stream operations are lazily evaluated — they’re only executed when a terminal operation triggers the pipeline.

This improves efficiency, enables short-circuiting, and avoids unnecessary work.

Question 17: Terminal Operations

Terminal operations are methods like collect(), findFirst(), and forEach() that trigger stream processing and return a result or cause a side effect.