Table of contents
1.
Introduction
2.
Class in Java
3.
Types of Classes
4.
Final Class
5.
Static Class
6.
Abstract Class
7.
Concrete Class
8.
POJO Class
9.
Singleton Class
10.
Inner Class
11.
Wrapper Class
12.
Frequently Asked Questions
12.1.
Can we declare a class as both abstract & final?
12.2.
What is the purpose of a private constructor in a singleton class?
12.3.
Can a local inner class access non-final local variables?
13.
Conclusion
Last Updated: Nov 18, 2024
Medium

Types of Classes in Java

Author Rahul Singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Classes in Java act like the building blocks that help programmers to create robust and efficient software systems. A class is like a blueprint that defines the properties (attributes) and behaviors (methods) that objects of that class will possess. With the encapsulation of related data and functionality within a class, Java provides a modular and organized approach to coding. This not only enhances code reusability but also improves maintainability, as classes can be easily modified or extended without affecting the entire codebase. 

In this article, we will look into each of these class types, explaining their definitions, use cases, and syntactical implementations.

Class in Java

In Java, a class is a fundamental concept that serves as a template or blueprint for creating objects. It defines the common properties (attributes) & behaviors (methods) that objects of that class will possess. When you create an object from a class, it is known as an instance of that class.

For example: 

 Person {
    // Properties
    private String name;
    private int age;
    
    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Methods
    public void sayHello() {
        System.out.println("Hello, my name is " + name + " and I'm " + age + " years old.");
    }
}


In this example, we have a `Person` class with two properties: `name` (of type String) and `age` (of type int). The class also has a constructor that allows us to initialize these properties when creating a new instance of the `Person` class. Additionally, there is a method called `sayHello()` that prints a greeting message along with the person's name and age.

To create an object (instance) of the `Person` class, you would use the following code:

Person person1 = new Person("John", 25);
person1.sayHello();


This code creates a new `Person` object called `person1` with the name "John" and age 25. It then calls the `sayHello()` method on the `person1` object, which will output: "Hello, my name is John, and I'm 25 years old."

Types of Classes

Java offers several types of classes, each serving a specific purpose & having unique characteristics. Let's explore the different types of classes available in Java:

1. Final Class
 

2. Static Class
 

3. Abstract Class
 

4. Concrete Class
 

5. POJO Class
 

6. Singleton Class
 

7. Inner Class
 

8. Wrapper Class


Note: Each of these class types has distinct features and use cases. 

Final Class

A final class in Java is a class that cannot be inherited by other classes. When a class is declared final, it means that it is the final version of the class and cannot be extended or subclassed. Final classes are often used to prevent unintended inheritance and ensure that the class's behavior remains consistent.

For example : 

public final class MathUtils {
    public static int square(int num) {
        return num * num;
    }
}


In this example, the `MathUtils` class is declared as `final`, indicating that it cannot be subclassed. It contains a static method called `square()` that calculates the square of a given number.

Final classes are commonly used for utility classes that provide a collection of static methods, such as mathematical functions or string manipulation methods. By making the class final, you prevent other classes from modifying or extending its behavior, which ensures that the class remains focused on its specific purpose.

Static Class

In Java, a static class is a nested class that is declared as static. It is a class within another class, but unlike inner classes, a static class does not have access to the instance members of the enclosing class. Static classes are associated with the enclosing class itself, rather than instances of the enclosing class.

For example: 

public class OuterClass {
    private static int outerStaticVariable = 10;
    
    public static class StaticNestedClass {
        public void printMessage() {
            System.out.println("Outer static variable: " + outerStaticVariable);
        }
    }
}


In this example, we have an `OuterClass` that contains a static variable, `outerStaticVariable,` and a static nested class called `StaticNestedClass`. The `StaticNestedClass` has access to the static members of the `OuterClass`, such as `outerStaticVariable`, but it cannot access any instance members.

To create an instance of a static class, you don't need an instance of the enclosing class. You can directly create an instance of the static class using the following syntax:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
nestedObject.printMessage();


Static classes are used for grouping related utility methods or constants that do not require access to instance members of the enclosing class. They provide a way to organize & encapsulate functionality that is closely associated with the enclosing class but does not depend on its instances.

Important points to remember about static classes:

  • Static classes can only access static members of the enclosing class directly.
     
  • Static classes cannot access instance members of the enclosing class without an instance.
     
  • Static classes can be instantiated independently without creating an instance of the enclosing class.
     
  • Static classes are commonly used for utility classes, constants, or other static-related functionality.

Abstract Class

An abstract class in Java is a class that is declared with the `abstract` keyword and may contain abstract methods. An abstract method is a method that is declared without an implementation (without braces, followed by a semicolon). Abstract classes cannot be instantiated directly, but they can be subclassed by other classes.

For example: 

public abstract class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    public abstract double getArea();
    
    public void printColor() {
        System.out.println("Color: " + color);
    }
}


In this example, we have an abstract class called `Shape`. It has a constructor that takes a `color` parameter and initializes the `color` instance variable. The class also contains an abstract method `getArea()`, which is declared without an implementation. Additionally, there is a non-abstract method `printColor()` that prints the color of the shape.

To use an abstract class, you need to create a concrete subclass that extends the abstract class and provides implementations for all the abstract methods. 

For example: 

public class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}


In this example, the `Circle` class extends the abstract `Shape` class. It provides an implementation for the abstract `getArea()` method, calculating the area of a circle based on its radius.

Abstract classes are used when you want to define a common base class that contains shared properties and methods, but you don't want the base class to be instantiated directly. They provide a way to define a contract or template for subclasses to follow, enforcing the implementation of certain methods.

Some key points about abstract classes:

  • Abstract classes can have both abstract and non-abstract methods.
     
  • Abstract methods have no implementation in the abstract class and must be implemented by concrete subclasses.
     
  • Abstract classes cannot be instantiated directly, but they can be subclassed.
     
  • Abstract classes can have constructors, which are called by the subclass constructor using the `super` keyword.
     
  • Abstract classes can have instance variables and static members.

Concrete Class

A concrete class in Java is a class that provides implementations for all the methods declared in its parent class or interface. Unlike abstract classes, concrete classes can be instantiated and do not contain any abstract methods. They represent fully functional classes that can be used to create objects.

For example: 

public class Rectangle extends Shape {
    private double width;
    private double height;
    
    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return width * height;
    }
}


In this example, the `Rectangle` class extends the abstract `Shape` class from the previous example. It provides an implementation for the abstract `getArea()` method, which calculates the area of a rectangle based on its width and height. The `Rectangle` class also has its own constructor that takes the `color`, `width`, and `height` parameters.

Concrete classes are the most common type of classes in Java. They represent fully implemented classes that can be instantiated & used to create objects. Concrete classes can inherit from other classes (abstract or concrete) & implement interfaces.

Important points to remember about concrete classes:

  • Concrete classes provide implementations for all the methods declared in their parent class or interface.
     
  • Concrete classes can be instantiated using the `new` keyword.
     
  • Concrete classes can have their own constructors, instance variables, & methods.
     
  • Concrete classes can inherit from other classes & implement interfaces.
     
  • Concrete classes are used to define specific, concrete types of objects that can be used in a program.

For example: 

Rectangle rectangle = new Rectangle("Red", 5.0, 3.0);
double area = rectangle.getArea();
rectangle.printColor();


In this example, we create an instance of the `Rectangle` class with the color "Red," width 5.0, and height 3.0. We can then call methods on the `rectangle` object, such as `getArea()` to calculate the area and `printColor()` to print the color of the rectangle.

POJO Class

POJO stands for Plain Old Java Object. It is a simple, lightweight Java object that follows certain conventions. A POJO class is a regular Java class that does not extend or implement any specialized classes or interfaces from a particular framework. It is often used to represent data or transfer objects in Java applications.

The characteristics of a POJO class are: 

  • It should have a no-argument constructor (default constructor).
     
  • It should provide getter and setter methods for accessing its properties.
     
  • It should not extend or implement any specialized classes or interfaces.
     
  • It should not have any dependencies on a particular framework.


For example: 

public class Person {
    private String name;
    private int age;
    
    public Person() {
        // Default constructor
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
}


In this example, the `Person` class is a POJO. It has a no-argument constructor, getter, and setter methods for the `name` and `age` properties and does not extend or implement any specialized classes or interfaces.

POJO classes are used in various scenarios, like:

  • Representing data entities or domain objects in an application.
     
  • Transferring data between different layers of an application (e.g., between the presentation layer and the business layer).
     
  • Serializing and deserializing objects for storage or network transmission.
     
  • Encapsulating data for use in frameworks like Spring or Hibernate.


The main advantage of using POJO classes is their simplicity and independence from specific frameworks. They provide a clean and straightforward way to represent and manipulate data in a Java application.

Let’s discuss more important points about POJO classes:

  • POJO classes should follow the Java naming conventions (e.g., using camelCase for property names).
     
  • POJO classes can have additional methods besides getters and setters, but they should not contain complex business logic.
     
  • POJO classes are often used in conjunction with annotations or configuration files to provide additional metadata or mapping information.

Singleton Class

A singleton class is a class that allows only one instance of itself to be created throughout the lifetime of a program. It ensures that there is only a single instance of the class available globally, and provides a way to access that instance from any part of the code.

The main characteristics of a singleton class are:

  • Private constructor: The class has a private constructor, which prevents direct instantiation of the class from outside.
     
  • Static instance: The class maintains a static instance of itself, which is the sole instance of the class.
     
  • Global access point: The class provides a public static method (often named `getInstance()`) that returns the single instance of the class.


For example : 

public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    private DatabaseConnection() {
        // Private constructor to prevent direct instantiation
    }
    
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    // Other methods and properties specific to the DatabaseConnection class
    public void connect() {
        // Code to establish a database connection
    }
    
    public void disconnect() {
        // Code to close the database connection
    }
}


In this example, the `DatabaseConnection` class is a singleton. It has a private constructor to prevent direct instantiation. The class maintains a static `instance` variable to hold the single instance of the class. The `getInstance()` method is responsible for creating the instance if it doesn't exist and returning the single instance.

To use the singleton class, you would call the `getInstance()` method to obtain the single instance of the class:

DatabaseConnection connection = DatabaseConnection.getInstance();
connection.connect();
// Perform database operations
connection.disconnect();


Singleton classes are commonly used in scenarios where you want to ensure that only one instance of a class is created and shared across the application. 

Some use cases for singleton classes are:

  • Managing global resources, such as database connections or configuration settings.
     
  • Coordinating access to shared resources or services.
     
  • Implementing caches or pools of reusable objects.
     
  • Providing a centralized point of control for certain operations.
     

Note: Always remember that the singleton pattern should be used carefully because it can introduce global state and make testing and maintenance more challenging if overused or misused.

Inner Class

An inner class in Java is a class that is defined inside another class. It is a member of the enclosing class and has access to the members (including private members) of the enclosing class. Inner classes are useful for organizing related classes, encapsulating helper classes, or defining callbacks or event handlers.

There are several types of inner classes in Java:

1. Member Inner Class: A member inner class is defined as a member of the enclosing class. It has access to all the members (including private members) of the enclosing class.

Example:

   public class OuterClass {
       private int outerValue = 10;
       public class InnerClass {
           public void printValue() {
               System.out.println("Outer value: " + outerValue);
           }
       }
   }


2. Local Inner Class: A local inner class is defined inside a method or a block of code. It has access to the final local variables of the enclosing method.

Example:

   public class OuterClass {
       public void someMethod() {
           final int localValue = 20;
           class LocalInnerClass {
               public void printValue() {
                   System.out.println("Local value: " + localValue);
               }
           }
           LocalInnerClass innerObj = new LocalInnerClass();
           innerObj.printValue();
       }
   }


3. Anonymous Inner Class: An anonymous inner class is a local inner class without a name. It is defined and instantiated in a single statement. Anonymous inner classes are often used to implement interfaces or extend abstract classes on the fly.

Example:

   public class OuterClass {
       public void someMethod() {
           Runnable runnable = new Runnable() {
               @Override
               public void run() {
                   System.out.println("Running in an anonymous inner class");
               }
           };
           runnable.run();
       }
   }


4. Static Inner Class: A static inner class is defined as a static member of the enclosing class. It can only access the static members of the enclosing class directly.

Example:

   public class OuterClass {
       private static int staticValue = 30;
       public static class StaticInnerClass {
           public void printValue() {
               System.out.println("Static value: " + staticValue);
           }
       }
   }


When this is helpful-: Inner classes provide a way to logically group related classes, encapsulate helper functionality, and create more modular and readable code. They are commonly used in event-driven programming, GUI development, and design patterns like the Observer pattern.

Wrapper Class

In Java, a wrapper class is a class that wraps (encapsulates) a primitive data type, which provides an object-oriented representation of the primitive value. Each primitive data type in Java has a corresponding wrapper class. The main purpose of wrapper classes is to allow primitive values to be treated as objects, enabling them to be used in scenarios where objects are required, such as collections or generic types.

The wrapper classes in Java for each primitive data type are:

- `byte` → `Byte`
 

- `short` → `Short`
 

- `int` → `Integer`
 

- `long` → `Long`
 

- `float` → `Float`
 

- `double` → `Double`
 

- `char` → `Character`
 

- `boolean` → `Boolean`


Wrapper classes provide several useful features:


1. Object Representation: Wrapper classes allow you to treat primitive values as objects. This is useful when you need to store primitive values in collections or pass them as method parameters that expect objects.

Example:

   ArrayList<Integer> numbers = new ArrayList<>();
   numbers.add(10);
   numbers.add(20);


2. Autoboxing and Unboxing: Java provides autoboxing and unboxing mechanisms to automatically convert between primitive types and their corresponding wrapper classes. Autoboxing converts a primitive value to its wrapper object, while unboxing converts a wrapper object to its primitive value.

Example:

   int primitiveValue = 10;
   Integer wrapperObject = primitiveValue; // Autoboxing
   int unboxedValue = wrapperObject; // Unboxing


3. Utility Methods: Wrapper classes provide various utility methods for parsing, converting, and manipulating primitive values.

Example:

   String strValue = "123";
   int intValue = Integer.parseInt(strValue); // Parsing a string to an int
   String binaryString = Integer.toBinaryString(intValue); // Converting an int to a binary string


4. Null Values: Since wrapper classes are objects, they can be assigned a null value, representing the absence of a value. This is not possible with primitive types.

Example:

   Integer nullableValue = null;


Wrapper classes are commonly used in scenarios like:

  • Storing primitive values in collections (e.g., ArrayList, HashMap).
     
  • Passing primitive values as method parameters or return types that expect objects.
     
  • Utilizing utility methods provided by wrapper classes for parsing, converting, and manipulating primitive values.
     
  • Handling null values when a primitive value may be absent.
     

Note: Just remember that while wrapper classes give additional functionality, they also introduce some issues if compared to using primitive types directly. In performance-critical scenarios, it's usually recommended to use primitive types whenever possible to avoid the cost of autoboxing and unboxing.

Frequently Asked Questions

Can we declare a class as both abstract & final?

No, a class cannot be declared as both abstract & final in Java. An abstract class is meant to be subclassed, while a final class cannot be extended.

What is the purpose of a private constructor in a singleton class?

A private constructor in a singleton class prevents direct instantiation of the class from outside, ensuring that only one instance of the class can be created through the designated getInstance() method.

Can a local inner class access non-final local variables?

No, a local inner class can only access final local variables of the enclosing method. This is because the local variables must be final or effectively final to be captured by the local inner class.

Conclusion

In this article, we discussed the different types of classes in Java, such as final, static, abstract, concrete, POJO, singleton, inner, and wrapper classes. Each class type serves a specific purpose and has its own characteristics. Knowing when and how to use each type of class is crucial for designing and implementing efficient and maintainable Java programs. 

You can also check out our other blogs on Code360.

Live masterclass