Encapsulation
Encapsulation is a core principle of object-oriented programming that bundles data and methods within a single unit while restricting direct access to internal state. It acts as a protective barrier, ensuring controlled interaction through well-defined interfaces.
Example of Encapsulation
A common implementation uses private fields with public accessors and mutators:
public class EncapsulationDemo {
public static void main(String[] args) {
Employee emp = new Employee();
emp.setEmployeeId(101);
emp.setEmployeeName("Alice");
System.out.println(emp.getEmployeeId());
System.out.println(emp.getEmployeeName());
}
}
class Employee {
private int employeeId;
private String employeeName;
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
}
Output:
101
Alice
Benefits of Encapsulationn
- Reduces coupling between components
- Allows internal implementation changes without affecting clients
- Enables precise control over data validation
- Hides implementation details from external code
Inheritance
Inheritance enables hierarchical class relationships where subclasses acquire properties and behaviors from parenet classes. This mechanism promotes code reuse and establishes natural hierarchies.
Inheritance Example
public class InheritanceExample {
public static void main(String[] args) {
Feline cat = new Feline();
Canine dog = new Canine();
cat.consume();
cat.rest("cat");
cat.climb();
dog.consume("dog");
dog.rest("dog");
}
}
class Animal {
public void consume(String species) {
System.out.println(species + " is eating...");
}
public void rest(String species) {
System.out.println(species + " is sleeping...");
}
}
class Feline extends Animal {
private String species = "Feline";
public void consume() {
super.consume(species);
System.out.println(species + " finished eating");
}
public void rest(String species) {
System.out.println(species + " just went to sleep!");
}
public void climb() {
System.out.println(species + " is climbing a tree!");
}
}
class Canine extends Animal {
}
Output:
Feline is eating...
Feline finished eating
cat just went to sleep!
Feline is climbing a tree!
dog is eating...
dog is sleeping...
Key Inheritance Concepts
Constructor Behavior: Constructors are not inherited. Subclasses must use super() to invoke parent constructors.
Upcasting: Assigning a subclass instance to a superclass reference:
Animal creature = new Feline();
creature.consume("cat");
creature.rest("cat");
Upcasting may cause loss of subclass-specific methods and fields.
Polymorphism
Polymorphism allows objects to take multiple forms, enabling dynamic method dispatch where the actual method implementation is determined at runtime based on the object's type.
Polymorphism Requirements
- Inheritance relationship between classes
- Method overriding in subclass
- Superclass reference pointing to subclass object
Polymorphism Example
public class PolymorphismDemo {
public static void main(String[] args) {
Animal creature = new Feline();
creature.consume();
}
}
class Animal {
private String type = "Animal";
public void consume() {
System.out.println(type + " is eating...");
rest();
}
public void rest() {
System.out.println(type + " is sleeping...");
}
}
class Feline extends Animal {
private String type = "Feline";
public void consume(String name) {
System.out.println(name + " finished eating");
rest();
}
public void rest() {
System.out.println(type + " is sleeping");
}
}
Output:
Animal is eating...
Feline is sleeping
The rest() method call executes the subclass implementation due to dynamic method binding.
Method Resolution Priority
When resolving method calls with polymorphism, Java follows this priority order:
this.show(O)- Exact match in current classsuper.show(O)- Match in parent classthis.show((super)O)- Match with supertype parametersuper.show((super)O)- Parent method with supertypee parameter
Advantages of Polymorphism
- Substitutability: Objects of different types can be used interchangeably
- Extensibility: New subclasses can be added without modifying existing code
- Interface consistency: Provides uniform interface across related types
- Flexibility: Enables diverse operations through common interfaces
- Simplification: Reduces code complexity when handling object collections