OOPs – Object Oriented Programming

Object-oriented programming (OOP) refers to a programming methodology based on objects, instead of just functions and procedures. The objects contain the data and the methods (or behavior).

In OOPs concepts, we will learn four major principles – abstraction, encapsulation, inheritance, and polymorphism. They are also known as four pillars of the object oriented programming paradigm.

  1. Abstraction is the process of exposing the essential details of an entity, while ignoring the irrelevant details, to reduce the complexity for the users.
  2. Encapsulation is the process of bundling data and operations on the data together in an entity.
  3. Inheritance is used to derive a new type from an existing type, thereby establishing a parent-child relationship.
  4. Polymorphism lets an entity take on different meanings in different contexts.
Table of Contents

1. What is Object-oriented programming?
    1.1. Class and Object
    1.2. Constructor
2. Four Principles of OOP
    2.1. Abstraction
    2.2. Encapsulation
    2.3. Inheritance
    2.4. Polymorphism
3. Minor Concepts
4. Best practices

1. What is Object-oriented programming?

In initial days, the programs were written with binary code, and mechanical switches were used to load the programs. Later, as the hardware capabilities evolved, experts tried to simplify the programming using high-level languages where compilers were used to generate machine instructions from the program.

With more evolution, experts created the structured programming which was based on small functions. These functions helped in many ways e.g. code reuse, local variables, code debugging, and code maintainability.

With more computing advancement and demand for more complex applications, the limitations of structured programming started to be visible. The complex applications needed to be more closely modeled with real-life world and usecases.

Then experts developed object-oriented programming. In center of OOP, we have objects and classes. Just like a real-life entity, an object has two major characterstics :

  • data – tells about the attributes and the state of the object
  • behavior – gives it the ability to change itself and communicate with other objects

1.1. Class and Object

An object is an instances of a class. Each object has its own state, behavior, and identity. A class is the blueprint or template for its objects. Objects can communicate with other objects by calling functions. It is sometimes referred as message passing.

For example, if we are working on an HR application then it consists of entities/actors e.g. employee, manager, department, payslips, vacation, goals, time tracking, etc. To model these entities in computer programs, we can create classes that will have similar data attributes and behavior like in real-life.

For example, an employee entity can be represented as Employee class:

public class Employee 
{
    private long id;
    private String title;
    private String firstName;
    private String middleName;
    private String lastName;
    private Date dateOfBirth;

    private Address mailingAddress;
    private Address permanentAddress;

    // More such attributes according to application requirements

    // Constructor
    public Employee(id)
    {
        this.id = id;
    }

    // Method

    public String getFullName()
    {
        return String.join(" ", this.firstName, 
                this.middleName, 
                this.lastName);
    }

    public int getAge()
    {
        //Calculate age from dateOfBirth and return
        return ;
    }
}

The above Employee acts as a template. We can use this class to create as many different employee objects as we need in the application.

Employee e = new Employee(111);
e.setFirstName("Alex");
..
..

int age = e.getAge();

The id field helps in storing and retrieving the detail of any individual employee.

The object identity is generally maintained by the application runtime environment e.g. for Java applications, its Java Virtual Machine (JVM). Each time we create a Java object, JVM creates a hashcode for this object and assign it. This way, even if programmer forgets to add id field, JVM ensures that all objects are uniquely identified.

1.2. Constructor

Constructors are special methods without return value. Their name is always the same as the name of the class; but they can accept parameters that help in setting the initial state of the object, before the application starts using it.

If we do not provide any constructor, JVM assigns a default constructor to the class. This default constructor does not accept any parameter.

Remember, if we assign a constructor to any class then JVM does not assign the default constructor to it. If needed, we need to specify the default constructor explicitly to the class.

public class Employee 
{
    // Default constructor
    public Employee()
    {

    }

    // Custom constructor
    public Employee(int id)
    {
        this.id = id;
    }
}

2. Four Principles of OOP

The four major features of object oriented programming are:

  • Abstraction
  • Encapsulation
  • Inheritance
  • Polymorphism
OOP
OOP Pillars

2.1. Abstraction

Abstraction is very easy to understand when we relate it to the real-time example. For example, when we drive our car, we do not have to be concerned with the exact internal working of the car. What we are concerned with is interacting with the car via its interfaces like steering wheel, brake pedal, accelerator pedal, etc. Here the knowledge we have of the car is abstract.

In computer science, abstraction is the process by which data and programs are defined with a representation similar in form to its meaning (semantics) while hiding away the implementation details.

In more simple terms, abstraction is to hide information that is not relevant to context or rather shows only relevant information and to simplify it by comparing it to something similar in the real world.

Abstraction captures only those details about an object that is relevant to the current perspective.

Typically abstraction can be seen in two ways:

  1. Data abstraction

    Data abstraction is the way to create complex data types from multiple smaller data types – which is more close to real-life entities. e.g. An Employee class can be a complex object of having various small associations.

    public class Employee 
    {
        private Department department;
        private Address address;
        private Education education;
        //So on...
    }
    

    So, if you want to fetch information of a employee, you ask it from Employee object – as you do in real life, ask the person itself.

  2. Control abstraction

    Control abstraction is achieved by hiding the sequence of actions for a complex task – inside a simple method call, so logic to perform the task can be hidden from the client and could be changed in the future without impacting the client code.

    public class EmployeeManager
    {
        public Address getPrefferedAddress(Employee e)
        {
            //Get all addresses from database 
            //Apply logic to determine which address is preferred
            //Return address
        }
    }
    

    In the above example, tomorrow if you want to change the logic so that everytime domestic address is always the preferred address, you will change the logic inside getPrefferedAddress() method, and client will be unaffected.

Read More : Abstraction in Java

2.2. Encapsulation

Wrapping data and methods within classes in combination with implementation hiding (through access control) is often called encapsulation. The result is a data type with characteristics and behaviors.

Whatever changes, encapsulate it” – A famous design principle

Encapsulation essentially has both i.e. information hiding and implementation hiding.

  • Information hiding is done through using access control modifiers (public, private, protected) and implementation hiding is achieved through creation of interface for a class.
  • Implementation hiding gives the designer the freedom to modify how the responsibility is fulfilled by an object. This is especially valuable at points where the design (or even the requirements) are likely to change.

Let’s take an example to make it more clear.

2.2.1. Information hiding
class InformationHiding 
{
    //Restrict direct access to inward data
    private ArrayList items = new ArrayList();

    //Provide a way to access data - internal logic can safely be changed in future
    public ArrayList getItems(){
        return items;
    }
}
2.2.2. Implementation hiding
interface ImplemenatationHiding {
    Integer sumAllItems(ArrayList items);
}
class InformationHiding implements ImplemenatationHiding
{
    //Restrict direct access to inward data
    private ArrayList items = new ArrayList();

    //Provide a way to access data - internal logic can safely be changed in future
    public ArrayList getItems(){
        return items;
    }

    public Integer sumAllItems(ArrayList items) {
        //Here you may do N number of things in any sequence
        //Which you do not want your clients to know
        //You can change the sequence or even whole logic
        //without affecting the client
    }
}

Read More : Encapsulation In Java

2.3. Inheritance

Inheritance is another important concept in object-oriented programming. Inheritance in Java is a mechanism by which one class acquires the properties and behaviors of the parent class. It’s essentially creating a parent-child relationship between classes. In Java, we will use inheritance mainly for code reusability and maintainability.

Keyword “extends” is used to inherit a class in java. The “extends” keyword indicates that we are making a new class that derives from an existing class.

In the terminology of Java, a class that is inherited is called a super class. The new class is called a subclass.

A subclass inherits all the non-private members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

2.3.1. Inheritance example
public class Employee 
{
    private Department department;
    private Address address;
    private Education education;
    //So on...
}

public class Manager extends Employee {
    private List<Employee> reportees;
}

In above code, Manager is specialized version of Employee and reuses department, address and education from Employee class as well as define it’s own reportees list.

3.2.2. Types of inheritance
  • Single inheritance – A child class is derived from one parent class.
    class Parent
    {
        //code
    }
    
    class Child extends Parent
    {
        //code
    }
    
  • Multiple inheritances – A child can derive from multiple parents. Till JDK 1.7, multiple inheritance was not possible in java through the use of classes. But from JDK 1.8 onwards, multiple inheritance is possible via the use of interfaces with default methods.

    Multiple inheritance through the use of interfaces is always possible in Java.

    interface MyInterface1
    {
           
    }
    
    interface MyInterface2
    {
          
    }
    
    class MyClass implements MyInterface1, MyInterface2
    {
    
    }
    
  • Multilevel inheritance – it refers to inheritance between more than three classes in such a way that a child class will act as parent class for another child class.

    In given example, B is parent class as well as child class also.

    class A
    {
    
    }
    
    class B extends A
    {
    
    }
    
    class C extends B
    {
    
    }
    
  • Hierarchical inheritance – It refers to inheritance when there is one super class and more than one sub classes extending the super class.
    class A
    {
    
    }
    
    class B extends A
    {
    
    }
    
    class C extends A
    {
    
    }
    
    class D extends A
    {
    
    }
    
  • Hybrid inheritance – It is a combination of two or more types of inheritance. So when the relationship between classes contains inheritance of two or more types, then we say classes implement hybrid inheritance.
    interface A
    {
    
    }
    
    interface B extends A
    {
    
    }
    
    class C implements A
    {
    
    }
    
    class D extends C impements B
    {
    
    }
    

2.4. Polymorphism

Polymorphism is the ability by which, we can create functions or reference variables which behaves differently in a different programmatic context. It is often referred as one name with many forms.

For example, in most programming languages, '+' operator is used to add two numbers as well as concatenating two strings. Based on the type of variables, the operator changes its behavior. It is known as operator overloading.

In Java, polymorphism is essentially considered into two types:

2.4.1. Compile-time polymorphism

In compile-time polymorphism, the compiler can bind the appropriate methods to the respective objects at compile time because it has all the necessary information and knows which method to call during program compilation.

It is often referred as the static binding or early binding.

In Java, it is achieved with the use of method overloading. In method overloading, the method parameters can vary with a number, order, or the types of parameter.

class PlusOperator 
{
       int sum(int x, int y) {
             return x + y;
       }

       double sum(double x, double y) {
             return x + y;
       }

       String sum(String s1, String s2) {
             return s1.concat(s2);
       }
}
2.4.2. Runtime polymorphism

In runtime polymorphism, the call to an overridden method is resolved dynamically at runtime. The object, on which the method will be executed, is determined at runtime – generally depending on user-driven context.

It is often referred as the dynamic binding or method overriding. We may have heard it with name dynamic method dispatch.

In runtime polymorphism, we generally have a parent class and minimum one child class. In a class, we write a statement to execute a method which is present in the parent class and child class, both.

The method call is given using the variable of the type of parent class. The actual instance of the class is determined at runtime because a parent class type variable can store the reference to the instance of the parent class as well as child class also.

class Animal {
   public void sound() {
         System.out.println("Some sound");
   }
}

class Lion extends Animal {
   public void sound() {
         System.out.println("Roar");
   }
}

class Main 
{
   public static void main(String[] args) 
   {
        //Parent class reference is pointing to a parent object
        Animal animal = new Animal(); 
        animal.sound(); //Some sound

        //Parent class reference is pointing to a child object
        Animal animal = new Lion(); 
        animal.sound(); //Roar
   }
}

Read More : Polymorphism in java

3. Minor Concepts

Apart from the above 4 building blocks of OOP, we have a few more concepts which play an important role in building the whole understanding.

Before going deeper, we shall understand the term module. In general programming, a module refers to a class or a sub-application which perform unique functionality. In HR application, a class can perform various functions such as sending emails, generate salary slip, calculate the age of the employee, etc.

3.1. Coupling

Coupling is the measure of the degree of interdependence between the modules. Coupling refers to how strongly a software element is connected to other elements. A good software will have low coupling.

It means a class should perform a unique task or only tasks that are independent of other tasks. E.g. an EmailValidator class will only validate the email. Similarily, EmailSender class will only send email.

If we include both functonality within a single class EmailUtils then it is example of tight coupling.

3.2. Cohesion

Cohesion is the internal glue that keeps the module together. Good software design will have high cohesion.

It means a class/module should include all the information that is needed for it to perform its function without any dependency. For example, an EmailSender class should be able to configure SMTP server, accept sender’s email, subject and content. Basically, it should focus on sending emails only.

The application should not use EmailSender for any other function other than sending email. Low cohesion results in monolithic classes that are difficult to maintain, understand and reduces reusablity.

3.3. Association

Association refers to the relationship between objects who have an independent lifecycle without ownership.

Let’s take an example of a teacher and student. Multiple students can associate with a single teacher, and a single student can associate with multiple teachers, but both have their own lifecycles.

Both can be created and deleted independently so when a teacher leaves the school, we don’t need to delete any students, and when a student leaves the school, we don’t need to delete any teachers.

3.4. Aggregation

Association refers to the relationship between objects who have an independent lifecycle with ownership. It is between child and parent classes where child objects cannot belong to another parent object.

Let’s take an example of a cell phone and a cell phone battery. A single battery can belong to only one phone at a time. If the phone stops working, and we delete it from our database, the phone battery will not be deleted because it may still be functional. So in aggregation, while there is ownership, objects have their own lifecycle.

3.5. Composition

Composition refers to relationships when objects don’t have an independent lifecycle. If the parent object is deleted, all child objects will also be deleted.

For example, the relationship between questions and answers. Single questions can have multiple answers, but the answers cannot belong to multiple questions. If we delete a question, all its answers will automatically be deleted.

4. Best practices

4.1. Favor composition over inheritance

Inheritance and composition, both kind of promote the code reusability. But the use of composition is preferred over the inheritance.

An implementation of composition over inheritance typically begins with the creation of various interfaces representing the behaviors that the system must exhibit. Interfaces enable polymorphic behavior. Classes implementing the identified interfaces are built and added to business domain classes as needed. Thus, system behaviors are realized without inheritance.

interface Printable {
    print();
}

interface Convertible {
    print();
}

class HtmlReport implements Printable, Convertible
{
    
}

class PdfReport implements Printable
{
    
}

class XmlReport implements Convertible
{
    
}

4.2. Program to an interface, not to the implementation

This leads to flexible code that can work with any new implementation of the interface. We should aim to use interfaces as variables, as return types of a method or as argument type of methods.

Interfaces act as superclass types. In this way, we can create more specializations of the interface in future without modifying the existing code.

4.3. DRY (Don’t Repeat Yourself)

Don’t write duplicate code, instead use Abstraction to abstract common things in one place.

As a thumb rule, if you write the same piece of code at two places – consider extracting in a separate function and call the function at both places.

4.4. Encapsulate What Changes

All software get changes over time. So, encapsulate the code you expect or suspect to be changed in the future.

In Java, use private methods to hide such implementations from clients so that when you make a change, the client is not forced to make changes on its code.

Use of design patterns is also recommeded to acheive encapsulation. For example, factory design pattern encapsulates object creation code and provides flexibility to introduce a new type later with no impact on existing clients.

4.5. Single Responsibility Principle

Its one of Solid principles of OOP class design. It emphasizes that one class should have one and only one responsibility.

In other words, we should write, change, and maintain a class for only one purpose. This will give we the flexibility to make changes in the future without worrying about the impacts of changes for another entity.

4.6. Open Closed Principle

It emphasizes that software components should be open for extension, but closed for modification.

This means that our classes should be designed in such a way that whenever fellow developers wants to change the flow of control in specific conditions in the application, all they need to extend our class and override some functions and that’s it.

If other developers are not able to design desired behavior due to constraints put by our class, then we should reconsider changing our class.

There are a lot of other concepts and definitions in whole OOPs paradigm which we will learn in other tutorials.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

14 thoughts on “OOPs – Object Oriented Programming”

  1. 4 pillers of oop are significant , how , and if we omit any 2 of the pillers what effect will it have on oop ?
    pls answer

    Reply
  2. Hi Lokesh,

    Manager can not reuse department, address and education from Employee because these attributes are private.
    Manager can reuse them if they are protected.

    Reply
    • Yes, he can resuse the. For brevity Lokesh has omitted the getters and setters I believe. You can access those data using the getters and setters of the Parent Class and initialize them using setters/chaining of constructors.

      Child class {
      @Override
      public getSuperClassProperty() {
      super.getProperty(); // This is defined in the parent class.
      }

      Reply
  3. Wonderful points you have mentioned here, Its actually a great and helpful piece of information. I am satisfied that you simply shared this helpful info with us. Please stay us informed like this.
    Thank you for sharing.

    Reply
  4. Informative and Insightful blog!
    All i was looking for the OOPs concept in java in detail and i have found it very well here, OOPs concepts you have defined clearly and precise which is understandable for us. As beginner i have enjoyed your article and will look forward to read more from you. Thanks A lot for this meaningful article!

    Reply
  5. Hi Lokesh,
    Can you please point out some differences between inheritance and composition. And as they say we should prefer composition over inheritance, but what is the reason behind it.

    Reply
    • I will list out differences between inheritance and composition some other time. Main reason to prefer composition over inheritance is “loose coupling”. Inheritance introduces tight coupling which shall be avoided in most of the cases.

      Reply
      • public class Person {

        //composition has-a relationship
        private Job job;

        public Person(){
        this.job=new Job();
        job.setSalary(1000L);
        }
        public long getSalary() {
        return job.getSalary();
        }

        }

        The above example is for Composition in which we are creating object of dependent object inside constructor so i think it is tight coupling? right ?

        Reply
  6. Hi Lokesh,

    please clarify my doubt in spring.

    Can we inject Service Class into Controller in spring framework?. this is one of the interview question i was faced in CTS Pune.

    please provide me the answer with simple example. thanks in advance.

    if any tutorials are there please send it to my mail: pradepp.tanugula@gmail.com

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.