Class Types in Java

  • Last Updated: March 3, 2025
  • By: javahandson
  • Series
img

Class Types in Java

Discover the different class types in Java, including Concrete Classes, Abstract Classes, Interfaces, Nested Classes, and Anonymous Classes. Understand their roles, use cases, and how they contribute to object-oriented programming in Java.

 

In Java, classes can be categorized into different types based on their behavior, usage, and the way they are defined. The key types of classes are:

  • Concrete Classes
  • Abstract Classes
  • Interfaces
  • Nested Classes
  • Anonymous Classes

Concrete Class

A concrete class in Java is a class that has a complete implementation and can be instantiated. It provides full functionality with method definitions and can have instance variables, constructors, and methods. Unlike abstract classes and interfaces, which serve as blueprints, a concrete class is ready for direct use in object creation.

Characteristics of a Concrete Class

1. Instantiable – Objects can be created directly from a concrete class using the new keyword.

2. Fully Implemented Methods – All methods in a concrete class have complete implementations.

3. Can Extend Other Classes – A concrete class can inherit from another class (including an abstract class) and must provide implementations for any abstract methods.

4. Can Implement Interfaces – A concrete class can implement one or more interfaces and must provide implementations for all their abstract methods.

class Car {
    String model;
    int speed;

    // Constructor
    Car(String model, int speed) {
        this.model = model;
        this.speed = speed;
    }

    // Method
    void displayCarInfo() {
        System.out.println("Car Model: " + model);
        System.out.println("Car Speed: " + speed);
    }
}

public class Main {
    public static void main(String[] args) {
        Car car1 = new Car("Tesla", 120);
        car1.displayCarInfo();
    }
}

Car is a concrete class in the above example because it is fully implemented and can be instantiated to create objects.

Abstract Class

An abstract class in Java is a class that serves as a blueprint for other classes. It cannot be instantiated on its own and is meant to be extended by subclasses that provide implementations for its abstract methods. Abstract classes are used to define common behavior that multiple related classes can share.

Characteristics of an Abstract Class

1. Cannot be Instantiated – You cannot create objects of an abstract class directly.

2. Can Have Abstract Methods – Abstract methods are methods without a body that must be implemented by subclasses.

3. Can Have Concrete Methods – Unlike interfaces, an abstract class can have methods with full implementations.

4. Can Have Instance Variables – Abstract classes can declare instance variables like regular classes.

5. Can Have Constructors – Though an abstract class cannot be instantiated, it can have constructors that are called when a subclass is instantiated to initialize common fields to all subclasses.

abstract class Animal {
    abstract void sound();  // Abstract method (no body)

    void breathe() {  // Non-abstract method (with body)
        System.out.println("Animal is breathing.");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Woof! Woof!");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.sound();  // Calling overridden method
        dog.breathe();  // Calling inherited method
    }
}

In the above example, Animal is an abstract class, and Dog is a concrete subclass that implements the sound() method.

When to Use an Abstract Class?

1. When you want to enforce a common structure for multiple related classes.

2. When you have some shared code among subclasses but still want to enforce specific methods to be implemented by each subclass.

3. When you need to define methods with implementations that all subclasses can inherit.

An abstract class is ideal for situations where multiple classes share common functionality but also require their own specific behavior. It provides the flexibility of partially implemented classes while enforcing a contract for subclasses.

Interface

An interface in Java is a collection of abstract methods and constants that define a contract for classes to follow. Interfaces provide a way to achieve multiple inheritance and abstraction in Java. Any class that implements an interface must provide implementations for all its abstract methods unless declared as abstract itself.

Characteristics of an Interface

1. Cannot Have Constructors – Unlike abstract classes, interfaces cannot have constructors because they cannot be instantiated.

2. Only Abstract Methods (Before Java 8) – Before Java 8, interfaces could only have abstract methods (without implementation).

3. Default and Static Methods (Since Java 8) – Java 8 introduced default and static methods in interfaces, allowing them to have method implementations.

4. Private Methods (Since Java 9) – Java 9 introduced private methods inside interfaces to allow code reuse.

5. All Methods Are Public and Abstract by Default – Explicitly using public abstract is optional.

6. All Variables Are Public, Static, and Final – Interface fields act as constants.

interface Vehicle {
    void start();  // Abstract method (no body)
}

class Car implements Vehicle {
    @Override
    public void start() {
        System.out.println("Car is starting.");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();  // Calling method implemented from interface
    }
}

In the above example, Vehicle is an interface, and Car implements the start() method defined in the interface.

Write an Interface with Default and Static Methods (Java 8 and Later)

// Interface with default and static methods
interface Student {
    void study(); // Abstract method

    // Default method (Introduced in Java 8)
    default void submitAssignment() {
        System.out.println("Assignment submitted.");
    }

    // Static method (Introduced in Java 8)
    static void schoolInfo() {
        System.out.println("Welcome to Mount carmel convent School.");
    }
}

// Implementing class
class CollegeStudent implements Student {
    private String name;

    CollegeStudent(String name) {
        this.name = name;
    }

    @Override
    public void study() {
        System.out.println(name + " is studying.");
    }
}

public class Main {
    public static void main(String[] args) {
        CollegeStudent student = new CollegeStudent("Suraj");
        student.study();             // Output: Suraj is studying.
        student.submitAssignment();  // Output: Assignment submitted.

        // Calling static method of interface
        Student.schoolInfo();        // Output: Welcome to Mount carmel convent School.
    }
}

When to Use an Interface?

1. When you want to define a contract that multiple classes must follow.

2. When you need multiple inheritance, Java allows a class to implement multiple interfaces but not extend multiple classes.

3. When you want to separate implementation from definition and allow flexibility in extending functionality.

Interfaces are a fundamental part of Java’s polymorphism and abstraction, making code more modular, reusable, and scalable.

Nested Classes

A nested class is a class defined inside another class. Nested classes are used to logically group related classes, improve encapsulation, and make the code more readable and maintainable.

Types of Nested Classes

Nested classes in Java are categorized into two types:

1. Static Nested Class – A static class inside another class.

2. Non-Static Nested Class (Inner Class) – A class that belongs to an instance of the outer class. It has three types:

  • Member Inner Class
  • Local Inner Class
  • Anonymous Inner Class

Static Nested Class

A static nested class is associated with the outer class, but it does not require an instance of the outer class to be instantiated.

class OuterClass {
    static int staticVar = 100;

    static class NestedClass {
        void display() {
            System.out.println("Static Variable: " + staticVar);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass.NestedClass nestedObj = new OuterClass.NestedClass();
        nestedObj.display();
    }
}

In the above example, NestedClass is a static nested class because it is defined with the static keyword and can be instantiated without an outer class instance.

Non-Static Nested Class (Inner Class)

An inner class is associated with an instance of the outer class. It requires an instance of the outer class to be instantiated.

class OuterClass {
    int outerVar = 50;

    class InnerClass {
        void display() {
            System.out.println("Outer Class Variable: " + outerVar);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass outerObj = new OuterClass();
        OuterClass.InnerClass innerObj = outerObj.new InnerClass();
        innerObj.display();
    }
}

In the above example, InnerClass is a non-static inner class, and it can access the instance variable of the outer class.

Anonymous Classes

An anonymous class is a type of inner class that is declared and instantiated in a single statement. It does not have a name and is primarily used when a one-time implementation of a class or interface is needed.

Anonymous classes are commonly used in event handling, thread creation, and inline implementations of interfaces or abstract classes.

Characteristics of Anonymous Classes

1. No Explicit Class Name – The class is defined without a name.

2. Single Instance Usage – It is used for a one-time instantiation.

3. Can Extend a Class or Implement an Interface – But not both simultaneously.

4. Can Access Outer Class Members – even private members of the enclosing class.

5. Can Access Local Variables – But they must be final or effectively final (unchanged after assignment).

interface Greeting {
    void greet();
}

public class Main {
    public static void main(String[] args) {
        Greeting greeting = new Greeting() {  // Anonymous class
            @Override
            public void greet() {
                System.out.println("Java HandsOn");
            }
        };
        greeting.greet();  // Calling the method
    }
}

In the above example, we created an anonymous class that implements the Greeting interface and overrides the greet() method.

Write a program to access Outer Class Members (Including Private Members)

class Outer {
    private String message = "Hello from Outer Class";

    void display() {
        System.out.println("Outer class method.");
    }

    void createAnonymous() {
        // Anonymous class inside a method
        new Object() {
            void show() {
                System.out.println(message); // Accessing private member
                display(); // Accessing private method
            }
        }.show(); // Calling method of anonymous class
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.createAnonymous();
    }
}
Output:
Hello from Outer Class
Outer class method.

Write a program to access local variables (Must Be Final or Effectively Final)

interface Printer {
    void print();
}

public class Main {
    public static void main(String[] args) {
        String localVar = "Local Variable"; // Effectively final

        Printer printer = new Printer() {
            @Override
            public void print() {
                System.out.println(localVar); // Accessing local variable
            }
        };

        printer.print(); 
    }
}
Output: Local Variable

Limitations of Anonymous Classes

1. Cannot Have Constructors – Since the class has no name, constructors cannot be explicitly defined.

2. Cannot Implement Multiple Interfaces – Unlike regular classes, an anonymous class can implement only one interface or extend only one class.

3. Code Readability Issue – If overused, anonymous classes can make code harder to read and debug.

When to Use Anonymous Classes?

1. When you need a one-time implementation of a class or interface.

2. Defining a separate class would be unnecessary and would increase code complexity.

3. In event listeners, callbacks, and small helper classes.

Conclusion

Java provides a variety of class types to support different programming needs, from fully implemented concrete classes to abstract classes and interfaces that define blueprints for other classes. Nested and anonymous classes further enhance modularity and flexibility. Understanding these class types allows developers to write more maintainable, reusable, and efficient code. By leveraging the right class type in the right scenario, you can create robust Java applications that follow best coding practices.

So this is all about class types in Java. If you have any questions on this topic, please raise them in the comments section. If you liked this article, then please share this post with your friends and colleagues.

Leave a Comment