Core Concepts of Object-Oriented Programming in Java

Key Principles of Object-Oriented Programming

Object-oriented programming (OOP) in Java is built on three founadtional pillars: inheritance, encapsulation, and polymorphism.

Inheritance

  • Java supports single inheritance for classes—each class can extend only one direct superclass—but allows multiple interface implementation.
  • During subclass instantiation, the superclass constructor is invoked first. If the superclass declares only parameterized constructors, the subclass must explicitly call one using super(...); otherwise, compilation fails due to the absence of a default no-arg constructor.
  • Member resolution follows the nearest scope principle: if a subclass defines a field or method with the same name as one in its superclass, the subclass version takes precedence. To access the superclass member explicitly, use the super keyword.
  • When a subclass provides a specific implementation of a method already defined in its superclass, it is called method overriding (@Override). In contrast, method overloading (same name, different parameters) is a compile-time polymorphism feature.

Encapsulation

  • The this keyword refers to the current instance and is used to disambiguate between instance variables and local parameters (e.g., this.name = name).
  • Encapsulation promotes data hiding by restricting direct access to internal state, typically through private fields and public getter/setter methods.
  • Refactoring in OOP often involves identifying common behavior and abstracting it into superclasses or interfaces.

Polymorphism

Polymorphism allows objects of different types to be treated through a common interface. It enables flexibility and extensibility—for example, a method accepting an AnimalActive reference can operate on any implementing class like Cat or Dog.

Object Construction and Initialization

An object’s lifecycle begins with class loading and initialization by the JVM, followed by instance creation.

Initialization Order

  1. Static blocks: Executed once when the class is loaded into memory.
  2. Instance variable initialization
  3. Instance initializer blocks (non-static blocks): Run every time an object is created, before the constructor body.
  4. Constructor execution

In an inheritance hierarchy, this sequence applies recursively from the topmost superclass down to the subclass:

  1. Superclass static blocks
  2. Superclass instance variables and initializer blocks
  3. Superclass constructor
  4. Subclass instance variables and initializer blocks
  5. Subclass constructor

Code Example: Instance Initializer Block

public class CodeBlock {
    private String name;
    private int age;

    public CodeBlock() {
        System.out.println("Executing no-arg constructor.");
    }

    public CodeBlock(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Executing parameterized constructor.");
    }

    // Instance initializer block
    {
        this.name = "defaultUser";
        this.age = 18;
        System.out.println("Instance initializer block executed.");
    }

    @Override
    public String toString() {
        return "CodeBlock{name='" + name + "', age=" + age + "}";
    }
}

When instantiated, the initializer block runs before the constructor body, setting default values that may be overridden by constructor logic.

The static Keyword

  • static members belong to the class, not instances, and are initialized when the class loads.
  • They reside in the method area of memory and can be accessed via ClassName.member without object instantiation.
  • static methods cannot access non-static (instance) members because those exist only after object creation.
  • Conversely, instance methods can access both static and non-static members.

The final Keyword

  • Applied to a class: prevents inheritance (e.g., String is final).
  • Applied to a method: prohibits overriding in subclasses.
  • Applied to a variable: allows assignment only once (effectively a constant). For reference types, the reference itself is immutable, though the object’s internal state may change.

Abstract Classes vs. Interfaces

Interfaces

  • Define a contract of what behaviors a class must support, not how.
  • All method are implicitly public abstract (prior to Java 8); constants are public static final.
  • Support multiple inheritance of type—classes can implement multiple interfaces.
  • Cannot contain constructors or instance initializers (since they can’t be instantiated).
  • From Java 8+, interfaces can include default and static methods with implementations.

Abstract Classes

  • Represent a partial implementation—a template for subclasses.
  • Can contain both abstract and concrete methods, instance variables, constructors, and initializers.
  • Support single inheritance only.
  • Used when sharing code among closely related classes.

Comparison Summary

Feature Abstract Class Interface
Purpose Class abstraction (template) Behavior abstraction (contract)
Inheritance Single Multiple (via implements)
Members Can have instance variables, constructors, concrete/abstract methods Only constants and abstract/default/static methods
Access Modifiers Flexible (private, protected, etc.) All members implicitly public
Instantiation No No
Adding New Methods Can provide default implementation without breaking subclasses Use default methods (Java 8+) to avoid forcing changes in implementers

Practical Example

// Interface: behavior contract
public interface AnimalActive {
    int HEIGHT = 10; // public static final by default

    void eat(AnimalImpl animal);
    void sleep();
    void jump(AnimalImpl animal);
}

// Abstract class: partial implementation
public abstract class AnimalImpl implements AnimalActive {
    private String name;
    private int age;

    public AnimalImpl(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void eat(AnimalImpl animal) {
        System.out.println(animal.getName() + " is eating.");
    }

    @Override
    public void sleep() {
        System.out.println(this.getName() + " is sleeping.");
    }

    // Getters omitted for brevity
}

// Concrete class
public class Cat extends AnimalImpl {
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void jump(AnimalImpl animal) {
        System.out.println(animal.getName() + " (age " + animal.getAge() + 
                           ") can jump up to " + HEIGHT + " units.");
    }

    public void purr() {
        System.out.println("Cat is purring.");
    }
}

Tags: java OOP Inheritance encapsulation Polymorphism

Posted on Sun, 09 Aug 2026 16:07:04 +0000 by Gente