Principles of Object-Oriented Programming (OOPs)
Introduction
Object Oriented programming is a programming paradigm where our program is defined using classes and objects which are used to model real-world entities. The methods and data in these classes is used to carry out different procedures and operations. It is quite different from Procedural Programming, where we focus solely on writing procedures (methods) to perform operations.
Importance of OOPS
Object Oriented Programming is the most popular mode of programming in today’s world as it has several advantages over the Procedural Programming, offering easier execution, modification and debugging of the code. OOPs concepts are essential for creating robust, maintainable, readable, and reusable code.
Java concepts are based on OOPs principles as it supports classes, objects, inheritance, polymorphism, abstraction, encapsulation, and interfaces which makes it an excellent option to learn OOPs. In this article, we’ll explore the fundamentals of OOPs and Java OOPs concepts with examples:
OOPs Concepts
Classes and Objects
Classes and objects are fundamental concepts of Object-Oriented Programming as we rely on objects to access and execute the attributes in our classes.
Classes: A class is a user-defined datatype that stores methods (functions) and other variables/data. It acts as a template for creating objects where we have certain defined attributes for the objects.
Objects: An object is an entity that has the same properties defined in the class. It is an instance of the class. However, every object has its own unique state and behaviour. The state of the object is the current value its parameters have (the same parameters defined in the class). The behaviour of an object is the actions it can perform.
Classes and objects are used to implement real-world entities which have certain attributes and desired behaviour. The class represent a basic set of properties while the objects are used to create entities which can replicate the class defined properties.
For example, we can define a building to be a class with certain properties like windows, doors, measurements, number of rooms etc. An object of this class could be a hospital as it is a building but will have different values from another object, say a school.
What are the 4 OOP concepts in Java?
The 4 principles of OOPs are –
- Abstraction
- Encapsulation
- Inheritance
- Polymorphism
Some additional concepts as well, which are not part of the 4 concepts of OOPs but equally important are:
- Association
- Aggregation
- Composition
- Coupling
- Cohesion
Abstraction
Abstraction refers to the process of identifying essential features of a program and hiding the internal processing. This allows us to provide a simpler breakdown of complex programs into easy-to-understand components so that we have an easy interface to work with.
For example, when we write code in Java, we are not concerned with how it is converted into machine language for the system to understand. We are only concerned with the Java code.
Encapsulation
Encapsulation (also referred to as data-hiding) refers to wrapping data and methods within a single unit. It is used to restrict access to class members and methods, generally we do this to restrict other classes from accessing data of a class, so that only the members of class in which the data was declared can access it.
In Java, we make the use of access modifiers to implement encapsulation. There are 3 kinds of access modifiers in Java: public, protected, and private.
Public: Methods and variables set as public can be accessed from anywhere in the program.
Protected: Methods and variables set as protected can be access from within the class and by subclasses (even from different packages)
Private: Methods and variables set as private can only be accessed from within the class. They cannot be accessed by another subclass or from outside the class anywhere in the program.
To implement Encapsulation, we set methods to private, ensuring no other classes can access our class’s data.
Inheritance
Inheritance puts forth the mechanism that allows us to reuse code. It is the concept where an object inherits some properties from another object. This applies to classes as well, meaning we can create a class (say class A) and have subclasses (say class B and C) inherit methods and properties from this class. This prevents us from rewriting those same properties and methods again in the subclasses.
In the above example, Class A is referred to as the Parent class. (Super/Base class) while Class B and C are called Child classes. (Derived/Extended class) It is called a is-a relationship. In Java, we implement Inheritance using the extends keyword.
There are several kinds of Inheritance in Java:
Single Inheritance: A child class only extends to one parent class.
Multilevel Inheritance: A parent class extends a child class, which itself extends a child class.
Hierarchical Inheritance: When multiple child classes extend a single parent class.
Multiple Inheritance: When single child class extends multiple parent classes. Java does not support this type of inheritance. However, one can emulate multiple inheritance using interfaces, since classes can implement multiple interfaces.
Polymorphism
Polymorphism in OOPs refers to the ability of programming languages to refer to different entities with the same name. This means that different objects can respond to the same method name in different ways. There exist 2 types of Polymorphism in Java:
- Compile time polymorphism is when the function to be executed is determined by the compiler at compile time. The compiler determines this based on the type or number of arguments passed to the function. This technique of changing the parameters of the method is called method overloading.
- Run time polymorphism is when the function to be executed is determined at runtime.
For example, Take a base class Vehicle with 2 derived classes, Car, and Bike. Consider a method “Wheels” in Vehicle, having this method in both the subclasses is called method overriding. The method to be chosen will be based on the type of object at runtime.
Having gone over the core objected oriented principles, we shall look at other important OOP principles:
Association
Association is an OOPs concept that defines the relationship between classes and/or objects. This association can be between single or multiple objects. There are 4 types:
- One to One
- One to Many
- Many to One
- Many to Many
Composition
Composition is a stronger form of Association. It is generally composed of a One-to-Many object relation, where one object (called the parent object) contains other objects (called child objects) as part of its state. These child objects don’t exist independently and get deleted automatically if the parent object is deleted.
Aggregation
Aggregation is a weaker form of composition. It represents the relationship where one object contains other objects as part of its state. It is called a has-a relationship. In Java, we perform aggregation by creating an instance variable of the aggregated class within the containing class. We then access and manipulate the aggregated class using methods of the containing class.
Since we can create complex objects made up of simpler objects, we are able to get better modularity and reuse code.
Coupling
Coupling refers to the dependency between classes which occurs when the attributes inside each class are being accessed by the other. Coupling is stronger the more access a class has access to the other class’s parameters. In Java, we can control coupling with the help of the access modifiers we have mentioned before. Interfaces provide a weaker coupling, this is because since they do not have a concrete implementation, it is impossible for another entity to have extensive details about them.
Cohesion
Cohesion refers to how many levels a method required to perform a well-defined task. A highly cohesive method can do a well-defined task within a single level or multiple well-connected levels. Meanwhile a weakly cohesive method will split the given objective into several unrelated level spanning various unrelated classes.
In Java, the java.io package is highly cohesive as it contains only I/O related classes which means it can carry out the task of input-output efficiently. The java.util package is weakly cohesive as it spans many unrelated classes with no single well-defined task in mind.
Is Java Object Oriented?
Yes, Java is an object-oriented programming language which supports all the OOPs principles we’ve discussed above. OOPs in Java is defined via objects for classes, and everything is run using these objects. Java supports encapsulation via access modifiers, can implement inheritance using the extends keyword. Polymorphism can be implemented using abstract classes and method overloading. These are a few examples of oops principles in Java.
Benefits of using Object-Oriented Programming
Object Oriented Programming has several key benefits as it helps enforce programming practices that bring out more efficient code that is easier to manage, debug and reuse. It also is more secure and provides simpler components by hiding away the internal processing. Let’s check how each principle ensures a superior programming approach:
- Modularity: By breaking down programs into objects and classes, we break down the code into smaller components which make it more readable and provide an easier way to maintain and organize. This can be done via Abstraction, abstract classes and interfaces provide a blueprint for the behaviour of objects but do not provide implementation details. This modularity helps make the code easier to maintain.
- Code Reusability: All principles of OOPs help create code that is more reusable. Inheritance helps developers save a lot of time by simply inheriting previous methods and data.
- Better Privacy: Encapsulation via the implementation of access modifiers helps make the code less vulnerable from outside its class. By setting our crucial methods and data to private, we prevent them from tampering by other classes.
- Increased Flexibility: Using polymorphism along with other OOP principles helps make the code much more flexible to developers. We can reuse the classes with methods, only changing the parameters to ensure the objects are able to access the specified methods. This increases readability as well.
- Applications: OOPs, using its classes and objects can simulate real-world entities quite well. This allows developers to model real world problems in a way that’s easy to code and provides all the benefits we’ve mentioned above.
FAQs
What is the difference between method overloading and method overriding?
Method overloading refers to when 2 or more methods have the same name but different type or number of parameters. It occurs in compile-time polymorphism.
Method overriding refers to the child class using methods with same name as parent class. It occurs in run-time polymorphism.
What is the role of abstract classes in OOPs?
Abstract classes are an implementation of abstraction where we do not define but declare methods. We cannot create instances of abstract classes.
What are the benefits of using aggregation?
Aggregation allows us to create complex objects based on the combination of simpler objects. This is due to the association between two or more classes.
We avoid creating bulky classes and instead can make smaller classes associate together to form the bigger class object.
What is hybrid inheritance?
It is the combination of multiple and hierarchical inheritance. A class is derived from multiple parent classes using both hierarchical and multiple inheritance. It can lead to ambiguity if not used properly.
What are the limitations of inheritance?
Causing Inflexibility: Inheritance can create rigid chains that can be difficult to modify or extend which makes the code inflexible.
Code Duplication: Inheritance can cause the same code to be replicated across classes in a hierarchy. This violates the reuse principle and makes the code difficult to maintain.
Poor Derived Class Connectivity: Changes in base class can cause a ripple effect in the derived classes leading to unexpected results and difficult to fix bugs.
What is operator overloading?
The implementation of user-defined operators for custom use is called operator overloading.
Do abstract class have abstract objects?
No, an abstract class cannot be used to create objects.
What is an interface?
Abstraction is implemented in Java using interface. The methods and variables are only declared in the interface, not defined. It helps achieve loose coupling and is used to implement multiple inheritance in Java.
What are virtual functions?
Virtual functions are methods in a subclass, when overridden, the subclass implementation is used when the method is called on its instance. All non-static methods are virtual functions in Java.
What is a constructor?
A constructor is a special method with the same name as the class. It is a method that is run the instance an object is created and can be used to initialize the variables of the object.
What is a destructor?
A destructor is a method which is called when an object reaches the end of its life cycle. It gives up its memory, closes all files and connections etc.
What are the benefits of using an interface over an abstract class in OOPs?
An interface is Java’s implementation of abstract classes. It provides greater flexibility as it does not require any definition for its methods, only declaration.
What is the role of the “this” keyword in OOPs, and how is it used?
In Java, the “keyword” is to refer to the current object in which the method is being called. This ensures that when accessing variables in the method, we are referring the current object’s variables. This avoids confusion between the distinguishing variables inside the class definition of methods.
What is the difference between an object and a class in OOPs?
A class is a blueprint of attributes that would extend to several instances which would share the same properties, but for different values. We call this “instance” the object of that class. It has its own values for the methods and variables defined while the class only declares and defines these variables and methods.
What is the difference between a shallow copy and a deep copy?
A shallow copy creates a new object but copies of the references to the previous object, this causes any change in the new object to be reflected in the old object as well.
A deep copy creates a new object and declares its original references and simply copies the values onto the new object. This ensures both the objects can be changed independently.
How is encapsulation implemented in Java?
Encapsulation is implemented in Java using access modifiers.
How is inheritance implemented in Java?
Inheritance is implemented in Java using the keyword extends.
What are is-a and has-a relationship?
Inheritance provides an is-a relationship while Aggregation provides a has-a relationship.