OOPs! It's Object Oriented Programming.
In this blog, we will learn about the basic concepts of OOPs in Java.
Define OOPs
Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code that manipulates that data. In OOP, a program is composed of a set of objects that interact with each other to accomplish a task. OOP is characterized by the use of classes and objects, encapsulation, inheritance, polymorphism, and encapsulation. These concepts provide a way to organize and structure code in a way that is easy to understand, maintain, and extend.
Concepts of OOPs
What is Object?
In object-oriented programming (OOP), an object is a self-contained unit of code and data that represents a specific entity or concept within a program. An object has two main components:
State: This refers to the data stored within the object, also known as its properties or attributes. The state of an object can change over time as the object is manipulated or interacts with other objects.
Behavior: This refers to the actions that an object can perform, also known as its methods or functions. The behavior of an object determines how it will respond to certain inputs or events.
An object is an instance of a class, which is a blueprint or template that defines the state and behavior of a group of objects. The class defines the properties and methods that all objects created from it will have, but each object will have its own unique state.
Objects are used to model real-world concepts and entities within a program, and they interact with each other through methods to accomplish a specific task. The use of objects allows for a more modular and organized approach to programming, as well as increased code reusability and maintainability.
What is Class?
In object-oriented programming (OOP), a class is a blueprint or template for creating objects. A class defines the properties and methods that an object created from it will have, but does not contain any specific values for those properties.
A class is a way to define a new data type, and it can contain both data and functions (methods). It defines a set of properties, which are the data members and a set of methods, which are the functions that operate on the data members.
A class can be thought of as a blueprint for creating objects, which are instances of the class. Each object created from a class will have its own unique state, but will share the same behavior and methods defined by the class.
Creating a class is also known as defining a class, and an object of a class is also known as an instance of the class.
For example, you can define a class called "Car" that has properties such as make, model, year and methods such as start, stop, drive, etc. You can then create multiple instances of the class "Car" representing different physical cars, each with their own unique state (e.g. "Toyota Camry 2020", "Honda Civic 2019").
Classes are a fundamental concept in OOP, and they provide a way to organize and structure code in a way that is easy to understand, maintain, and extend.
Inheritance
Inheritance is a mechanism in object-oriented programming (OOP) that allows a class to inherit the properties and methods of another class, also known as the parent class or superclass. The class that inherits the properties and methods is called the child class or subclass.
The main advantage of inheritance is code reusability, as the child class can reuse the properties and methods of the parent class without having to redefine them. This helps to reduce code redundancy and improve maintainability.
Inheritance can be represented using a diagram known as an inheritance hierarchy or class hierarchy, where the parent class is at the top, and the child classes are below it.
There are several types of inheritance, such as:
Single Inheritance: A child class inherits from only one parent class.
Multiple Inheritance: A child class inherits from multiple parent classes.
Multi-level Inheritance: A child class inherits from a parent class, which itself inherits from another parent class.
Hierarchical Inheritance: A parent class has multiple child classes.
Hybrid Inheritance: A combination of multiple inheritance, multi-level inheritance, and hierarchical inheritance.
Inheritance can also be used to model real-world relationships between objects, such as a "Car" class inheriting from a "Vehicle" class. The "Vehicle" class might have properties and methods that are common to all vehicles, such as a "start" method and a "speed" property, while the "Car" class would have additional properties and methods specific to cars, such as a "number of doors" property and a "honk" method.
In Java, inheritance is implemented using the "extends" keyword.
Here's an example of how you can use inheritance in Java:
class Parent {
int x;
void method1() {
// method code
}
}
class Child extends Parent {
int y;
void method2() {
// method code
}
}
In this example, the Child
class is inheriting from the Parent
class. The Child
class will have access to the x
variable and method1()
method from the Parent
class, in addition to its own y
variable and method2()
method.
It is also possible to override the methods inherited from the parent class, this is done by using the @Override
annotation and providing the new implementation of the method in the child class.
class Child extends Parent {
int y;
@Override
void method1() {
// new implementation of method1
}
void method2() {
// method code
}
}
In this example, the method1()
method in the Child
class will override the implementation of the same method in the Parent
class.
A child class can also use the super
keyword to call a method from the parent class.
class Child extends Parent {
int y;
void method2() {
super.method1(); // calling the method1 from Parent class
// method code
}
}
In the example above, the method2()
in the child class is calling the method1()
from the parent class using the super
keyword.
It's also worth mentioning that in Java, a class can only inherit from one parent, this is known as single inheritance, but it can implement multiple interfaces.
Polymorphism
Polymorphism is a mechanism in object-oriented programming (OOP) that allows different objects to respond to the same method call in a way that is specific to their types. Polymorphism enables a single method or operator to be used on multiple types of data.
There are two types of polymorphism:
- Compile-time Polymorphism (also known as static polymorphism or method overloading): This type of polymorphism is achieved by using method overloading, which is when a class has multiple methods with the same name but different parameter lists. The correct method to be called is determined at compile-time based on the types and number of arguments passed to the method.
class Calculator {
void add(int a, int b) {
System.out.println(a+b);
}
void add(int a, int b, int c) {
System.out.println(a+b+c);
}
}
In this example, the Calculator
class has two methods with the same name "add", but with different parameter lists. The first method takes two integer arguments, while the second method takes three integer arguments.
- Run-time Polymorphism (also known as dynamic polymorphism or method overriding): This type of polymorphism is achieved by using method overriding, which is when a subclass provides a different implementation of a method that is already defined in its superclass. The correct method to be called is determined at runtime based on the actual type of the object.
class Shape {
void draw() {
System.out.println("Drawing Shape");
}
}
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("Drawing Rectangle");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing Circle");
}
}
In this example, the Shape
class has a method "draw", which is overridden by the Rectangle
and Circle
classes. When the draw()
method is called on an object of type Rectangle
or Circle
, the overridden method in the respective class is called instead of the one in the Shape
class.
Polymorphism is an important concept in OOP, as it allows for greater flexibility and code reusability. It is closely related to the concept of inheritance, which allows for the creation of related classes that share a common interface.
Abstraction
Abstraction is a mechanism in object-oriented programming (OOP) that allows the user to focus on the essential features of an object and hide the implementation details. It is a technique of hiding the implementation details and showing only the necessary information to the user.
There are two ways to achieve abstraction in OOP:
- Abstract classes: An abstract class is a class that cannot be instantiated and is used as a base class for other classes. An abstract class can have both abstract and concrete methods (abstract methods are methods without a body and it must be overridden by the subclasses). The abstract methods in the abstract class force the subclasses to provide their own implementation.
abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
void draw() {
// draw a circle
}
}
In this example, the Shape
class is an abstract class, it cannot be instantiated, but it can be inherited by other classes, in this example the Circle class. The Shape
class has an abstract method draw()
that must be overridden by the Circle class to provide its own implementation.
- Interfaces: An interface is a collection of abstract methods. A class that implements an interface must provide an implementation for all of the methods defined in the interface. Interfaces are used to define a contract for a class and can be used to achieve multiple inheritance.
interface Shape {
void draw();
}
class Circle implements Shape {
void draw() {
// draw a circle
}
}
In this example, the Shape
interface defines the method draw()
that must be implemented by any class that implements the interface. The Circle
class implements the Shape
interface and provides its own implementation of the draw()
method.
Abstraction is useful because it allows the user to work with objects without having to know the details of how the object is implemented. This makes the code more modular, easier to understand, and easier to maintain. Additionally, it enables the implementation to be changed without affecting the clients that use the objects.
Encapsulation
Encapsulation is a mechanism in object-oriented programming (OOP) that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. It is the practice of hiding the internal state and behavior of an object from the outside world, and exposing a public interface through which the object can be controlled.
Encapsulation can be achieved by using the following techniques:
- Data hiding: Data hiding is the practice of keeping the internal state of an object private and only exposing a public interface through which the object can be controlled. This ensures that the internal state of the object cannot be directly accessed or modified by external code, and that any changes to the state are made through the provided methods.
class BankAccount {
private int balance;
public void deposit(int amount) {
balance += amount;
}
public void withdraw(int amount) {
balance -= amount;
}
public int getBalance() {
return balance;
}
}
In this example, the BankAccount
class has a private variable balance
that stores the current balance of the account. The deposit()
and withdraw()
methods are used to update the balance, and the getBalance()
method is used to read the balance. The variable balance
is private, so it can only be accessed by the methods within the class, and cannot be accessed directly by external code.
- Getters and setters: Getters and setters are methods that are used to read and write the internal state of an object, respectively. Getters are used to retrieve the value of a private variable, while setters are used to update the value of a private variable.
class BankAccount {
private int balance;
public void setBalance(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
}
In this example, the BankAccount
class has a private variable balance
and two methods: the setBalance()
and getBalance()
. The setBalance()
method updates the value of the balance
variable, while the getBalance()
method retrieves the value of the balance
variable. This way, the balance variable can't be accessed directly by external code, only through the setter and getter methods.
Encapsulation is an important concept in OOP, as it allows for data security and integrity, and promotes modularity and reusability of code. It enables the implementation of an object to be changed without affecting the clients that use the object, and it also makes it easier to maintain the code by minimizing the dependencies between the different parts of the system.
Coupling & Cohesion
Coupling and cohesion are two concepts in object-oriented programming (OOP) that are used to measure the quality of the design of a software system.
Coupling: Coupling refers to the degree to which one module or class depends on another. High coupling indicates that a module or class is dependent on many other modules or classes, while low coupling indicates that a module or class is independent and has few dependencies on other modules or classes. High coupling can lead to a complex and fragile system, where changes in one module or class affect many other parts of the system, making it difficult to maintain and extend.
Cohesion: Cohesion refers to the degree to which the elements within a module or class are related to each other and work together to achieve a single, well-defined purpose. High cohesion indicates that a module or class has a single, well-defined purpose and that its elements are closely related to each other, while low cohesion indicates that a module or class has multiple, poorly-related responsibilities. High cohesion makes the module or class more modular and easier to understand and maintain, while low cohesion can make the module or class harder to understand and more prone to errors.
In general, it is desirable to have low coupling and high cohesion in a software system, as this leads to a more modular, flexible, and maintainable design. Low coupling allows for more independence between modules and classes, making it easier to make changes to one module or class without affecting other parts of the system. High cohesion ensures that each module or class has a single, well-defined purpose, which makes it easier to understand and maintain.