Java OOPS Concepts Interview Questions and Answers for Beginners and Experienced

In this article, we will discuss important Java OOPS (Object Oriented Programming) interview questions and answers for freshers as well as experienced candidates.

Is Java a pure object-oriented programming language?

There are a lot of arguments around whether Java is purely object-oriented or not. Well, Java is not a pure OOP language due to two reasons:

The first reason is that the Object-oriented programming language should only have objects whereas Java contains 8 primitive data types char, boolean, byte, short, int, long, float, and double which are not objects. These primitive data types can be used without the use of any object. 
For example:
public class IsJavaFullyOOPS {
    public static void main(String[] args) {
        int i = 10;
        byte b = 20;
        short s = 30;
        long l = 100L;
        double d = 100.10;
        float f = 200f;
        boolean flag = true;
        char c = 'R';
        System.out.println(i);
        System.out.println(b);
        System.out.println(s);
        System.out.println(l);
        System.out.println(d);
        System.out.println(l);
        System.out.println(d);
        System.out.println(f);
        System.out.println(flag);
        System.out.println(c);
    }
}
Output:
10
20
30
100
100.1
100
100.1
200.0
true
R
The second reason is related to the static keyword. In a pure object-oriented language, we should access everything by message passing (through objects). But Java contains static variables and methods that can be accessed directly without using objects. 

For example:
public class IsJavaFullyOOPS {
    private static String message = "hello";
    public static void main(String[] args) {
        
        // calling message instance variable without object
        System.out.println(message);
        
        // calling demo static method without object
        demo();
    }
    
    private static String demo(){
        return "hello from method";
    }
}

What are Core OOPS Concepts?

Core OOPS concepts:
Advanced OOPS concepts:

What is Method Overloading in OOP (Java)?

In Java, if the class has two or more methods having the same name but different parameters, this process is known as Method Overloading.

Here is a simple example that illustrates method overloading:
package com.javaguides.corejava.basics.polymorphism;

public class MethodOverloading {
    public static void main(String[] args) {
        OverloadDemo ob = new OverloadDemo();
        double result; // call all versions of test()
        ob.test();
        ob.test(10);
        ob.test(10, 20);
        result = ob.test(123.25);
        System.out.println("Result of ob.test(123.25): " + result);
    }
}

// Demonstrate method overloading.
class OverloadDemo {
    void test() {
        System.out.println("No parameters");
    }

    // Overload test for one integer parameter.
    void test(int a) {
        System.out.println("a: " + a);
    }

    // Overload test for two integer parameters.
    void test(int a, int b) {
        System.out.println("a and b: " + a + " " + b);
    }

    // Overload test for a double parameter
    double test(double a) {
        System.out.println("double a: " + a);
        return a * a;
    }
}
Output:
No parameters
a: 10
a and b: 10 20
double a: 123.25
Result of ob.test(123.25): 15190.5625
The compiler will resolve the call to a correct overloaded method depending on the actual number and/or types of the passed parameters. We can use Method overloading to achieve Java compile-time polymorphism.
The main advantage of method overloading increases the readability of the program.
There are four main rules that govern method overloading:
1. Method overloading is accomplished by changing the method parameters
2. Return type is not a part of method signature
3. We can overload private, static and final methods
4. We overload method within the same class.
A famous example of method overloading is System.out.println()

The println() has several overloaded methods. For example:
public void println() {
          // logic goes here
}

public void println(String x) {
   // logic goes here
}

public void println(boolean x) {
   // logic goes here
}

public void println(int x) {
   // logic goes here
}
public void println(char x) {
 // logic goes here
}
Read more about method overloading at Method Overloading in Java

What is Method Overriding in OOP (Java)?

In a Java class hierarchy, when a method in a subclass has the same name and type signature as a method in its superclass, then the method in the subclass is said to override the method in the superclass. Method overriding can be used in the presence of Inheritance or Runtime Polymorphism.

Java decided at the runtime the actual method that should be called depending upon the type of object we create.  For example:
public class MethodOverriding {
    public static void main(String args[]) {
        SuperClass superClass = new SubClass();
        superClass.method1("hello");
    }
}

class SuperClass {
    void method1(String message) {
        System.out.println(message);
    }
}
class SubClass extends SuperClass {
    void method1(String message) {
        message = message + " in SubClass";
        System.out.println(message);
    }
}
Output:
hello in SubClass
Another example:
class Shape {
    void draw() {
        System.out.println("drawing...");
    }
}
class Rectangle extends Shape {
    void draw() {
        System.out.println("drawing rectangle...");
    }
}
class Circle extends Shape {
    void draw() {
        System.out.println("drawing circle...");
    }
}
class Triangle extends Shape {
    void draw() {
        System.out.println("drawing triangle...");
    }
}
class TestPolymorphism2 {
    public static void main(String args[]) {
        Shape s;
        s = new Rectangle();
        s.draw();
        s = new Circle();
        s.draw();
        s = new Triangle();
        s.draw();
    }
}
Output:
drawing rectangle...
drawing circle...
drawing triangle...
Here are the main rules that govern method overriding:
1. The name and signature of the method are the same in the superclass and subclass, or in the interface and its implementations.
2. We can't override a method in the same class (but we can overload it in the same class)
3. We can't override private, static, and final methods
4. The overriding method cannot reduce the accessibility of the overridden method, but the opposite is possible
5. The overriding method cannot throw checked exceptions that are higher in the exception hierarchy than the checked exception thrown by the overridden method
6. Always use the @Override annotation for the overriding method. 

How can the superclass overridden method be called from the subclass overriding method?

We can call the superclass overridden method from the subclass overriding method via the Java super keyword.

For example:

// Method overriding.
class A {
	int i, j;
	A(int a, int b) {
		i = a;
		j = b;
	}
	// display i and j
	void show() {
		System.out.println("i and j: " + i + " " + j);
	}
}

class B extends A {
	int k;
	B(int a, int b, int c) {
		super(a, b);
		k = c;
	}
	void show() {
		super.show(); // this calls A's show()
		System.out.println("k: " + k);
	}
}

Output:

i and j: 1 2
k: 3
Here, super.show( ) calls the superclass version of show( ).

Can we override or overload the main() method?

As we know that the main() method is static so we can overload it but we can't override it.
Because the static methods are resolved at compile-time, while the methods that we can override are resolved at runtime depending upon the type of the object.

The below diagram demonstrates that the main() method can be overloaded:

Can we override a non-static method as static in Java?

No. we cannot override a non-static method as a static one. If we try then it leads to a compilation error:

Can you achieve Runtime Polymorphism by data members?

No, we cannot achieve runtime polymorphism by data members. The method is overridden not the data members, so runtime polymorphism can not be achieved by data members.

In the example given below, both the classes have a data member speedlimit, we are accessing the data member by the reference variable of the Parent class which refers to the subclass object. Since we are accessing the data member which is not overridden, hence it will access the data member of the Parent class always.

Runtime polymorphism can't be achieved by data members
class Bike {
    int speedlimit = 90;
}
class Honda extends Bike {
    int speedlimit = 150;

    public static void main(String args[]) {
        Bike obj = new Honda();
        System.out.println(obj.speedlimit); //90  
    }
}
Output:
90

What is Abstraction and give a real-world example?

Abstraction exposes to the user only those things that are relevant to them and hides the remainder of the details. In OOP terms, we say that an object should expose to the users only a set of high-level; operations, while the internal implementation of those operations is hidden.

Abstraction is achieved in Java via abstract classes and interfaces

Let's consider a real-life example: a man driving a car. The man knows what each pedal does and what the steering wheel does, but he doesn't know how these things are done internally by the car. He doesn't know about the inner mechanisms that empower these things. This is known as abstraction.


Code example: Let's create a Car interface with high-level methods:
public interface Car {

    public void speedUp();

    public void slowDown();

    public void turnRight();

    public void turnLeft();
    
    public String getCarType();
}
Next, each type of car should implement the Car interface and override these methods to provide the implementation of these methods. This implementation is hidden from the user.

For example, here is the ElectricCar class implementation:
public class ElectricCar implements Car {
    
    private final String carType;

    public ElectricCar(String carType) {
        this.carType = carType;
    }        

    @Override
    public void speedUp() {
        System.out.println("Speed up the electric car");
    }

    @Override
    public void slowDown() {
        System.out.println("Slow down the electric car");
    }

    @Override
    public void turnRight() {
        System.out.println("Turn right the electric car");
    }

    @Override
    public void turnLeft() {
        System.out.println("Turn left the electric car");
    }

    @Override
    public String getCarType() {
        return this.carType;
    }        
}
The user of this class has access to these public methods without being aware of the implementation:
public class Main {

    public static void main(String[] args) {

        Car electricCar = new ElectricCar("BMW");

        System.out.println("Driving the electric car: " + electricCar.getCarType() + "\n");
        electricCar.speedUp();
        electricCar.turnLeft();
        electricCar.slowDown();
    }
}
So, this was an example of abstraction via an interface.

What is Encapsulation and give a real-world example?

Encapsulation refers to combining data and associated functions as a single unit. In OOP, data and functions operating on that data are combined together to form a single unit, which is referred to as a class.
Encapsulation is the technique whereby the object's state is hidden from the outer world and a set of public methods for accessing this state are exposed. When each object keeps its state private inside a class, we can say that encapsulation was achieved. This is why encapsulation is also referenced as the data-hiding mechanism.
Encapsulation has a number of important advantages such as loosely coupled, reusable, secure, and easy-to-test code.
In Java, encapsulation is implemented via the access modifiers - public, private, and protected.
Let's consider an example: a Cat class can have its state represented by fields such as mood, hungry, and energy. While the code external to the Cat class cannot modify any of these fields directly, it can call public methods play(), feed(), and sleep() that modify the Cat state internally.
The Cat class may also have private methods that are not accessible outside the class, such as meow(). This is encapsulation.
public class Cat {

    private int mood = 50;
    private int hungry = 50;
    private int energy = 50;

    public void sleep() {
        System.out.println("Sleep ...");
        energy++;
        hungry++;
    }

    public void play() {
        System.out.println("Play ...");
        mood++;
        energy--;
        meow();
    }

    public void feed() {
        System.out.println("Feed ...");
        hungry--;
        mood++;
        meow();
    }

    private void meow() {
        System.out.println("Meow!");
    }

    public int getMood() {
        return mood;
    }

    public int getHungry() {
        return hungry;
    }

    public int getEnergy() {
        return energy;
    }
}
The only way to modify the state (private fields) is via the public methods play(), feed(), and sleep(), as in the following example:
public class Main {

    public static void main(String[] args) {

        Cat cat = new Cat();
        
        cat.feed();
        cat.play();
        cat.feed();
        cat.sleep();
        
        System.out.println("Energy: " + cat.getEnergy());
        System.out.println("Mood: " + cat.getMood());
        System.out.println("Hungry: " + cat.getHungry());
    }
}
The output will be as follows:

Feed ...
Meow!
Play ...
Meow!
Feed ...
Meow!
Sleep ...
Energy: 50
Mood: 53
Hungry: 49

What is Inheritance?

Inheritance is the process by which one object acquires the properties of another object.

Inheritance - IS-A relationship between a superclass and its subclasses. The process where one object acquires the properties of another object plus it can have its own.

Let's consider a Dog, the Dog (subclass) is a type of Animal (superclass). So Dog can inherit (reuse) members of the Animal class; plus it can have its own new behavior and properties.

In the Java library, you can see extensive use of inheritance. The below figure shows a partial inheritance hierarchy from java.lang library. The Number class abstracts various numerical (reference) types such as Byte, Integer, Float, Double, Short, and BigDecimal.

What is Composition?

A Composition is an association that represents a part of a whole relationship where a part cannot exist without a whole. If a whole is deleted then all parts are deleted. It has a stronger relationship.


Read more about Composition with a complete example at Composition in Java with Example.

What is Aggregation?

Aggregation is an association that represents a part of a whole relationship where a part can exist without a whole. It has a weaker relationship.
It is a specialized form of Association where all object has their own lifecycle but there is ownership. This represents a “whole-part or a-part-of” relationship.
Let’s take an example of the relationship between the Department and the Teacher. A Teacher may belong to multiple departments. Hence Teacher is a part of multiple departments. But if we delete a Department then the Teacher Object will not destroy.

What is a SOLID OOPS Principle?

SOLID is one of the most popular sets of design principles in object-oriented software development. It’s a mnemonic acronym for the following five design principles:
  • Single Responsibility Principle - A class should have only one reason to change
  • Open/Closed Principle - Software entities like classes, modules, and functions should be open for extension but closed for modifications.
  • Liskov Substitution Principle - Derived types must be completely substitutable for their base types.
  • Interface Segregation Principle - The Interface Segregation Principle states that clients should not be forced to implement interfaces they don't use. ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.
  • Dependency Inversion Principle - High-level modules should not depend on low-level modules. Both should depend on abstractions.

What is the Single Responsibility Principle?

"A class should have only one reason to change". Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. There should never be more than one reason for a class to change.
Read more about the Single Responsibility Principle with an example at Single Responsibility Principle.

What is the Open-Closed Principle?

Software entities like classes, modules, and functions should be open for extension but closed for modifications.
Read more about Open Closed Principle with an example at Open Closed Principle

What is the Liskov Substitution Principle?

Derived types must be completely substitutable for their base types.
Read more about Liskov Substitution Principle with an example of Liskov's Substitution Principle

What is the Interface Segregation Principle?

The Interface Segregation Principle states that clients should not be forced to implement interfaces they don't use. ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.
Read more about Interface Segregation Principle with an example at  Interface Segregation Principle

What is the Dependency Inversion Principle?

Entities must depend on abstractions, not on concretions. It states that the high-level module must not depend on the low-level module, but should depend on abstractions.

Comments