1
1 Comment

What is OOPs in Java - Tutorial

In general, Object-Oriented programming is the programming paradigm used to develop large-scale applications. Many programming languages support the principles of object-oriented programming including java. In Object-oriented programming, everything is considered as classes and objects.

To understand better object-oriented programming, let's compare it with regular procedural programming.

A procedural programming paradigm involves writing a procedure or method that runs on a set of data, while an object-oriented program involves creating objects that use both data and methods.

Example - Consider that we need to find the area of the rectangle. Then in procedural programming, we perform the following task -

  1. Get the length and breadth.
  2. Create a method that calculates the area.
  3. Call the method by passing the argument to calculate length and breadth.

That is all good. But the method we declared can be misused further in the program. Because it can be used anywhere. But the same with object-oriented programming, we can assign that method related to the particular task that the method is defined for. By doing this we avoid the risk of program corruption as well as code reusability. This article covers the OOPs concept in java in depth.

What is Java?

Java is a high-level programming language developed by James Gosling during the 1990s. It was initially named ‘oak‘ and further renamed with ‘java’.
Learning Java and object-oriented programming are two different things. Both of these have importance. So in this article, we will understand the concepts of object-oriented programming with the java language.

Java is based on the concept of object-oriented programming that contains the basic 4 principles that we call - APIE.

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

Apart from this, there are also 2 major components of object-oriented programming, that is ‘Classes’ and ‘Objects’.

So let’s first learn about classes and objects and then we will proceed with the principles of Object-oriented programming.

What is an Object?

Anything in the world is considered an object. Consider the Example - A Pen is an object, A Car is an object, A Computer is an object, etc. And each of these objects has its properties that the action it does. Similarly in object-oriented programming, anything that is defined in terms of its properties and behaviour is an object.

What are Classes?

In the real world, every object has its properties and behaviours associated with it. Consider an Example - (Television) It has the behaviour of changing channels, displaying visuals, etc. And as well as properties like- Its display size, colour, speakers, etc. Similarly in the terms of object-oriented programming, Classes contain the blueprint of the object that has all the behaviours and properties of the objects defined in it.

Now putting classes and objects together let's understand it programmatically with the help of an example.

We can see different properties and behaviours related to Television. So to implement it programmatically, We need to declare the classes and objects. So the syntax for declaring the classes and objects in java are-

Declaration of classes in Java-

class Television{
    final String brandName = "XYZ";
    int currentVolume;
    int currentChannel;
    
    public void changeVolume(){
        //Logic for Volume Change
    }
    public void changeChannel(){
        //Logic for Channel Change
    }
    public void changeBrightness(){
        //Logic for Brightness Change
    }
}

In the above, class is a keyword and { } is the block of the class in which the properties and behaviours related to objects are defined.

Creation of Objects in Java

Television tv = new Television();

Television is the class name, tv is the object name, and Television() is the constructor called for creating the object the Television class.

We have learned what classes and objects are all about. Now let's understand the basic 4 principles of Object-Oriented programming.

1. Abstraction : The idea of abstraction is to hide the internal implementation and only expose the features required. But what does that mean? Consider the same television example in the term of the real world. If we want to view the TV, then we don’t care how it is working internally. We are only required to switch on the TV and see the screen. Similarly for changing channels also. We don't consider how the channels got changed. We are only provided with the feature to change the channel and we are using that. With this example, we can say that these things are abstract.

Considering this in the terms of programming, We all have used System.out.println() for printing the message on the console. But have we ever wondered how the stream is gonna print the message on the screen? No, Because we don’t care about that.

2. Encapsulation : The idea of encapsulation is to put all the data and its behaviours in a single unit. This is the same thing that we see in real-world engineering also. Consider the same example of television. In television, we can see the buttons that are used for changing the channels. And obviously, there are some electrical operations performed on the circuit in the television. But that thing we can’t see and as well as the complete thing about the television are there in the single body. That is encapsulation. It means, everything is considered as a single unit related to a particular object. And exactly that happens in real-world software development. The data and its operations are kept in a single class. It is grouped.

3. Inheritance: Inheritance means inheriting the behaviours and properties from others and adding the extra properties and behaviours to make the object different. For example - Previously we had CRT TV, in which we could only see the visuals. But now we have Smart TV, in which we can see visuals also and as well as, we can surf the internet also. So here the smart TV is inheriting the features of the existing CRT TV and implementing its own to make a different new object. Exactly that is also implemented in the programming. We have seen the versions of applications released with some extra features. Inheritance can be used there.

4. Polymorphism: The word polymorphism is made with two different words. ‘Poly’ means many and ‘morphs’ means forms. It states that a single entity can work for different things. For Example - For smart TV, we also call it TV because it also performs the work of television. Although it functions differently, we call it television. Similarly, in programming, we give the same name to the multiple behaviours to function on data, and then their polymorphism is used.

In java, everything is executed under the classes and objects. So these basic 4 principles are extremely important to understand java better. Although we will see the implementation and uses of these principles later in this article. But before that, let’s first quickly understand more things related to Object-oriented programming in Java.

Constructor

Constructors are the special methods used to initialize the objects. When we create an object of classes then these constructors are called for initializing the object. It happens because when the memory area is allocated to the object, the default values are assigned to the variables to identify the memory block of each object.

Constructor is defined the same as class name with no return types. Otherwise, it will be treated as the method in java.

Example:

class Television{
    final String brandName = "XYZ";
    int currentVolume;
    int currentChannel;
    
    //Constructor
    Television(){
        this.currentChannel = 10;
        this.currentVolume = 5;
    }
}

In the above code, we can see the constructor Television(). It is initializing the object with values. Here we have used the no-argument constructor to initialize the values. There are various types of constructors in java.

1. No-Argument Constructor - When it received no value in the parameter.
2. Default Constructor - Implicitly defined by JVM when no constructor is defined in the class.
3. Parameterized Constructor - When the constructor received the values to initialize the object in the class.

Modifiers in Java

We can use the modifiers to provide the access level to the members of the class. Modifiers are also helping in achieving abstraction and polymorphism. There are 4 types of Access Modifiers available in java.

  • Public - The members of the classes are accessible everywhere. (Same package or another package).
  • Private - Private members are accessible only inside the class.
  • Default - Default members are accessible anywhere but in the same package. We need not explicitly define this, by default, the default keyword is auto-implemented.
  • Protected - These members are accessible only to their inherited class in any package.

These modifiers can be used with the classes, methods, or variables in java. There are also 6 other modifiers in java, that have their own function. The modifiers are -

  1. Final.
  2. Synchronized.
  3. Abstract.
  4. Static.
  5. Transient.
  6. Volatile.

With this basic understanding of some of the Java OOP concepts, Let’s understand the implementations of the principles of Object-Oriented programming.

Encapsulation

We saw about encapsulation that everything is considered as a single unit. Now let’s consider a car. It has the engine, clutch, breaks, steering, wheels, etc. And that is all in a single box we call a car. So let's programmatically understand that.

class Car{
    //Properties of a car.
    private boolean engine;
    private int wheel = 4;
    private String steering;
    private boolean breaks;
    private int escalator;

    //Methods to Drive Car.
    public void startEngine(){
        this.engine = true;
        System.out.println(" Car Engine Started. ");
    }
    public void stopEngine(){
        this.engine = false;
        this.escalator = 0;
        this.breaks = false;
        System.out.println(" Car Engine Stopped. ");
    }
    public void escalate(){
        this.breaks = false;
        this.escalator += 5;
        System.out.println(" Car escalated. Speed = "+this.escalator);
    }
    public void applyBreak(){
        this.escalator = 0;
        this.breaks = true;
        System.out.println(" Car Stopped. ");
    }
    public void turnStreeting(String direction){
        this.steering = direction;
        System.out.println(" Car turned "+direction+". ");
    }
}
public class Main {
    public static void main(String args[]) {
        //User Driving Car.
        Car car = new Car();
        car.startEngine();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.turnStreeting("Left");
        car.turnStreeting("Straight");
        car.turnStreeting("Right");
        car.applyBreak();
        car.stopEngine();
    }
}

Output - Run this code

Car Engine Started. 
 Car escalated. Speed = 5
 Car escalated. Speed = 10
 Car escalated. Speed = 15
 Car escalated. Speed = 20
 Car escalated. Speed = 25
 Car escalated. Speed = 30
 Car turned Left. 
 Car turned Straight. 
 Car turned Right. 
 Car Stopped. 
 Car Engine Stopped

In the above code, we can see that the car has all the methods and properties to drive a car. We also declared the properties private. So this provides security also. No user can directly affect the properties. It has to use the methods to do that. These all things are encapsulated in a single unit. So here encapsulation is used.

Inheritance

We have already seen inheritance When a subclass is extended from the parent class then the properties and behaviour of the parent class are inherited in the subclass. And that is called inheritance. So let’s see this programmatically.
We can consider the television example. One is CRT TV and another one is Smart TV. CRT TV can only show visuals but Smart TV can also allow surfing the internet. So it can be like -

class CRTTV{
    private boolean onOff;
    private int channel;
    private int volume;

    //Methods to operate TV
    public void switchOn(){
        this.onOff = true;
        System.out.println(" TV Switched ON. ");
    }
    public void switchOff(){
        this.onOff = false;
        System.out.println(" TV Switched OFF. ");
    }
    public void changeChannel(int channel){
        this.channel = channel;
        System.out.println(" Channel Changed. Current Channel : "+this.channel);
    }
    public void adjustVolume(boolean lowHigh){
        if(lowHigh){
            this.volume += 1;
            System.out.println(" TV Volume Increased. ");
        }else{
            this.volume -= 1;
            System.out.println(" TV Volume Decreased. ");
        }
    }
}
class SmartTV extends CRTTV{
    private boolean internetSurf;
    private boolean USB;
   
    //Extra features on SmartTV
    public void surfInternet(){
        this.internetSurf = true;
        System.out.println(" Surfing Internet ");
    }
   
    public void viewUSB(){
        this.internetSurf = false;
        this.USB = true;
        System.out.println(" Playing USB Content ");
    }
}
public class Main {
    public static void main(String args[]) {
        SmartTV tv = new SmartTV();
        tv.switchOn();
        tv.adjustVolume(true);
        tv.changeChannel(6);
        tv.surfInternet();
        tv.viewUSB();
        tv.switchOff();
    }
}

Output: Run this Code

 TV Switched ON. 
 TV Volume Increased. 
 Channel Changed. Current Channel: 6
 Surfing Internet 
 Playing USB Content 
 TV Switched OFF.

In the above code, the SmartTV is inherited from the CRT class. It uses the ‘extends’ keyword for inheriting. So all the features of the CRT TV are already there in the SmartTV. And also some extra features are also added to the SmartTV. So that is how the inheritance is used.

Abstraction

We have already seen abstraction means hiding the internal complexity and showing only the required things. So programmatically we can achieve this using 2 different approaches -

  1. Abstract Classes [can be incomplete abstraction].
  2. Interfaces [Complete abstraction].

Abstract classes - Abstract classes are classes that are declared abstract. They may have abstract and non-abstract methods. They must be extended and their methods must be implemented. They cannot be instantiated.

Example- Consider the class car that has all the features. The company purchased the complete car from the other but it provides the brand name for making it a complete ready class. So this can be achieved using the abstract classes.

abstract class Car{
    //Properties of a car.
    protected String brand;
    private boolean engine;
    private int wheel = 4;
    private String steering;
    private boolean breaks;
    private int escalator;

    //Abstract method Needs to be implemented in the Derived class

    protected abstract void setBrandName();

    //Methods to Drive Car.
    public void startEngine(){
        this.engine = true;
        System.out.println(" "+this.brand + " car Started. ");
    }
    public void stopEngine(){
        this.engine = false;
        this.escalator = 0;
        this.breaks = false;
        System.out.println(" "+this.brand + " car Stopped. ");
    }
    public void escalate(){
        this.breaks = false;
        this.escalator += 5;
        System.out.println(" Car escalated. Speed = "+this.escalator);
    }
    public void applyBreak(){
        this.escalator = 0;
        this.breaks = true;
        System.out.println(" Car Stopped. ");
    }
    public void turnStreeting(String direction){
        this.steering = direction;
        System.out.println(" Car turned "+direction+". ");
    }
}

class InterviewBitCar extends Car{
    InterviewBitCar(){
        setBrandName();
    }
    protected void setBrandName(){
        brand = "InterviewBit";
    }
}
class Main {
    public static void main(String args[]) {
        InterviewBitCar car = new InterviewBitCar();
        car.startEngine();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.turnStreeting("Left");
        car.turnStreeting("Straight");
        car.turnStreeting("Right");
        car.applyBreak();
        car.stopEngine();
    }
}

Output: Run this Code

 InterviewBit car Started. 
 Car escalated. Speed = 5
 Car escalated. Speed = 10
 Car escalated. Speed = 15
 Car escalated. Speed = 20
 Car escalated. Speed = 25
 Car escalated. Speed = 30
 Car turned Left. 
 Car turned Straight. 
 Car turned Right. 
 Car Stopped. 
 InterviewBit car Stopped.

In the above code, the abstract class was created as Car. That is already made. But not completely abstracted because it has non abstract methods also. When the InterviewBitCar class is extended then the method setBrandName() needs to be implemented to make the Car class useful. So this is how abstraction is achieved with this.

The other way is that if the company purchases the designs from the other company and based on that design it manufactures the car. This can be achieved with the abstract classes as well as interfaces. Since we saw it with the abstract classes. Now let's see it with the help of interfaces.

Interfaces: Java interfaces serve to achieve multiple inheritance and abstraction in Java. Only abstract methods can be defined in an interface, but no method body. Java’s Interface also represents the IS-A relationship, so they can have abstract methods and variables. However, they cannot have a method body.

interface InterviewBitCarDesign{
    public void setBrandName();
    public void startEngine();
    public void stopEngine();
    public void escalate();
    public void applyBreak();
    public void turnStreeting(String direction);
}
class Car implements InterviewBitCarDesign{
   
    protected String brand;
    private boolean engine;
    private int wheel = 4;
    private String steering;
    private boolean breaks;
    private int escalator;
    Car(){
        setBrandName();
    }
    public void setBrandName(){
        brand = "InterviewBit";
    }
    //Methods to Drive Car.
    public void startEngine(){
        this.engine = true;
        System.out.println(" "+this.brand + " car Started. ");
    }
    public void stopEngine(){
        this.engine = false;
        this.escalator = 0;
        this.breaks = false;
        System.out.println(" "+this.brand + " car Stopped. ");
    }
    public void escalate(){
        this.breaks = false;
        this.escalator += 5;
        System.out.println(" Car escalated. Speed = "+this.escalator);
    }
    public void applyBreak(){
        this.escalator = 0;
        this.breaks = true;
        System.out.println(" Car Stopped. ");
    }
    public void turnStreeting(String direction){
        this.steering = direction;
        System.out.println(" Car turned "+direction+". ");
    }
}
class Main {
    public static void main(String args[]) {
        Car car = new Car();
        car.startEngine();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.escalate();
        car.turnStreeting("Left");
        car.turnStreeting("Straight");
        car.turnStreeting("Right");
        car.applyBreak();
        car.stopEngine();
    }
}

Output: Run this Code

 InterviewBit car Started. 
 Car escalated. Speed = 5
 Car escalated. Speed = 10
 Car escalated. Speed = 15
 Car escalated. Speed = 20
 Car escalated. Speed = 25
 Car escalated. Speed = 30
 Car turned Left. 
 Car turned Straight. 
 Car turned Right. 
 Car Stopped. 
 InterviewBit car Stopped.

In the above code, the interface defined contains all the method declarations that the class needs to implement to make the concrete class. And the car class did the same. So this is how the interfaces are used for achieving the abstraction.

Polymorphism

Polymorphism means one name for many forms that we have already seen. There are basically 2 types of polymorphism.

  1. Compile-time polymorphism (Static Binding).
  2. Runtime polymorphism (Dynamic Binding).

To achieve compile-time polymorphism, Method Overloading is used. When multiple methods are declared with the same name but with different arguments then that is called Method Overloading.

It is also called compile-time polymorphism because during the time of compilation the compiler can decide which method signature to call. Let's understand it with the help of an example -

Consider the toy example of addition. There is a method that adds the two numbers and returns the addition. And we don't know which data type number the user will pass. So we declare the method as -

class Main {
    //Add method accepting the integer parameter
    public static int Add(int num_1, int num_2){
        return num_1+num_2;
    }

    //Overloaded Add method accepting float as parameter.
    public static float Add(float num_1, float num_2){
        return num_1+num_2;
    }

    //Overloaded Add method accepting double as parameter.
    public static double Add(double num_1, double num_2){
        return num_1+num_2;
    }
    public static void main(String args[]) {
        //Integer Add Method will call
        System.out.println(Add(2, 3));

        //Float Add Method will call
        System.out.println(Add(2.3f, 3.1f));

        //Double Add Method will call
        System.out.println(Add(2.9, 3.1));
    }
}

Output: Run this Code

5
5.3999996
6.0

In the above code, the compiler already knows which method to call. The compiler identifies it based on the method signature. So this is how the compile-time polymorphism is achieved.

To achieve Runtime Polymorphism, The Dynamic Method Dispatch is used. Dynamic Method Dispatch is the rule that helps to identify which method to call when the program is running. Dynamic method dispatch works on Method overriding. When the method with the same name and same signature is used in the derived class overriding the base class method of that same name. Then it is called the method overriding. Understanding the dynamic method dispatch will make things clear about method overriding.

So consider an example of CRT TV and SmartTV. If the SmartTV is inheriting the CRT TV and if we create the reference of the CRT TV and create the object of SmartTV then only the method overridden in the derived class will be called. Let’s understand this with the help of an example

class CRTTV{
    //Method
    public void switchOn(){
        System.out.println(" CRTTV is ON.");
    }
}
class SmartTV extends CRTTV{
    //Overriden Method
    public void switchOn(){
        System.out.println(" SmartTV is ON.");
    }
    public void surfInternet(){
        System.out.print(" Internet Surfing. ");
    }
}
class Main {
    public static void main(String args[]) {
        CRTTV tv = new SmartTV();
       
        //It will print SmartTV is ON.
        tv.switchOn();

        //It will give compile-time error.
        //tv.surfInternet();
    }
}

Output: Run this Code

SmartTV is ON.

In the above, we have taken the reference of the CRT TV and created the object for the SmartTV. Then the only methods that are overridden will be called. If we try to print the other methods in the SmartTV class that are not overridden then it will give the compile-time error. It is because the memory is allocated to the object at runtime. But during the compilation time, the compiler checks the reference for the availability of methods. And it will bind that to the object created at runtime. So this is how the dynamic method dispatch works.

So these are all the four basic principles of object-oriented programming in java.

Conclusion

Java is an object-oriented programming language. In an object-oriented programming language, there are basically four pillars in which the whole OOPs stand. That is all we have discussed in this article. This article helps you to understand the oops concepts in java. And that helps you in cracking the java interview.

on May 12, 2022
  1. 1

    OOPs is a programming paradigm based on the concept of objects, which can contain data and code that operates on that data.

    Java supports OOPs concepts such as encapsulation, inheritance, polymorphism, and abstraction.

    Encapsulation refers to the ability to group related data and behavior into objects and restrict access to that data from other objects. Inheritance allows objects to inherit properties and methods from parent objects, enabling code reuse and reducing redundancy. Polymorphism enables objects to take on different forms and behave differently based on the context in which they are used. Abstraction refers to the ability to define objects in a way that hides implementation details, making it easier to reason about code and maintain it over time.