OOPS Concepts – Object Oriented Programming in Java
In this article, we will be learning about the OOP concepts in detail, which is the basics of Java programming. Every line of code in Java is inside a class, everything in Java is related to classes and objects. So, it is of utmost importance that we understand these concepts with full clarity. Let us now start.
OOPS Concepts
There are two programming procedures/paradigms. One of them is procedural programming which focuses on the procedures to be followed rather than the data. Another is object-oriented programming which focuses more on the data and related methods rather than procedures. OOPs make the Java program reusable, easier to modify and debug, make the program more structural and modular, and makes the code short and simple.
In the previous article, we had learned briefly about the basic definitions of class and object and saw an example of each. In short, a class is a template that has certain attributes and an object is an instance of a class.
Here is a simple example of the usage of class and object in Java.
class Bank {
private String name;
private int account_no;
private float balance;
public void getData() {
name = "ABC";
account_no = 12345;
balance = 0;
}
private void deposit(float amt) {
balance += amt; //balance = balance + amt
}
private float calculateAmount(float principal, float rate, int time) {
float simpleInterest = (principal * rate * time) / 100;
float amount = principal + simpleInterest;
return amount;
}
public void displayData() {
System.out.println("Name: " + name);
System.out.println("Account number: " + account_no);
System.out.println("Balance: " + balance);
}
public static void main (String args[]) {
Bank obj = new Bank();
obj.getData();
obj.deposit(1500);
float amount = obj.calculateAmount(300, 4, 2);
System.out.println("Amount calculated for Rs 300/- at a rate of 4% for 2 years is: " + amount);
obj.displayData();
}
}
The output of the above program will be-
Amount calculated for Rs 300/- at a rate of 4% for 2 years is: 324.0
Name: ABC
Account number: 12345
Balance: 1500.0
Here’s a brief description of what we did in the above example.
- We created a class named Bank and give it attributes(data members) name, account number, and balance. Here, we have specified the data members as “private” because we want them to be inaccessible to the outer world in order to provide security.
- Inside the class we have related methods(member methods) namely:
- getData() for getting the data. It can also take user input or could take arguments with minor changes in the code.
- addAmount() is increasing the value of the amount in the bank account.
- calcAmount() is calculating the amount for a particular principal amount, rate of interest and time.
- displayData() is used to display all the members of the class.
- Note that calcAmount() is returning a float value that has to be stored at the time of calling. The addAmount() is not returning any value so its return type is void. Both these methods have been declared as “private”. This is because we want these methods to be accessible only by objects or other methods of the same class only.
- Then we created the main() from which execution will begin. We create an object named ‘obj’ and use this to call the member methods of the class. We cannot directly call these methods without using objects because this is the specialty of OOPs.
Everything is related and wrapped inside the class due to which they can only be called using objects of the same class. We can also create an array of objects but we need to call each of their methods individually using loops for each element.
- When we create the object, it means that obj is a variable of type Bank or, obj is an instance of class Bank. It means that class is a user-defined data type and we are defining its attributes and creating variables of the type class.
- This example was pretty simple and we will discuss the other major concepts of OOPs.
Constructors in Java
In the above Bank example, we assigned values to the data members in the getData() but that is not the best solution. What if we want to create more objects, then all of them will have the same names and account numbers. Here comes the concept of constructors.
Constructors are special methods. These have the same name as the class name. They have no return type, not even void, and are mainly used to initialize objects, i.e., to initialize the data members of the object. These are implicitly called when an object is created. They are of 3 types-
- Default constructor: The constructor having no parameters is known as the default constructor. It is used to initialize the objects with 0 or null values.
- Parameterized constructor: The constructor having parameters used to initialize the object using those parameters is called a parameterized constructor.
- Copy constructor: The constructor used to initialize an object using the values of another object is known as copy constructor. It contains another object of the same class as a parameter.
If we use more than one constructor in the same class with different parameters, it is known as constructor overloading.
Here is the same Bank example with added constructors.
class Bank {
private String name;
private int account_no;
private float balance;
// Default constructor
public Bank() {
name = null;
account_no = 00000;
balance = 0;
}
/* Parameterized constructor 1 with an extra variable to avoid confusion between the two parameterized constructors */
public Bank(String n, int acc, float b, float amt) {
name = n;
account_no = acc;
balance = b;
}
// Parameterized constructor 2
public Bank(String name, int account_no, float balance) {
this.name = name;
this.account_no = account_no;
this.balance = balance;
}
// Copy constructor
public Bank(Bank B) {
name = B.name;
account_no = B.account_no;
balance = B.balance;
}
private void deposit(float amt) {
balance += amt; //balance = balance + amt
}
private float calculateAmount(float principal, float rate, int time) {
float simpleInterest = (principal * rate * time) / 100;
float amount = principal + simpleInterest;
return amount;
}
public void displayData() {
System.out.println("Name: " + name);
System.out.println("Account number: " + account_no);
System.out.println("Balance: " + balance);
}
public static void main (String args[]) {
Bank obj1 = new Bank();
Bank obj2 = new Bank("Tom", 12345, 1000, 500);
Bank obj3 = new Bank("Harry", 98765, 2000);
Bank obj4 = new Bank(obj2);
obj1.deposit(1500);
float amount = obj1.calculateAmount(300, 4, 2);
System.out.println("Amount calculated for Rs 300/- at a rate of 4.5% for 2 years is: " + amount);
obj1.displayData();
obj2.displayData();
obj3.displayData();
obj4.displayData();
}
}
The output of the above program will be:
Amount calculated for Rs 300/- at a rate of 4.5% for 2 years is: 324.0
Name: null
Account number: 0
Balance: 1500.0
Name: Tom
Account number: 12345
Balance: 1000.0
Name: Harry
Account number: 98765
Balance: 2000.0
Name: Tom
Account number: 12345
Balance: 1000.0
Now we know how to write a simple class, create objects from it, define methods and initialize the objects using constructors.
Let us now learn the most important concepts of OOPs:
Data Encapsulation
The wrapping of data and related methods into a single unit is known as data encapsulation. To implement encapsulation in Java, we use something known as the Getter and Setter methods. They are used to get and set the values of data members respectively. This is achieved by declaring the class attributes as private and the getter and setter methods as public. An example is as below-
class Shop {
private int noOfItems;
private int shopNumber;
// setter
public void setNoOfItems(int n) {
noOfItems = n;
}
//setter
public void setShopNumber(int sn) {
shopNumber = sn;
}
// getter
public int getNoOfItems() {
return noOfItems;
}
// getter
public int getShopNumber() {
return shopNumber;
}
}
class Main {
public static void main(String args[]) {
Shop S = new Shop();
S.setNoOfItems(153);
S.setShopNumber(211);
System.out.println(S.getNoOfItems());
System.out.println(S.getShopNumber());
// S.noOfItems = 154; --> error
// System.out.println(S.noOfItems); --> error
}
}
In the above example, we have created two classes, Shop and Main. In class Shop, we have specified the attributes noOfItems and shopNumber as private and getter and setter methods as public. We create another class Main where we write the main() and create an object from which we call the getter and setter methods.
Pretty simple? This is the implementation of encapsulation. Note that we cannot access the data members directly by the object since they are private. So, trying to do that will cause an error. Also, as we were using only one class to write all our methods, the private members could still be accessed by the objects since they were inside one class. In the above example, we provide security to the data by using two classes.
Encapsulation provides flexibility to code, increases control of class and attributes and provides security to data.
Inheritance
Just like a child inherits the properties of his/her parents, a child class inherits the properties of its parent class. We use inheritance in Java to increase the reusability of code. Let us see a very simple example of inheritance.
class Car {
String modelName;
int mileage;
}
class MarutiSuzuki extends Car {
int a, b;
public void set(int a, int b, String modelName, int mileage) {
this. a = a;
this.b = b;
super.modelName = modelName;
super.mileage = mileage;
}
public void get() {
System.out.println("a: " + a + "nb: " + b + "nModel Name: " + modelName + "nMileage: " + mileage);
}
}
class Hyundai extends Car {
int c, d;
public void set(int c, int d, String modelName, int mileage) {
this. c = c;
this.d = d;
super.modelName = modelName;
super.mileage = mileage;
}
public void get() {
System.out.println("c: " + c + "nd: " + d + "nModel Name: " + modelName + "nMileage: " + mileage + "n");
}
}
class Example {
public static void main(String args[]) {
Car objCar = new Car();
MarutiSuzuki objMS = new MarutiSuzuki();
objMS.set(10, 20, "Alto", 150);
objMS.get();
Hyundai objH = new Hyundai();
objH.set(15, 25, "Verna", 200);
objH.get();
}
}
In the above example, we have created a class Car and two classes MarutiSuzuki and Hyundai and both are cars in general. So instead of defining attributes modelName and mileage of each of those two, we inherit the properties of Car in MarutiSuzuki as well as Hyundai.
So, both these classes now have the properties of Car which reduced our code and reused it. Note here the use of “extends” keyword while inheriting. Here, the class from which classes are being derived/inherited is known as the “super”/parent class which, in this case, is Car. And the classes which inherit the properties are known as derived/sub/child classes.
If the scope of the data members of the parent class is private, then the subclasses won’t be able to inherit it.
There are various types of inheritance in Java:
- Single inheritance: When only one class inherits properties from only one class then it is known as single inheritance. A->B
- Multi-level inheritance: The inheritance which is of the type: A->B->C
where A, B, C are classes and B is derived from A and C is derived from B then it is known as multi-level inheritance. - Hierarchical: The inheritance in which 2 or more classes inherit properties from the same class. A->B, A->C, A->D. In the above Car example, hierarchical inheritance was shown.
- Hybrid: A mixture of the above two types of inheritances is known as hybrid inheritance.
There is another type of inheritance – Multiple inheritance where one class is deriving properties from more than one superclass. Java does not support this type. This is becauseit creates confusion when there exist methods with the same name and the same arguments in superclasses and subclasses.
If a class has been declared as “final”, i.e. the class becomes immutable, then we cannot inherit classes from a final class.
Polymorphism
Polymorphism means to exist in more than one form. In Java let us consider an example of animals eating food:
class Animal {
public void eats() {
System.out.println("The animal eats food");
}
}
class Dog extends Animal {
public void eats() {
System.out.println("The Dog eats meat");
}
}
class Cat extends Animal {
public void eats() {
System.out.println("The Cat eats fish");
}
}
class Example {
public static void main(String args[]) {
Animal objAnimal = new Animal();
Dog objDog = new Dog();
Cat objCat = new Cat();
objAnimal.eats();
objDog.eats();
objCat.eats();
}
}
Here, the method eats()has been defined for each of the subclasses. When we call these methods from their respective objects, we get the output:
The animal eats food
The Dog eats meat
The Cat eats fish
This allows us to perform actions in more than one way.
Polymorphism in Java is implemented by methods overloading and function overloading. Runtime polymorphism takes place during inheritance as we saw in the above example. Compile-time polymorphism takes place during method/function overloading. Polymorphism lets us perform actions specifically according to the type of object and not in a general case.
Abstraction
Data abstraction is one of the OOPS concepts in whichwe show only the essential features while hiding the complex background details. We usually define a class as abstract if we do not want to create any objects from it and is purely written for the purpose of being inherited. To define a class as abstract, we use the keyword “abstract” before class. If we do not want to use the methods inside the abstract class, then we have to declare them as abstract as well using the “abstract” keyword again.
An abstract method has to be overridden in the subclass in order to be used. Overriding a method is redefining the method of the superclass in the subclass for its implementation. An abstract method must always contain an empty body. It may seem like a lot to take in, so let us consider the Animal example. In that example, we never have to use the eats() defined inside the Animal class because every animal has its own characteristic food. So we can declare the Animal class and method eats() as abstract.
abstract class Animal {
public abstract void eats();
}
When you make the above changes in your Animal class and recompile the code, you may see the error:
Example.java:17: error: Animal is abstract; cannot be instantiated
Animal objAnimal = new Animal();
Thus, we cannot create objects from abstract classes. The final code will be-
abstract class Animal {
public abstract void eats();
}
class Dog extends Animal {
public void eats() {
System.out.println("The Dog eats meat");
}
}
class Cat extends Animal {
public void eats() {
System.out.println("The Cat eats fish");
}
}
class Example {
public static void main(String args[]) {
Dog objDog = new Dog();
Cat objCat = new Cat();
objDog.eats();
objCat.eats();
}
}
We can also use the annotation @Override above the method we want to override. Method overriding can be really useful when we want to reduce unwanted code in our program. An abstract class can contain abstract as well as regular methods.
Interfaces
An interface is one of the crucial OOPS concepts. Interface in Java is a completely abstract class. It contains only abstract methods and has to be implemented by another class. An interface is derived by a class by using the keyword “implements” instead of “extends”. Interfaces are another way to achieve abstraction in Java. All the methods defined inside an interface have to be overridden and defined by the class that implements the interface. An example of an interface in Java is-
interface A {
public abstract void method1();
public abstract void method2();
}
class B implements A {
public void method1() {
System.out.println("This is method 1");
}
public void method2() {
System.out.println("This is method 2");
}
}
public class Interfaces {
public static void main(String args[]) {
B obj = new B();
obj.method1();
obj.method2();
}
}
By default, the variables in an interface are final, public and static. Java programming does not allow you to extend more than one class but you can implement more than one interface. An interface cannot implement another interface. It has to extend the other interface. Just like an abstract class, we cannot create objects from interfaces in Java.
Conclusion
We have learned about all the concepts of Object-Oriented Programming in Java. This article was lengthy but very essential at the same time. Learning about object-oriented concepts forms the major part of the Java language. To summarize, we have learned how to create a class and object, how to initialize the object using various types of constructors.
We learned what is data encapsulation, its usefulness in Java. How to implement it? What is inheritance in Java? How important it is and how to implement it? What is data abstraction? How to define a class and its methods as abstract? And finally, studied interfaces in Java.
It is a lot to understand so go slow. Make sure you understand everything in detail because this will help you a lot. Practice OOPS concepts. Google for things you didn’t understand perfectly. Experiment with various code modifications, and learn as much as you can. Learning a new language is really fun and exciting. So, make the best out of it and Happy Learning.