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
superkeyword. - 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
thiskeyword 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
- Static blocks: Executed once when the class is loaded into memory.
- Instance variable initialization
- Instance initializer blocks (non-static blocks): Run every time an object is created, before the constructor body.
- Constructor execution
In an inheritance hierarchy, this sequence applies recursively from the topmost superclass down to the subclass:
- Superclass static blocks
- Superclass instance variables and initializer blocks
- Superclass constructor
- Subclass instance variables and initializer blocks
- 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
staticmembers 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.memberwithout object instantiation. staticmethods 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.,
Stringis 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 arepublic 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
defaultandstaticmethods 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.");
}
}