OOPS Concepts in Java with Examples

In this article, we will discuss the OOPS concepts in Java with examples. We will discuss different OOPS concepts in Java with examples like Classes, Objects, and Constructors, and we will majorly focus on the 4 pillars of OOPS. So, let’s get started by knowing the meaning of OOPS i.e. Object-Oriented Programming Systems.

What is OOPS?

Object-Oriented Programming is a paradigm in programming that deals with Objects and Classes. We will discuss the Objects and Classes later in this article. For now, we can say that Object-Oriented Programming is a type of programming that makes programming a lot easier by focusing on real-world entities (called objects).

Also, OOPS has 4 pillars namely:

  1. Abstraction
  2. Encapsulation
  3. Inheritance
  4. Polymorphism

These 4 pillars help in making programming in Java easy and the programs reusable. So, we will learn more about OOPS and its importance and we dive into the OOPS concepts.

All the OOPS Concepts in Java with Examples

Let’s get started with Objects and Class.

Objects

Objects are real-world entities. This means that Objects are something that has a state and behavior. Here, by state we mean its properties for instance color, size, etc, and by behavior we mean its actions or the functions that it performs like walking, talking, etc.

For instance, we can say that a car is an object. This is because it will have states (properties) like model name, brand name, color, and type (SUV or Sidan, etc.), etc. It will also have behavior or functions like accelerating (a car can accelerate), applying brakes (while stopping), etc.

Another example can be a pen. It also has states like its color, brand, etc. It also has functions like writing, scribbling, etc.

So, we can say that any real-world entity is an Object.

Classes

A class in OOPS is a “Collection of Objects”. So, a class is not a physical entity. It is a logical entity. For instance, a Student is a class however a particular student with some name and unique roll number is an object.

An Object is an instance of a Class. For example, a student with the name “Guneet” and roll number “1498” is an instance of the class “Student”. Similarly, a car with the name “Honda City” and the number “DL6CAP8790” is an instance of the class “Car”.

We can say that a Class is a blueprint from which Objects can be created. It is a collection of properties (or states like size, color, etc.) and behavior (or functions like write, read, walk, talk, etc) and the different objects have different values of these properties and all of them or some of them perform some or all of the functions listed in a class.

Let us now learn how to create a class and how to create objects from the class in Java.

How to Create a Class and its Objects in Java

The following program shows the method to create a Class and further create its objects in Java.

import java.util.*;

class Student {
    String name;
    int marks;
    int rollNo;
    
    void setData(String sName,int sMarks,int sRollNo) {
        name = sName;
        marks = sMarks;
        rollNo = sRollNo;
    }
    
    void displayData() {
        System.out.println("I am " + name + " and my rollNo is " + rollNo + ". I got " + marks + " marks out of 100.");
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setData("Guneet",95,2);
        s1.displayData();
    }    
}

So, as you can see in the program above, a class Student is created and it has the functions “setData” and “displayData”. Also, the Student class has properties called name, marks, and rollNo. These properties are called data members and the functions are called member functions or methods in Java.

Now that we know about Classes and Objects, let us learn about constructors.

Constructors in OOPS in Java

A constructor is a method that has the same name as that of the class. It is called automatically when an Object of the Class is created. For instance, in the above example, consider the following code that we have written.

Here, Student() is the constructor being called as soon as we create the object s1. However, we have not written any such method in class. This means that Java provides us with a default constructor of its own for every class. So, if we don’t create a constructor, it will be present by default. However, we can also create our own constructor, and then the constructor provided by Java will be revoked from us. An example of creating our own constructor in the above Student class is shown below.

Program to Create your own Constructor in Java

import java.util.*;

class Student {
    String name;
    int marks;
    int rollNo;
    
    Student() {
        System.out.println("An object of Student class is created.");
    }
    
    void setData(String sName,int sMarks,int sRollNo) {
        name = sName;
        marks = sMarks;
        rollNo = sRollNo;
    }
    
    void displayData() {
        System.out.println("I am " + name + " and my rollNo is " + rollNo + ". I got " + marks + " marks out of 100.");
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setData("Guneet",95,2);
        s1.displayData();
    }    
}

As you can see from the program shown above, a constructor does not have any return type.

The constructors in Java can be of 3 types. They are as follows.

1. Default Constructor: A constructor that does not have any parameters is a default constructor. Such a constructor is already provided by Java to every class if we don’t create it on our own. An example is the above Student() constructor program.

2. Parameterized Constructor: As the name suggests, if the constructor has parameters, it is called a parameterized constructor. This type of constructor is also used to initialize an object with parameters. For instance, instead of creating a setData() method in the above Student class, we can have a parameterized constructor and can create an object directly using it as shown below.

Parameterized Constructor Example

import java.util.*;

class Student {
    String name;
    int marks;
    int rollNo;
    
    Student() {
        System.out.println("An object of Student class is created.");
    }
    
    Student(String name, int marks, int rollNo) {
        this.name = name;
        this.marks = marks;
        this.rollNo = rollNo;
    }
    
    void displayData() {
        System.out.println("I am " + name + " and my rollNo is " + rollNo + ". I got " + marks + " marks out of 100.");
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("Guneet",95,2);
        s1.displayData();
    }    
}

As you can see, we have used the keyword “this”. The “this” keyword is used for avoiding the namespace collision (parameters and data members have the same name) as shown above. It is a pointer to the object itself i.e. a self-referential pointer.

3. Copy Constructor: A copy constructor is used to copy an object. For example, let us say we create a new Student s2, and want to copy the data of s1 to s2, however, we only want to copy the name and marks because the roll numbers of 2 students can’t be the same. For that, we can use copy constructor as shown below.

Copy Constructor Example

import java.util.*;

class Student {
    String name;
    int marks;
    int rollNo;
    
    Student() {
        System.out.println("An object of Student class is created.");
    }
    
    Student(String name, int marks, int rollNo) {
        this.name = name;
        this.marks = marks;
        this.rollNo = rollNo;
    }
    
    Student(Student other) {
        this.name = other.name;
        this.marks = other.marks;
    }
    
    void displayData() {
        System.out.println("I am " + name + " and my rollNo is " + rollNo + ". I got " + marks + " marks out of 100.");
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("Guneet",95,2);
        s1.displayData();
        Student s2 = new Student(s1);
        s2.rollNo = 1;
        s2.displayData();
    }    
}

So, we now have sufficient knowledge of Classes, Objects, and Constructors to start discussing the 4 pillars of OOPS. So, let’s get started with it.

4 Pillars of Object-Oriented Programming (OOPs)

There are 4 pillars of Object-Oriented Programming namely Encapsulation, Abstraction, Inheritance, and Polymorphism. Let us discuss each one of them in detail.

Encapsulation

The concept of encapsulation is something that we have already encountered but not yet discussed. Consider the class Student that we created above. You can see that there are some data members (name, marks, and roll number) and some methods (constructors, setData, and displayData) all wrapped into a single entity called class Student.

This is what encapsulation is. So, encapsulation refers to binding (or encapsulating or wrapping) data (data members) and code (methods) together into a single entity like a capsule.

So, we can say that encapsulation is implemented in Java OOPS using Java classes.

Inheritance

Inheritance is the next and very important pillar of Object-Oriented Programming in Java. It is a real-world concept. Like a child inherits properties and behaviors from his/her parents, similarly, we can have child classes (also known as subclasses) that inherit from their parent class (also known as Super Class).

For instance, let us consider a class Car and a class FlyingCar. Now, in simple terms also, we can guess that a flying car is also a car that can fly. So, it will have all the basic functionalities of a car and additional functionality that it can fly. So, the class FlyingCar will inherit from the class Car. In Java, we use the “extends” keyword to inherit from a class as shown below.

Inheritance Example

import java.util.*;

class Car {
    String name;
    String color;
    int engineHP; //engine Horse Power
    
    Car(String name, String color,int engineHP) {
        this.name = name;
        this.color = color;
        this.engineHP = engineHP;
    }
    
    public void drive() {
       System.out.println("Car is driving");
    }
    
    public void reverse() {
        System.out.println("Car is in reverse mode");
    }
}

class FlyingCar extends Car {
    
    FlyingCar(String name, String color, int engineHP) {
        super(name,color,engineHP);
    }
    
    public void fly() {
        System.out.println("Flying Car is flying");
    }
}

public class Main {
    public static void main(String[] args) {
        FlyingCar fc = new FlyingCar("SuperFly","Black",5000);
        fc.drive();
        fc.reverse();
        fc.fly();
    }
}

In the program above, you can see that we used the keyword “super”. This keyword can be used in Java to call the methods or use the constructors or data members of the superclass (parent class). To call the parameterized constructor of the superclass we just used super(name,color,engineHP).

Inheritance can be of multiple types. Let us learn about them.

1. Single Inheritance: When one subclass (child class) inherits from one superclass (parent class) such an inheritance is called single inheritance. We saw the above example of Car and FlyingCar where one superclass Car and one subclass FlyingCar were used.

2. Multilevel Inheritance: In multilevel inheritance, there can be multiple generations inheriting from each other. For instance, class Car will be the superclass. Now, from the class Car, a class FlyingCar inherits and a class HydroFlyingCar inherits from FyingCar. So, a car is simply a car and a flying car is the advanced version of a car that can fly and the hydro-flying car is the ultimate version of a car, and the advanced version of the flying car that can drive on road, fly in the air and also traverse in water. This is shown below.

Multilevel Inheritance Example

import java.util.*;

class Car {
    String name;
    String color;
    int engineHP; //engine Horse Power
    
    Car(String name, String color,int engineHP) {
        this.name = name;
        this.color = color;
        this.engineHP = engineHP;
    }
    
    public void drive() {
       System.out.println("Car is driving");
    }
    
    public void reverse() {
        System.out.println("Car is in reverse mode");
    }
}

class FlyingCar extends Car {
    
    FlyingCar(String name, String color, int engineHP) {
        super(name,color,engineHP);
    }
    
    public void fly() {
        System.out.println("Flying Car is flying");
    }
}

class HydroFlyingCar extends FlyingCar {
    HydroFlyingCar(String name, String color, int engineHP) {
        super(name,color,engineHP);
    }
    
    public void driveInWater() {
        System.out.println("Hydro-flying car driving on the surface of the water");
    }
}

public class Main {
    public static void main(String[] args) {
        HydroFlyingCar hfc = new HydroFlyingCar("SuperFlySwim","Black",5000);
        hfc.drive();
        hfc.reverse();
        hfc.fly();
        hfc.driveInWater();
    }
}

3. Hybrid Inheritance: Consider the class Car. Now a car FlyingCar extends the class Car means that it has all the abilities of a Car and the additional ability is that it can fly. Also, let us say that there is another car HydroCar that inherits from the class Car. This means that a HydroCar has all the abilities of a car and the additional ability that it can be driven on the surface of the water. This is hybrid inheritance i.e. a class can be a superclass for multiple classes.

Hybrid Inheritance Example

import java.util.*;

class Car {
    String name;
    String color;
    int engineHP; //engine Horse Power
    
    Car(String name, String color,int engineHP) {
        this.name = name;
        this.color = color;
        this.engineHP = engineHP;
    }
    
    public void drive() {
       System.out.println(name + " Car is driving");
    }
    
    public void reverse() {
        System.out.println(name + " Car is in reverse mode");
    }
}

class FlyingCar extends Car {
    
    FlyingCar(String name, String color, int engineHP) {
        super(name,color,engineHP);
    }
    
    public void fly() {
        System.out.println("Flying Car is flying");
    }
}

class HydroCar extends Car {
    
    HydroCar(String name, String color, int engineHP) {
        super(name,color,engineHP);
    }
    
    public void swim() {
        System.out.println("Hydro car is driving on the surface of the water");
    }
}

public class Main {
    public static void main(String[] args) {
        FlyingCar fc = new FlyingCar("SuperFly","Black",5000);
        fc.drive();
        fc.reverse();
        fc.fly();
        
        HydroCar hc = new HydroCar("SuperSwim","Black",5000);
        hc.drive();
        hc.reverse();
        hc.swim();
    }
}

4. Multiple Inheritance: Multiple inheritance means that a class can extend from multiple classes i.e. a subclass can have multiple superclasses. This is shown below.

Multiple Inheritance is not possible in Java. This is due to a problem called the “deadly diamond of death” problem. Let us understand this problem below.

The Deadly Diamond of Death Problem

Consider the diagram shown below.

So, the diagram shows that FlyingCar will extend easily from its superclass Car and the same is the case with the HydroCar. However, the HydroFlyingCar has 2 super classes both with the same data members. Here, a dilemma arises about from which class will the HydroFlyingCar extend? This is the deadly diamond of death problem in multiple inheritance. Hence multiple inheritance is not possible in Java.

So, this was all about inheritance. Let us now move to the next pillar i.e. Polymorphism.

Polymorphism

The word “Poly” means many and “morph” means forms. So, polymorphism means different forms or ways of doing something. There are 2 types of Polymorphism in Java namely Compile-Time Polymorphism and Run-Time Polymorphism.

Compile Time Polymorphism:

As the name suggests, this happens during compile time. There can be different methods having the same name in a class but with different numbers or types of parameters. Hence this is also known as method overloading. An example of method overloading is shown below.

Method Overloading Example

import java.util.*;

public class Main {
    
    public static void sort(int[] arr) {
        Arrays.sort(arr);
    }
    
    public static void sort(List<Integer> list) {
        Collections.sort(list);
    }
    
    public static void display(int[] arr) {
        
        for(int i=0;i<arr.length;i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        int[] arr = {9,8,6,3,4,7,2};
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(6);
        list.add(1);
        list.add(9);
        
        System.out.println("Before sorting:");
        System.out.println("Array");
        display(arr);
        System.out.println("List");
        System.out.println(list);
        
        System.out.println("\n\nAfter Sorting:");
        sort(arr);
        sort(list);
        System.out.println("Array");
        display(arr);
        System.out.println("List");
        System.out.println(list);
    }
}

Run Time Polymorphism

As the name suggests, this happens during run-time. If there are same methods in child and parent class, then which method will be called is decided during the run time (i.e. during object creation). Hence it is also known as method overriding. An example for the same is shown below.

Method Overriding Example

import java.util.*;

class A {
    void meth1() {
        System.out.println("Class A meth1 called");
    }
}

class B extends A{
    
    void meth1() {
        System.out.println("Class B meth1 called");
    }
    
    void meth2() {
        System.out.println("Class B meth2 called");
    }
}

public class Main {

    public static void main(String[] args) {
       
        // parent class reference and object - A meth1 will be called
        A obj1 = new A();
        obj1.meth1();
        
        // child class reference and object - B meth1 will be called
        B obj2 = new B();
        obj2.meth1();
        
//         Method of object is called always and not the reference.
        
        // parent class reference and child class object - B meth1 will be called 
        A obj3 = new B();
        obj3.meth1();
        
        // child class reference and parent class object - not possible
        // B obj4 = new A()
    }
}

All the method oevrriding rules are mentioned in the comments of the above program.

Now, let us study abstraction i.e. the last pillar of OOPS.

Abstraction

Abstraction means hiding the unnecessary details from the user. In Java, abstraction can be achieved using abstract classes and Interfaces. Interfaces are blueprints of Classes and are used to achieve total abstraction. On the other hand, partial abstraction can be achieved using abstract classes in Java.

So, with this we have discussed all the 4 pillars of OOPS. We have discussed all the OOPS concepts in Java with examples for each concept so that everything is clearly understood.

We hope that you liked this article on OOPS concepts in Java with examples and have understood everything in detail. We hope to see you again soon at PrepBytes.

Leave a Reply

Your email address will not be published. Required fields are marked *