OOP Concepts in Java

OOP - Object-Oriented Programming

Object-oriented Programming is a methodology or paradigm to design a program using classes and objects. It simplifies the software development and maintenance by providing some concepts:

Procedural-oriented Programs VS OOP

Procedural-oriented ProgramsObject-oriented Programs
The procedural-oriented programs are based on functions. Functions are less reusable. It is very difficult to copy a function from one program and reuse in another program because the function is likely to reference the global variables and other functions. In other words, functions are not well-encapsulated as a self-contained reusable unit.OOP grouping data structures and algorithms of a software entity inside a "box". The basic unit of OOP is a class, which encapsulates both properties and methods. Class also specifies the public interface for external objects to use this "box". Since classes are well-encapsulated, it is easier to reuse these classes, ex: by inheritance or by instantiation.
The procedural languages (such as C, Fortran, Cobol and Pascal) are not suitable of high-level abstraction for solving real life problems. because forced programmers to think in low-level on how to use if-else, for-loop, array, method, pointer, which is very hard to abstract real problems.OOP languages permit higher level of abstraction for solving real-life problems. OOP languages (such as Java, C++ and C#) let you think in the problem space, and use software objects to represent and abstract entities of the problem space to solve the problem.

Class

A class is a blueprint or prototype that defines and describes the attributes and behaviors common to all objects of the same kind. Class is a logical entity from which an object can be created.

As example, we can have class Student.

Object

An Object can be defined as an instance of a class. An instance is a realization of a particular item of a class. In other words, an instance is an instantiation of a class. All the instances of a class have similar properties, as described in the class definition.

As example, from class Student, we can have three instances "Bill", "Paul", and "Steve".

In Java, we use the keyword class to define a class. The syntax for class definition in Java is:

[accessModifier] class ClassName { // class body contains members (variables and methods) ...... }

Class naming convention: A class name shall be a noun or a noun phrase made up of several words. All the words shall be initial-capitalized (camel-case). Use a singular noun for class name. Choose a meaningful and self-descriptive class name. For examples: Student, Employee, Files, SecurityManager, SpringApplication, etc.

The variables and methods belonging to a class are formally called member variables and member methods.

The syntax for variable definition in Java is:

[accessModifier] type variableName [= initialValue]; [accessModifier] type variableName1 [= initialValue1] [, type variableName2 [= initialValue2]] ... ;

Variable Naming Convention: A variable name shall be a noun or a noun phrase made up of several words. The first word is in lowercase and the rest of the words are initial-capitalized (camel-case), e.g., height, firstName, birthDate, and mailingAddress1.

The syntax for method declaration in Java is as follows:

[accessModifier] returnType methodName ([parameterList]) { // method body or implementation ...... }

Method Naming Convention: A method name shall be a verb, or a verb phrase made up of several words. The first word is in lowercase and the rest of the words are initial-capitalized (camel-case). For example, getLastName(), setBirthDate(), calculateDiscount(), and isPrimeNumber().

About accessModifier, please check Encapsulation in Java.

Now, let's create our Student class:

Student.java
import java.time.LocalDate;

public class Student {
    
    private String firstName;
    private String lastName;
    private LocalDate birthDate;

    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * @return the birthDate
     */
    public LocalDate getBirthDate() {
        return birthDate;
    }

    /**
     * @param birthDate the birthDate to set
     */
    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }
    
    public void display() {
        System.out.println("Student's name: " + this.firstName + " " + this.lastName);
        System.out.println("Student's birthdate:" + this.birthDate.toString());
    }
}
                    

To create an instance of a class, you have to:

  1. Declare an instance identifier (instance name) of a particular class.
  2. Initialize the instance using the "new" operator.

For examples, we can create instances of Student as follows:

Student student1 = new Student();
Student student2;
student2 = new Student();
                    

When an instance is declared but not initialized, it holds a special value called null.

To reference a member variable or method, you must use the dot operator (.) in the instance to reference the desired member variable or method.

student1.setFirstName("Bill");
student1.setLastName("Gates");
student1.setBirthDate(LocalDate.of(2005, Month.OCTOBER, 28));
student1.display();

student2.setFirstName("Steve");
student2.setLastName("Ballmer");
student2.setBirthDate(LocalDate.of(2006, Month.MARCH, 24));
student2.display();
                    

class Student contains three private member variables: firstName (String), lastName (String), and birthDate (LocalDate); and six public member methods: getFirstName(), setFirstName(..), getLastName(), setLastName(..), getBirthDate(), and setBirthDate(..). Do you notice all the set* and get* methods?

The public Getters and Setters

To allow other classes to read the value of a private variable says xxx, we provide a get method (or getter or accessor method) called getXxx(). A get method needs not expose the data in raw format. It can process the data and limit the view of the data others will see. The getters shall not modify the variable.

To allow other classes to modify the value of a private variable says xxx, we provide a set method (or setter or mutator method) called setXxx(). A set method could provide data validation (such as range checking), or transform the raw data into the internal representation.

For example, in our Student class, the variable firstName declared as private. That is to say, they are only accessible within the Student class and not visible in any other classes. Student class provides public accessor method getFirstName() that declared as public. Other classes now can invoke these public accessor methods to retrieve the firstName of Student class instances.

Constructors

A constructor looks like a special method that has the same method name as the class name. In the above Student class, we can create a constructor to accept firstName and lastName:

public Student(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}
                    

A constructor is different from an ordinary method in the following aspects:

  • The name of the constructor method is the same as the class name. By class name's convention, it begins with an uppercase (instead of lowercase for ordinary methods).
  • Constructor has no return type. It implicitly returns void. No return statement is allowed inside the constructor's body.
  • Constructor can only be invoked via the "new" operator. It can only be used once to initialize the instance constructed. Once an instance is constructed, you cannot call the constructor anymore.
  • Constructors are not inherited.

Default Constructor: A constructor with no parameter is called the default constructor. It initializes the member variables to their default value. For example:

public Student() {
    // can do something/initialization here
}
                    

but we doesn't need default constructor for now, and we forced firstName and lastName initialization when the Student class is instantiated via it's constructor. We also can remove setFirstName(..) and setLastName(..), so when we putting together our Student class will looks like:

Student.java
import java.time.LocalDate;
import java.time.Month;

public class Student {
    
    private final String firstName;
    private final String lastName;
    private LocalDate birthDate;

    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @return the birthDate
     */
    public LocalDate getBirthDate() {
        return birthDate;
    }

    /**
     * @param birthDate the birthDate to set
     */
    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }
    
    public Student(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    
    public void display() {
        System.out.println("Student's name: " + this.firstName + " " + this.lastName);
        System.out.println("Student's birthdate:" + this.birthDate.toString());
    }
    
    public static void main(String[] args) {
        Student student1 = new Student("Bill", "Gates");
        Student student2;
        student2 = new Student("Steve", "Ballmer");
        
        student1.setBirthDate(LocalDate.of(2005, Month.OCTOBER, 28));
        student1.display();

        student2.setBirthDate(LocalDate.of(2006, Month.MARCH, 24));
        student2.display();
    }
}
                    

Do you notice the final statement in front of firstName and lastName variable declaration?

And when we run, we get the result:

Student's name: Bill Gates Student's birthdate:2005-10-28 Student's name: Steve Ballmer Student's birthdate:2006-03-24

Package

A package is a namespace that organizes a set of related classes and interfaces. Conceptually you can think of packages as being similar to different folders on your computer. Because software written in the Java programming language can be composed of hundreds or thousands of individual classes, it makes sense to keep things organized by placing related classes and interfaces into packages.

Some well known packages in Java APIs are:

  • java.io - provides for system input and output through data streams, serialization and the file system.
  • java.lang - provides classes that are fundamental to the design of the Java programming language.
  • java.net - provides the classes for implementing networking applications.
  • java.nio - defines buffers, which are containers for data, and provides an overview of the other NIO packages.
  • java.util - contains the collections framework, legacy collection classes, event model, date and time facilities, internationalization, and miscellaneous utility classes (a string tokenizer, a random-number generator, and a bit array).
  • etc...


Creating package

The package statement should be the first line in the source file. There can be only one package statement in each source file, and it applies to all types in the file.

package com.dariawan.codes;

public class Student {

    // class members here
}
                    

The class file also need to be in the package/folder in the same directory structure as the package. In above example, Student class must be under com/dariawan/codes folder of source root folder.

The import Keyword

If a class wants to use another class in the same package, the package name need not be used. Classes in the same package find each other without any special syntax. But if the class is in different package, we need to do import.

import com.dariawan.codes.Student;
                    

or, to do full package import

import com.dariawan.codes.*;
                    

Best Practices for OOP

Here some best practices in OOP, to save your time without sacrificing security and ease of use:

  • DRY (Don’t Repeat Yourself). You should never have two blocks of identical code in two different places. Instead, have one that can be used for different applications.
  • Single Responsibility. Simply put, a class should always have only one functionality, one specialization. That way, it can be called and/or extended on its own when new uses arise for it, without causing coupling between different functionalities. Don't create a general class that do everything: access database, create logs, doing validations, etc.
  • Open Closed Design. Make all methods and classes Closed for modification but Open for an extension. That way, tried and tested code can remain static but can be modified to perform new tasks as needed. Even if you expect your Java code to change in the future, you should encapsulate it by making all variables and methods access modifiers as restricted as possible, and slowly refactor it.

Putting it together, all the OOP concepts like Inheritance, Polymorphism, Encapsulation, and Abstraction should applicable here.