-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
41: Practice Exercise: Building an Employee Management Hierarchy
Think about how a company organogram actually works. At the most basic level, everyone in the building is an "Employee." Whether they're the CEO or the junior intern, they all have a name, an employee ID, and they all get paid. However, the way they get paid or the specific responsibilities they have vary wildly. You wouldn't want to create a completely separate system for every single job title—that would be a maintenance nightmare. Instead, you define the "Employee" basics once and then layer the specifics on top.
In Java, this is where inheritance and polymorphism stop being abstract concepts and start becoming your best friends. We're going to map that corporate structure directly into our code:
- The Base Class (Employee): This is our "general template." It holds the fields that everyone shares, like
nameandid. - The Subclasses (Manager, Developer): These are the specializations. A Manager "is-an" Employee, but they also have a
teamSize. A Developer "is-an" Employee, but they have aprogrammingLanguage. - Method Overriding: Everyone gets a paycheck, but a Manager might get a bonus based on team performance, while a Developer gets a flat salary. We define a
calculatePay()method in the base class, but we override it in the subclasses to handle the specific math. - Polymorphism: This is the magic part. It allows us to keep a
List<Employee>that contains both Managers and Developers. When we loop through that list and callcalculatePay(), Java is smart enough to call the Manager's version for managers and the Developer's version for developers.
Defining the Core Identity
I always recommend starting with the most generic class first. If you find yourself putting "manager-specific" logic in the base Employee class, stop. The base class should only contain what is universally true for every single person in the hierarchy. Use protected modifiers for fields you want subclasses to access directly without needing public getters for everything.
public class Employee {
protected String name;
protected int id;
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
public double calculatePay() {
return 3000.0; // Base pay for any employee
}
public void displayDetails() {
System.out.println("ID: " + id + ", Name: " + name);
}
}
Adding the Specializations
When we create the Manager class, we use the extends keyword. Notice the use of super() in the constructor. I see a lot of beginners forget this or try to re-assign the name and ID manually. Don't do that. Let the parent class handle the initialization of its own fields; your subclass should only care about its own unique data.
public class Manager extends Employee {
private double bonus;
public Manager(String name, int id, double bonus) {
super(name, id); // Pass the basics up to the Employee constructor
this.bonus = bonus;
}
@Override
public double calculatePay() {
// Managers get base pay plus their specific bonus
return super.calculatePay() + bonus;
}
}
Handling the Hierarchy as a Group
The real power comes when you stop treating these as separate types. If you're writing a payroll system, you don't want a processManagerPay() method and a processDeveloperPay() method. You want one method that handles Employee objects. This keeps your code clean and means that if you add a Consultant class later, you don't have to change your payroll logic at all.
List<Employee> staff = new ArrayList<>();
staff.add(new Manager("Alice", 101, 500.0));
staff.add(new Developer("Bob", 102, "Java"));
for (Employee e : staff) {
// Java decides at runtime which calculatePay() to use
System.out.println(e.name + " earns: " + e.calculatePay());
}📋 Practical Task
Building the Corporate Payroll System
Your task is to implement a functional employee hierarchy that handles different payment structures for different types of workers. This will test your ability to use inheritance, constructor chaining with super(), and polymorphic method calls.
Requirements:
- Create a base class
Employeewith fields forname(String) andbaseSalary(double). Include a methoddouble getPayment()that simply returns thebaseSalary. - Create a subclass
FullTimeEmployeethat adds abonusfield. OverridegetPayment()to return the base salary plus the bonus. - Create a subclass
Contractorthat replaces the base salary logic. Instead of a salary, they have anhourlyRate(double) andhoursWorked(int). OverridegetPayment()to returnhourlyRate * hoursWorked. - In a
Mainclass, create anArrayList<Employee>. Add at least oneFullTimeEmployeeand oneContractorto this list. - Loop through the list and print the name and the calculated payment for each person. Ensure that the correct payment logic is applied based on the object's actual type.
There are no comments for now.