Java OOP Interview Questions and Answers With Examples

Java OOP interview questions with examples covering encapsulation, inheritance, polymorphism, abstraction, method dispatch and practical design choices.

Java OOP Interview Questions

These Java OOP interview questions cover encapsulation, inheritance, polymorphism and abstraction, then test the rules with short code examples. Start each answer with the distinction the interviewer asked for, and explain the behavior of the example rather than relying on a memorized definition.

1. Questions on OOP Core Concepts

1.1. What are the four major pillars of OOP?

The major pillars on which OOP relies are Encapsulation, Inheritance, Polymorphism, and Abstraction.

  • Encapsulation keeps an object’s representation behind methods that preserve its rules. private fields help, but public setters for every field do not automatically make the object safe.
  • Inheritance lets a subtype reuse and specialize a parent contract when the subtype truly has an IS-A relationship.
  • Polymorphism lets a caller use one interface while different implementations provide their own behavior; Java also supports method overloading at compile time.
  • Abstraction exposes the operations a caller needs while keeping implementation choices behind a class or interface.
Caller → Payment interface → CardPayment implementation
       → amount() contract → concrete amount

The caller sees the abstraction, while the implementation supplies its behavior. Inheritance and polymorphism let another payment type satisfy the same contract. Encapsulation keeps each implementation’s state behind its operations.

1.2. Is Java a Pure Object-Oriented Programming Language?

No. Java has primitive values such as int and boolean that are not objects. “Pure object oriented” is an informal label rather than a Java specification category; multiple class inheritance and operator overloading do not decide it.

int count = 3;                       // primitive value
Integer boxed = Integer.valueOf(3); // object
System.out.println(count == boxed); // true, after unboxing

The output follows because Java unboxes boxed for the comparison. That does not turn the primitive count into an object.

1.3. What Does Encapsulation Require Beyond Private Fields?

Private fields hide representation, but the public methods must also preserve the object’s rules. This account never exposes its balance for arbitrary assignment.

final class Account {
    private int balance;

    Account(int openingBalance) {
        if (openingBalance < 0) throw new IllegalArgumentException();
        balance = openingBalance;
    }

    void deposit(int amount) {
        if (amount <= 0) throw new IllegalArgumentException();
        balance = Math.addExact(balance, amount);
    }

    int balance() { return balance; }
}

The constructor and deposit() enforce the invariant that the balance cannot become negative through these operations. Making a field private while adding an unrestricted setter would lose that protection.

1.4. What Are Coupling and Cohesion?

Coupling describes how strongly one component depends on another. Cohesion describes whether a component’s responsibilities belong together. A report service that calculates totals and writes files has two reasons to change; separating them keeps the calculation focused and lets the output format vary.

interface ReportWriter { void write(String report); }

record ReportService(ReportWriter writer) {
    void publish(int total) {
        writer.write("Total: " + total);
    }
}

ReportService depends on a small contract. A PDF or text writer can implement it without changing the service. This is useful coupling; the goal is not to eliminate every dependency.

1.5. How Do Autoboxing and Unboxing Affect Overload Selection?

Autoboxing converts a primitive to its wrapper type; unboxing converts a wrapper to its primitive. Overload resolution considers applicable signatures at compile time. For an int argument, primitive widening to long is preferred over boxing to Integer.

class OverloadDemo {
    static String select(long value) { return "long"; }
    static String select(Integer value) { return "Integer"; }

    public static void main(String[] args) {
        System.out.println(select(10)); // long
    }
}

The literal has type int, so select(long) wins. An Integer variable instead selects select(Integer) without a conversion. Be careful when unboxing a nullable wrapper: it throws NullPointerException.

1.6. How Do Overloading and Overriding Select a Method?

Overload selection uses the compile-time types of arguments and the reference. Override dispatch uses the runtime class of the receiver after a signature has been selected.

class DispatchDemo {
    static class Parent {
        String show(int value) { return "parent int"; }
    }
    static class Child extends Parent {
        @Override String show(int value) { return "child int"; }
        String show(float value) { return "child float"; }
    }
    public static void main(String[] args) {
        Parent value = new Child();
        System.out.println(value.show(10)); // child int
    }
}

The Parent reference exposes show(int), so that signature is selected at compile time. The runtime Child override supplies the result. The Child.show(float) overload is not considered through the Parent reference.

1.7. What are widening/upcasting and narrowing/downcasting?

Distinguish primitive numeric conversions from reference conversions. A widening primitive conversion may be implicit yet lose precision, such as int to float. Upcasting usually refers to a widening reference conversion from a subtype to a supertype.

byte b = 10;
int x = b;  // widening primitive conversion

Narrowing primitive conversions can lose information. A reference downcast does not truncate an object: it checks whether the referenced object is compatible with the target type and can fail with ClassCastException.

For integer narrowing, high-order bits can be discarded. That description does not apply to every numeric cast or to reference downcasting. State which conversion the example performs.

double d = 50.52354;
int y = (int) d;

1.8. Why Does a Java Class Have Only One Direct Superclass?

Java permits one direct superclass but multiple implemented interfaces. Conflicting inherited default methods require an explicit resolution where the language rules demand it. This is more precise than saying Java has no multiple inheritance of any kind.

Two superclasses could supply conflicting instance state or implementations. Java avoids that ambiguity for classes. Interfaces can still supply multiple contracts, and the language defines rules for resolving conflicting default methods.

2. Questions on the Scope of Variables and Methods

2.1. What Are Formal and Actual Parameters?

The variables declared by a method are formal parameters. The expressions supplied at the call site are arguments, sometimes called actual parameters.

static int doubleValue(int number) { // number is the parameter
    return number * 2;
}

int result = doubleValue(4); // 4 is the argument

Java passes the argument’s value to the parameter. For an object, that value is a reference to the same object, but reassigning the parameter does not reassign the caller’s variable.

2.2. Can we declare formal parameters as final?

Yes. Formal parameters act as local variables of the method in which they are declared, and **final is the only modifier applicable to the local variables. **Note that we can’t re-assign value to any formal parameter declared as final.

public static void printMyName(final String name){
    // name = "Lokesh!"; //Compiler Error:final parameter name may not be assigned
}

2.3. Can a Constructor Invoke Both this() and super()?

One constructor cannot explicitly delegate to another constructor with this() and also explicitly call its superclass constructor with super(). The delegated constructor chain eventually reaches a constructor that calls super().

class Base { Base(int id) {} }

class Child extends Base {
    Child() { this(7); }
    Child(int id) { super(id); }
}

Creating new Child() calls Child(7), which invokes Base(7). Constructor invocation placement rules depend on the selected language level, but this one-invocation rule remains.

2.4. When Does Java Supply a Default Constructor?

If a class declares no constructor, the compiler supplies a no-argument constructor that calls a superclass constructor. If any constructor is declared, no default constructor is generated.

class DefaultConstructorDemo {
    static class Base { Base() {} }
    static class Child extends Base { }

    public static void main(String[] args) {
        Child child = new Child(); // uses Child's generated constructor
        System.out.println(child != null); // true
    }
}

The generated constructor’s access follows the class declaration rules. A superclass must have an accessible no-argument constructor for this example to compile.

2.5. Can we declare a final method inside an abstract class?

Yes. An abstract class can contain a concrete final method. Subclasses may inherit and call it according to its visibility, but cannot override it. It is not restricted to calls from the abstract class itself.

abstract class Car {

  public final void createEngine() {}
  public abstract void buildCar();
}

class MarutiCar extends Car {

  public void buildCar() {
    createEngine();
  }
}

2.6. Can we declare the abstract method inside a final class?

No, we can’t declare an abstract method inside a final class because a final class cannot be extended, and having an abstract method makes it incomplete.

In Java, if there is even one abstract method inside the class then we must mark the class as abstract, and the** final-abstract combination is illegal.**

final class MarutiCar { // CE: MarutiCar is not abstract and does not override abstract method mileage()
    public abstract int mileage();
}

2.7. Which Modifiers Can a Top-Level Class Use?

A top-level class can be public or package-private (no access modifier). It can also be abstract or final, but not both, because an abstract class needs subclasses and a final class forbids them. strictfp remains legal syntax, though it no longer changes floating-point evaluation in current Java.

public abstract class PaymentProcessor {
    public abstract void process();
}

final class LocalPaymentProcessor extends PaymentProcessor {
    @Override public void process() { System.out.println("processed"); }
}

Only the public class has to share its name with the source file. The package-private class is accessible within the same package.

2.8. What are the modifiers applicable for local variables?

The only applicable modifier for the local variable is final.

final double tax;

2.9. Can an Overriding Method Be final?

Yes. An override may be marked final to stop a further subclass from overriding that method.

class Parent { String name() { return "parent"; } }
class Child extends Parent {
    @Override final String name() { return "child"; }
}

A subclass of Child may inherit name(), but cannot override it. A final method in the parent cannot be overridden in the first place.

3. Questions on Method Overloading and Overriding

3.1. How Do Method Hiding and Method Overriding Differ?

An instance method override is selected from the runtime object’s class. A static method with the same signature in a subclass hides the parent’s method and is selected from the reference type.

class Parent {
    String instance() { return "parent"; }
    static String kind() { return "parent"; }
}
class Child extends Parent {
    @Override String instance() { return "child"; }
    static String kind() { return "child"; }
}
Parent value = new Child();
System.out.println(value.instance()); // child
System.out.println(value.kind());     // parent

Call static methods through their class names in production code to make that selection obvious.

3.2. Can we overload the main() method?

Yes, main can be overloaded like another method. The launcher recognizes more than one permitted main form in current Java. State the language level before claiming that only the traditional public static void main(String[]) form can launch.

public class MainMethodOverloading {  

    public static void main(String[] args){    //JVM will invoke this when program is run
        System.out.println("String[] argument method");
    }

    public static void main(int[] args){
        System.out.println("int[] argument method");
    }
}

3.3. Can Fields Be Overridden?

No. A subclass can hide a field with the same name, but field access is selected from the reference’s compile-time type. Method overrides behave differently.

class FieldDemo {
    static class Parent { int count = 1; }
    static class Child extends Parent { int count = 2; }
    public static void main(String[] args) {
        Parent value = new Child();
        System.out.println(value.count);         // 1
        System.out.println(((Child) value).count); // 2
    }
}

Use methods rather than hidden fields when a caller needs behavior that varies with the runtime subtype.

3.4. Can a Method Have the Same Name as Its Class?

Yes. A constructor has no return type; a declaration with a return type is a method even when its name matches the class.

class Application {
    Application() { }
    void Application() { System.out.println("method"); }
}

The void Application() declaration is a method. Although valid, a descriptive method name is clearer.

3.5. Can an Abstract Override Replace a Concrete Parent Method?

Yes. An abstract subclass can redeclare an inherited concrete method as abstract, requiring its concrete descendants to supply a new implementation.

class AbstractOverrideDemo {
    static class Parent {
        String name() { return "parent"; }
    }
    static abstract class Middle extends Parent {
        @Override abstract String name();
    }
    static class Child extends Middle {
        @Override String name() { return "child"; }
    }
    public static void main(String[] args) {
        Parent value = new Child();
        System.out.println(value.name()); // child
    }
}

The middle class removes the inherited implementation from its own contract. A concrete child must implement name() before it can be instantiated.

3.6. Can an Override Reduce a Method’s Visibility?

No. An overriding method cannot be less accessible than the method it replaces. A public parent method must stay public in the subclass.

class Parent { public void run() {} }
class Child extends Parent {
    @Override public void run() { System.out.println("running"); }
}

Changing Child.run() to protected would fail compilation because callers promised access through Parent would lose it.

4. Difference between

4.1. How Do IS-A and HAS-A Relationships Differ?

Inheritance expresses an IS-A relationship: a CardPayment is a Payment. Composition expresses a HAS-A relationship: a Checkout has a Payment collaborator.

CardPayment ──implements──> Payment
Checkout    ──contains────> Payment

Use inheritance when the subtype really honors the parent contract. Use composition when one object needs another object’s service without becoming that type.

4.2. How Do == and equals() Differ for Objects?

For object references, == asks whether both references point to the same object. equals() asks whether the objects are equal under the class’s equality contract. String overrides equals() to compare character content.

String first = new String("Java");
String second = new String("Java");
String alias = first;
System.out.println(first == second);      // false
System.out.println(first == alias);       // true
System.out.println(first.equals(second)); // true

For primitive operands, == compares their values. The default Object.equals() uses reference identity, but classes can override it; equal objects must also have equal hash codes.

4.3. How Do static and Instance Fields Differ?

A static field belongs to a class and is shared by all of its instances. An instance field belongs to each object separately.

class StaticFieldDemo {
    static class Counter {
        static int created;
        int value;
        Counter(int value) { this.value = value; created++; }
    }

    public static void main(String[] args) {
        Counter first = new Counter(2);
        Counter second = new Counter(5);
        System.out.println(Counter.created); // 2
        System.out.println(first.value);      // 2
        System.out.println(second.value);     // 5
    }
}

The shared counter changes when either object is created. In concurrent code, updates to a mutable static field still need an appropriate synchronization rule.

5.1. Is this a valid method overloading?

public class Test{

    public void m1(int x){
        System.out.println("void m1 method");
    }

    public int m1(int x){
        System.out.println("int m1 method");
    }
}

No, we can’t overload a method just by changing the method’s return type. To overload a method, the method name must be the same, and the parameter to that method must be different. Hence we’ll get compile time error in the above code.

Test.java:7: error: method m1(int) is already defined in class Test

5.2. Is this valid method overriding?

class Parent{
    private void m1(){
        System.out.println("Parent class m1");
    }
}
public class Test extends Parent{
    protected void m1(){
        System.out.println("Child Class m1");
    }
}

No, the overriding is not done on private methods as they are not available to the child class through inheritance. We will not get any compile time error in the above code but it’s not method overriding.

class Parent{
    public void m1() throws InterruptedException{
        System.out.println("Parent class m1");
    }
}
public class Test extends Parent{
    public void m1() throws Exception{
        System.out.println("Child class m1");
    }
}

An overriding method cannot declare broader checked exceptions than the inherited contract permits. Unchecked exceptions are not subject to that restriction. Separate exception compatibility from whether the parent method is accessible and actually overridden.

Test.java:7: error: m1() in Test cannot override m1() in Parent

5.3. Can a Class Implement Both Interfaces With Conflicting Return Types?

No. These interfaces have the same method name and parameter list but incompatible return types. A class cannot provide one method that satisfies both contracts.

interface A { void m1(); }
interface B { int m1(); }
// class Both implements A, B { } // cannot implement both m1() contracts

Java does allow a class to implement two interfaces whose same-signature methods have compatible return types. Conflicting default implementations may require an explicit override.

5.4. Why does the following code not generate NullPointerException at run time?

class Name{
    static void m1(){
        System.out.println("Name class m1");
    }
}
public class Test{
    public static void main(String[] asrg){
        Name n = null;
        n.m1();
    }
}

JVM does not need an instance created to invoke the static members of a class. The JVM invokes static members through the class type. In the following example, n.m1() will be treated as Name.m1(), and thus no NullPointerException.

6. Conclusion

For each OOP answer, distinguish compile-time selection, runtime dispatch and visibility. Be ready to predict the shown code, identify a relevant version rule and explain how that rule affects a design you would actually use.

Happy Learning!

Leave a Comment

  1. class ChildLevelOne extends Parent {
    public abstract void method();
    }. this is wrong we have to make the class as abstract if we are defining the abstract method

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.