Introduction to Object Oriented Programming in Java

This article assumes that you are familiar with procedural programming. So, let’s begin with an example of the same. Suppose we need to create an application that maintains student records, including name, age, roll number, and marks in Science, English, and Mathematics. The application should also perform functions like calculating percentage marks of each student, changing the student's roll number, increasing marks in a particular subject, etc.

What are Class and Object in OOPS?

In procedural programming, one way to approach this problem would be to create six arrays, one for each attribute: name, age, roll number, and marks in Science, English, and Mathematics. In this case, the same index of different arrays can have values corresponding to one student. The application would look like something we have below.

public class StudentDemo {
    public static void main(String args[]) {
        String name[] = new String[10];
        int rollNumber[] = new int[10];
        int age[] = new int[10];
        int mathMarks[] = new int[10];
        int englishMarks[] = new int[10];
        int scienceMarks[] = new int[10];
        // name[i] and rollNumber[i] would represent name and roll number of ith student
        // Take input for these arrays        
    }
    
    public float calculatePercentage(int mathMarks, int englishMarks, int scienceMarks, int maxMarksPerSubject) {
        return (mathMarks + englishMarks + scienceMarks) * 100 / (float)(maxMarksPerSubject * 3);
    }
    // more functions to calculate different stats
}

If a new requirement comes to store the address of each student, we will have to create one more array. If the maximum marks of each subject change, then we will have to alter the arguments of the method calculatePercentage(). If we want to store more details about each subject, we will need another set of arrays to keep properties of subjects like subjectName[] and subjectCode[].

With each new requirement, the complexity of this code will increase drastically. Thus, we need classes and objects to make code more efficient and reusable. It enables us to think about the problem to align with our natural thinking and reduces complexity. So the critical question is: how can we transform the above solution into the world of classes and objects?

public class Student {
    private String name;
    private int age;
    private int rollNumber;
    private int englishMarks;
    private int scienceMarks;
    private int mathMarks;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
  
    // other getters and setters
  
    public float calculatePercentage(int maxMarksPerSubject) {
        return (mathMarks + englishMarks + scienceMarks) * 100 / (float)(maxMarksPerSubject * 3);
    }
    public void changeRollNumber(int newRollNumber) {
        this.rollNumber = newRollNumber;
    }
  
    // other methods related to class Student 
}

Here we have a Student class that contains all the properties (or attributes) of a student. We can say class Student defines a template for each student object, i.e., each student will have the attributes and methods defined in the class. Just like we have variables of type int, we can have variables of Student type. We can declare them as shown below:

Student studentA = new Student();
studentA.setName("Ayush");

Here, new Student() creates a student object and assigns its reference to the variable studentA and studentA.setName(“Ayush”) sets the name of the student. Similarly, we can call other setters on the object to set other attributes.

An object is a real entity that represents an instance of the class. We can also say that class is just a specification for the objects. It defines the properties (attributes) and methods that all objects of a particular type should have, and then each object is a real entity that defines the values for those attributes. If Car is a class with properties like topSpeed, horsePower, etc., then real cars like Nissan Micra, Fiat Punto, etc., are objects (of Car class) with different values for those specifications.

Each object has two things: state and behavior. The attributes like name, age, marks, etc. define the state of the object, and methods like changeRollNumber() define the behavior (algorithms that operate on attributes) of the object.

public static void main(String args[]) {
    Scanner sc = new Scanner(System.in);
    Student student[] = new Student[100];
    for(int i = 0; i < 100; i++) {
        student[i] = new Student();
        student[i].setName(sc.nextLine());
        student[i].setAge(sc.nextInt());
        // take other inputs            
    }        
}

Earlier code consisting of six different arrays (one for each attribute) got reduced to this, and now we only need one array to represent any number of attributes and any number of students. If we want to add a new attribute then we can just add it to the student class and we don’t need to create another array for it. If we have different properties for subjects then we can create a Subject class and use it inside Student class as shown below:

public class Subject {
    String name;
    String code;
    int maxMarks;
    
    // getters and setters
}
public class Student {
    private String name;
    private int age;
    private int rollNumber;
    private Subject[] subjectList;    

    //getters and setters
}

Now we don’t even need separate attributes for different subjects. We can see how classes and objects helped to gracefully and elegantly manage the complexity of the code. What we did was, just defined a class for each real-world entity we wanted to capture in our code. Thus, classes and objects help us to map real-world entities to simple programming constructs.

Besides reducing complexity, the object-oriented approach provides other advantages as well. Before delving into the advantages, let’s first discuss the four main concepts involved in OOPS so that it’s easier to understand the advantages it has to offer.

Four Basic Concepts of Object-Oriented Programming

1. Encapsulation

Encapsulation is the wrapping up of data members and methods operating on that data in a single unit. This means that every individual object has its own set of data and methods independent of the data or methods of other objects.

With this capability, each object (with its data and methods) has complete control over its state and behavior, and we can’t modify its state from outside. This helps prevent inconsistencies in the object state. Encapsulation in Java also enables objects to control access to their properties by using appropriate access specifiers.

Suppose we have a class ‘Number’ with two data members, ‘int odd’ and ‘int even’, that is always supposed to be odd and even. Now the methods in this class ensure that the value for these members always remains odd and even. However, consider it was possible to modify these members from the outside. In that case, it could have led to inconsistencies in the code (as other programmers using the class might not be aware of the constraints that the original programmer might have considered important).

You can learn more about encapsulation here: Encapsulation in Object-Oriented Programming

2. Abstraction

Abstraction is the concept of hiding implementation details from the user and providing them with just an interface to use any particular functionality. A typical example of abstraction is the way we drive a car. While driving a car, we just use the steering (an interface) to change direction but we are not aware (neither we need to) of how the rotation of steering causes a change in the direction of motion. Similarly, we know to apply the brakes we need to press the brake pedal but we don’t (need to) know the internal functioning of the same.

Similarly, we might have a class ‘Student’ with a method ‘getGPA()’. Now we can use objects of the ‘Student’ class and call the method ‘getGPA()’ to get the GPA of any student. Thus, the only thing we need to know is the signature of the method that we need to call. Its implementation might even change with time but to the end-user, it doesn’t matter as they are only concerned with the output returned by the method.

You can learn more about abstraction here: Abstraction in Object-Oriented Programming

3. Inheritance

Many entities share common properties in the real world, so they can be generalized into a more abstract entity that describes them. For example, we have the abstract entity ‘Person’. All the people in the real world will have common properties like date of birth, gender, height, weight, etc.

We know all the teachers and students in a school ultimately belong to this generalization ‘Person’ as they all share these common properties. Suppose we are required to store details of all the teachers and students present in a school. The straightforward solution would be to create two classes — ‘Student’ and ‘Teacher’ with their respective attributes and methods and create their instances corresponding to the teachers and students present in the school. These classes will have many common attributes and methods like dateOfBirth, address, getAge(), etc. Code will like as shown below:

class Teacher {
    private String firstName;
    private String lastName;
    private String address;
    private Integer age;

    private Long employeeId;
    private Long salary;
    private String grade;
    // ... more properties and methods
}
class Student {
    private String firstName;
    private String lastName;
    private String address;
    private Integer age;

    private Integer rollNumber;
    private Integer englishMarks;
    private Integer scienceMarks;
    private Integer mathMarks;
    // ... more properties and methods
}

This problem of redundant code can be solved using inheritance. Inheritance is a mechanism by which one class can inherit the properties and methods of another class. So it enables us to mimic the concept of real-world hierarchy in programming.

In our particular example, we need to create the parent class ‘Person’ with attributes and methods common to all people in general. ‘Student’ class and ‘Teacher’ class will then inherit those common properties from the ‘Person’ class while defining fields and methods that are unique to them.

public class Person {
    protected String firstName;
    protected String lastName;
    protected String address;
    protected Integer age;
}
class Teacher extends Person {
    private Long employeeId;
    private Long salary;
    private String grade;
    // ... more properties and methods
}
class Student extends Person {
    private Integer rollNumber;
    private Integer englishMarks;
    private Integer scienceMarks;
    private Integer mathMarks;
    // ... more properties and methods

    public String getFullName() {
        return firstName + lastName;
    }
}

We can see that now Student and Teacher classes only need to define their unique properties, and the common properties can be defined in the parent class ‘Person’. The getFullName() method in Student class is just for demonstration purposes to show that Student objects have actually inherited properties from the parent class.

You can learn more about inheritance here: Inheritance in Object Oriented Programming

4. Polymorphism

Polymorphism means having multiple forms. In terms of programming, it means using one interface to perform different operations based on the context. A typical example of this is the ‘+’ operator. If we do 5 + 5, we get 10 as the result. However, if we do “5” + 5, where the first operand is a string, we get “55” as the result, as all the operands are first converted to a string and then concatenated. In this example, we used the same operator ‘+’ but provided different types of arguments (operands), resulting in different operations actually being performed on those arguments. Polymorphism is usually implemented by method overloading and method overriding in Java, which will be covered in detail in a different post.

Explore these blogs to learn more about Polymorphism:

Advantages of Object-Oriented Programming

Now that we have covered the basic concepts of OOPS, it would be easier to understand its advantages of the same. Here are some important advantages of oops:

  • It prevents redundant code as discussed in inheritance above.
  • It helps us map real-world entities to objects that we can use in code.
  • It makes our code more modular and thus, reusable. It also makes the code extensible as adding a new feature to a class requires modification only in that class and not in other parts of the program.
  • It makes our code easy to maintain as it’s structured in logical components.
  • It facilitates data hiding via encapsulation. So if we make our data members private, we can restrict access to them and make them accessible only from the methods inside the class. This helps in enforcing a set of rules (like restricting access to certain data members), which might otherwise prove complicated to implement while also requiring extra coding efforts.
  • It offers abstraction and thus, the users don’t need to worry about the implementation details.
  • Polymorphism helps us to use the same method name for different operations (by using different method signatures). This makes it easier for other programmers to use our code as they don’t need to use different method names for the same operation.

Critical OOPS concepts to explore further

Enjoy learning, Enjoy OOPS!

Share feedback with us

More blogs to explore

Our weekly newsletter

Subscribe to get weekly content on data structure and algorithms, machine learning, system design and oops.

© 2022 Code Algorithms Pvt. Ltd.

All rights reserved.