A comprehensive Java project demonstrating fundamental Object-Oriented Programming (OOP) concepts and SOLID principles with practical examples and real-world use cases.
This project is a collection of educational modules that cover essential OOP concepts in Java. Each module includes working examples and demonstrates best practices in software design and architecture.
Target Audience: Beginner to Intermediate Java developers
Location: CallByValueAndReference/
Demonstrates the difference between calling methods with primitive types (call by value) and objects (call by reference).
- Main.java: Contains multiple overloaded
change()methods showing:- Primitives (int, String): Called by value - changes inside methods don't affect original variables
- Arrays: Called by reference - modifications inside methods affect the original array
- Objects: Called by reference - modifications to object properties persist
βββββββββββββββββββββββββββββββββββββββββββ
β Call By Value vs Call By Reference β
βββββββββββββββββββββββββββββββββββββββββββ€
β Primitives (int, float, etc.) β VALUE β
β Objects β REFERENCE β
β Arrays β REFERENCE β
β Strings β VALUE (immutable) β
βββββββββββββββββββββββββββββββββββββββββββ
int x = 5;
change(x); // x remains 5 (call by value)
Reference obj = new Reference(1);
change(obj); // obj.x changes (call by reference)Location: Constructor/
Explores different types of constructors and their role in object initialization.
- Constructor.java: Demonstrates:
- Default constructor (no arguments)
- Parameterized constructors (with arguments)
- Constructor chaining (
this()) - Copy constructor
| Type | Purpose | Example |
|---|---|---|
| Default | No arguments, initializes default values | new Constructor() |
| Parameterized | Takes arguments to initialize fields | new Constructor(20, "Nada", 2000f) |
| Constructor Chaining | Calls another constructor using this() |
Reduces code duplication |
| Copy Constructor | Creates a new object copying data from existing | new Constructor(ob4) |
private int age; // Default: 0
private String name; // Default: null
private float amount; // Default: 0.0f
private String color; // Optional parameter// Default constructor
Constructor ob = new Constructor();
// Parameterized constructor
Constructor ob2 = new Constructor(20);
Constructor ob3 = new Constructor(20, "Nada", 2000f);
// Constructor chaining
Constructor ob4 = new Constructor(52, "Ali", 200f, "Red");
// Copy constructor
Constructor ob5 = new Constructor(ob4);Location: Inheritance/
Demonstrates class inheritance where a child class inherits properties and methods from a parent class.
-
Employee.java (Parent Class)
- Properties:
name,email,phone,department,address - Constructors: Default and parameterized
- Methods: Getters and setters for all properties
- Properties:
-
Developer.java (Child Class)
- Extends Employee
- Additional property:
projectName - Overridden/additional methods
βββββββββββββββ
β Employee β (Parent)
β (Parent) β
ββββββββ¬βββββββ
β
β extends
β
ββββββββΌβββββββ
β Developer β (Child)
β (Child) β
βββββββββββββββ
- Access Modifier:
protectedallows child classes to access parent properties - Constructor Chaining: Child constructor can call parent constructor
- Method Overriding: Child class can override parent methods
- Polymorphism: Parent reference can hold child object
// Parent class
Employee emp = new Employee("Dodo", "abdohosh100@gmail.com", 50152933, "H1", "Cairo");
// Child class
Developer dev = new Developer("Dodo", "abdohosh100@gmail.com", 50152933, "H1", "Cairo", "OOP");
System.out.println(dev.getProjectName()); // OOPLocation: Static_Members/
Demonstrates static variables and methods that belong to the class rather than individual objects.
- Static.java: Student registration system showing:
- Static variables:
idcounter andfacultyconstant - Static methods:
validatePassword() - Instance variables:
name,password
- Static variables:
| Feature | Static | Instance |
|---|---|---|
| Scope | Belongs to class | Belongs to object |
| Memory | Shared by all objects | Unique per object |
| Access | ClassName.staticVar |
this.instanceVar |
| Initialization | Once at class loading | Each object creation |
public class Static {
private static int id = 0; // Static - shared by all
private static String faculty = "CS"; // Static - shared by all
private String name; // Instance - unique per object
public static boolean validatePassword(String password) {
return password.length() >= 6; // Static method - no access to instance vars
}
}Static ob1 = new Static("Ali", "123589");
ob1.display(); // ID: 1
Static ob2 = new Static("Ahmed", "1244841289");
ob2.display(); // ID: 2 (ID incremented)
ob4.login(3, "18552003"); // Login successfullyThe SOLID principles are five design principles that help create maintainable, scalable, and robust software. This project includes practical implementations of each principle.
Location: SOLID Principles/
Location: SOLID Principles/Sinlge Responsibility/
Principle: A class should have only one reason to change.
Each class should have a single, well-defined responsibility. This makes code easier to test, maintain, and understand.
- Employee.java: Represents employee data
- SalaryCalculator.java: Handles salary calculation logic
- EmployeeRepository.java: Handles employee data persistence
β BAD DESIGN
ββββββββββββββββββββββββββββββββββββ
β Employee β
ββββββββββββββββββββββββββββββββββββ€
β - name β
β - salary β
β - calculateSalary() β
β - saveToDatabase() β
β - generateReport() β
ββββββββββββββββββββββββββββββββββββ
(Multiple responsibilities - too many reasons to change)
β
GOOD DESIGN
ββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
β Employee β β SalaryCalculator β β EmployeeRepository
ββββββββββββββββ€ ββββββββββββββββββββ€ ββββββββββββββββββββ€
β - name β β - calculateSal.. β β - save() β
β - salary β β β β - update() β
ββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββ
(Each class has ONE reason to change)
// Employee - only data
public class Employee {
private String name;
}
// SalaryCalculator - handles calculations
public class SalaryCalculator {
public double calculateSalary(Employee emp) {
return emp.getSalary() * 1.1; // 10% bonus
}
}
// EmployeeRepository - handles persistence
public class EmployeeRepository {
public void save(Employee emp) {
// Save to database
}
}β Easier Testing: Test each responsibility independently
β Maintainability: Changes to one responsibility don't affect others
β Reusability: Classes can be reused in different contexts
β Clarity: Easier to understand what each class does
Location: SOLID Principles/Open_Closed/
Principle: Software entities should be open for extension but closed for modification.
Classes should be designed so new functionality can be added through extension, not by modifying existing code.
- Shape.java: Abstract base class (interface)
- Rectangle.java: Concrete implementation
- Circle.java: Concrete implementation
- AreaCalculator.java: Calculates area for any shape
ββββββββββββββββββββββββββββ
β <<abstract>> β
β Shape β
ββββββββββββββββββββββββββββ€
β + getArea(): double β
ββββββββββββββ¬ββββββββββββββ
β
ββββββββ΄βββββββ
β β
βββββββΌββββββ ββββββΌβββββββ
β Rectangle β β Circle β
βββββββββββββ€ βββββββββββββ€
β - length β β - radius β
β - width β β β
βββββββββββββ βββββββββββββ
ββββββββββββββββββββββββββββ
β AreaCalculator β
ββββββββββββββββββββββββββββ€
β + calculate(shape): β
β return shape.area() β
ββββββββββββββββββββββββββββ
β BAD DESIGN (Closed for extension)
calculateArea(shape):
if shape is Rectangle:
return length * width
if shape is Circle:
return Ο * rΒ²
(Every new shape requires modifying this method!)
β
GOOD DESIGN (Open for extension)
calculateArea(shape):
return shape.getArea()
(Just add new Shape implementations!)
// Abstract shape - closed for modification
public abstract class Shape {
public abstract double getArea();
}
// Concrete implementations - open for extension
public class Rectangle extends Shape {
private double length, width;
@Override
public double getArea() {
return length * width;
}
}
public class Circle extends Shape {
private double radius;
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
// Calculator - unchanged, works with any shape
public class AreaCalculator {
public double calculateArea(Shape shape) {
return shape.getArea();
}
}AreaCalculator calc = new AreaCalculator();
Shape rectangle = new Rectangle(5, 6.5);
System.out.println(calc.calculateArea(rectangle)); // 32.5
Shape circle = new Circle(7.9);
System.out.println(calc.calculateArea(circle)); // 196.1...
// Adding new shape doesn't require changing AreaCalculator!β Low Risk: No modification to existing code = fewer bugs
β Extensibility: Easy to add new shapes
β Maintainability: Existing functionality stays stable
β Code Reuse: Abstract class used with all implementations
Location: SOLID Principles/Listkov Substitution/
Principle: Subtypes must be replaceable for their base types without breaking behavior.
Objects of a child class should be substitutable for objects of the parent class without altering the correctness of the program.
- Shape.java: Base class with abstract methods
- Rectangle.java: Child class implementation
- Square.java: Child class implementation
Real-world relationship:
Square IS-A Rectangle (mathematically true)
But in OOP:
Square should NOT inherit from Rectangle
(violates LSP because Square changes Rectangle's behavior)
β BAD DESIGN (Violates LSP)
Rectangle extends Shape:
- setLength(double l)
- setWidth(double w)
Square extends Rectangle:
- setLength() β sets both length AND width
- setWidth() β sets both length AND width
(Square changes Rectangle's behavior - violates LSP!)
β
GOOD DESIGN (Respects LSP)
Both inherit from Shape:
- getArea() works correctly for each
No inheritance between Square and Rectangle
(Each implements getArea() according to its own rules)
// Base class
public abstract class Shape {
public abstract double getArea();
}
// Rectangle - standard behavior
public class Rectangle extends Shape {
private double length, width;
public void setLength(double l) { this.length = l; }
public void setWidth(double w) { this.width = w; }
@Override
public double getArea() {
return length * width;
}
}
// Square - follows LSP
public class Square extends Shape {
private double side;
public void setSide(double s) { this.side = s; }
@Override
public double getArea() {
return side * side;
}
}Shape shape = new Rectangle(5, 6);
System.out.println(shape.getArea()); // 30
Shape shape2 = new Square(10);
System.out.println(shape2.getArea()); // 100
// Both can be substituted for Shape without issues!β Predictable Behavior: Child class behaves as expected
β Polymorphism Safety: Can safely use parent references
β Testing: Easier to test with polymorphic types
β Reliability: No surprises when substituting objects
Location: SOLID Principles/interface Segregation/
Principle: Clients should not be forced to depend on interfaces they don't use.
Create multiple specific interfaces rather than one large general-purpose interface. Classes implement only the interfaces they need.
- Workable.java: Interface for working entities
- Eatable.java: Interface for entities that eat
- Sleepable.java: Interface for entities that sleep
- Human.java: Implements all interfaces (Workable, Eatable, Sleepable)
- Robot.java: Implements only Workable interface
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Workable β β Eatable β β Sleepable β
ββββββββββββββββ€ ββββββββββββββββ€ ββββββββββββββββ€
β + work() β β + eat() β β + sleep() β
ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β β
β βββββββββ΄ββββββββββββββββββ€
β β β
ββββΌββββββββββΌβββββββ βββββββββββΌβββββ
β Human β β Robot β
ββββββββββββββββββββ€ ββββββββββββββββ€
β + work() β β + work() β
β + eat() β β β
β + sleep() β β β
ββββββββββββββββββββ ββββββββββββββββ
β BAD DESIGN (Fat Interface)
interface Worker {
void work();
void eat();
void sleep();
void maintain();
}
Robot implements Worker {
void work() { /* OK */ }
void eat() { /* ERROR - Robot can't eat */ }
void sleep() { /* ERROR - Robot doesn't sleep */ }
void maintain() { /* OK */ }
}
β
GOOD DESIGN (Segregated Interfaces)
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Sleepable { void sleep(); }
Human implements Workable, Eatable, Sleepable {
void work() { /* OK */ }
void eat() { /* OK */ }
void sleep() { /* OK */ }
}
Robot implements Workable {
void work() { /* OK */ }
}
// Segregated interfaces
public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
// Human implements all
public class Human implements Workable, Eatable, Sleepable {
@Override
public void work() { System.out.println("Working..."); }
@Override
public void eat() { System.out.println("Eating..."); }
@Override
public void sleep() { System.out.println("Sleeping..."); }
}
// Robot implements only what it needs
public class Robot implements Workable {
@Override
public void work() { System.out.println("Processing..."); }
}β Flexibility: Classes implement only needed interfaces
β No Bloated Interfaces: Each interface has focused responsibility
β Cleaner Code: No empty/dummy method implementations
β Better Design: Easier to understand and maintain
Location: SOLID Principles/Dependency Inversion/
Principle: High-level modules should depend on abstractions, not concrete implementations.
Depend on interfaces/abstractions rather than concrete classes. This reduces coupling and improves flexibility.
- Database.java: Abstract interface (abstraction layer)
- MySqlDatabase.java: Concrete MySQL implementation
- PostgresDatabase.java: Concrete PostgreSQL implementation
- UserService.java: Depends on Database interface, not concrete classes
Without Dependency Inversion (Tightly Coupled):
βββββββββββββββββββ
β UserService β
βββββββββββββββββββ€
β - mySqlDb βββββββββ Directly depends on
ββββββββββ¬βββββββββ concrete class
β
βΌ
ββββββββββββββββββββ
β MySqlDatabase β
ββββββββββββββββββββ
With Dependency Inversion (Loosely Coupled):
βββββββββββββββββββ
β UserService β
βββββββββββββββββββ€
β - database βββββββββ Depends on
ββββββββββ¬βββββββββ abstraction
β
βΌ
ββββββββββββββββββββ
β <<interface>> β
β Database β
ββββββββββ¬ββββββββββ
β
ββββββββ΄βββββββββββ
β β
βββΌββββββββββββ ββββΌββββββββββ
β MySql β β Postgres β
β Database β β Database β
βββββββββββββββ ββββββββββββββ
β BAD DESIGN (Tightly Coupled)
class UserService {
private MySqlDatabase db = new MySqlDatabase();
void process() {
db.connect();
db.save();
}
}
// Problem: Hard-coded dependency on MySqlDatabase
// Can't switch to PostgreSQL without changing code
β
GOOD DESIGN (Loosely Coupled)
interface Database {
void connect();
void save();
}
class UserService {
private Database db; // Depends on abstraction
public UserService(Database db) {
this.db = db; // Inject concrete implementation
}
void process() {
db.connect();
db.save();
}
}
// Easy to switch implementations at runtime
// Abstraction layer
public interface Database {
void connect();
void disconnect();
void save();
void retrieve();
}
// Concrete implementation 1
public class MySqlDatabase implements Database {
@Override
public void connect() { System.out.println("Connecting to MySQL..."); }
@Override
public void save() { System.out.println("Saving to MySQL..."); }
// ... other methods
}
// Concrete implementation 2
public class PostgresDatabase implements Database {
@Override
public void connect() { System.out.println("Connecting to PostgreSQL..."); }
@Override
public void save() { System.out.println("Saving to PostgreSQL..."); }
// ... other methods
}
// High-level module depends on abstraction
public class UserService {
private Database database;
// Constructor injection
public UserService(Database database) {
this.database = database;
}
public void process() {
database.connect();
database.save();
database.disconnect();
}
}// Easy to switch implementations
Database db1 = new MySqlDatabase();
UserService service1 = new UserService(db1);
service1.process();
// Just switch the implementation
Database db2 = new PostgresDatabase();
UserService service2 = new UserService(db2);
service2.process();β Flexibility: Easy to switch implementations
β Testability: Can inject mock objects for testing
β Loose Coupling: High-level modules independent of implementations
β Maintainability: Changes to one implementation don't affect others
Java_OOP/
βββ README.md
β
βββ CallByValueAndReference/
β βββ src/
β β βββ Main.java
β β βββ Refrence.java
β βββ CallByValueAndReference.iml
β
βββ Constructor/
β βββ src/
β β βββ Constructor.java
β β βββ Main.java
β βββ Constructor.iml
β
βββ Inheritance/
β βββ src/
β β βββ Employee.java (Parent class)
β β βββ Developer.java (Child class)
β β βββ Main.java
β βββ Inheritance.iml
β
βββ Static_Members/
β βββ src/
β β βββ Static.java
β β βββ Main.java
β βββ Static_Members.iml
β
βββ SOLID Principles/
βββ src/
β βββ Main.java
β
βββ Single Responsibility/
β βββ src/
β β βββ Employee.java
β β βββ SalaryCalculator.java
β β βββ EmployeeRepository.java
β β βββ Main.java
β βββ Single Responsibility.iml
β
βββ Open_Closed/
β βββ src/
β β βββ Shape.java (Abstract base)
β β βββ Rectangle.java
β β βββ Circle.java
β β βββ AreaCalculator.java
β β βββ Main.java
β βββ Open_Closed.iml
β
βββ Listkov Substitution/
β βββ src/
β β βββ Shape.java (Abstract base)
β β βββ Rectangle.java
β β βββ Square.java
β β βββ Main.java
β βββ Listkov Substitution.iml
β
βββ Interface Segregation/
β βββ src/
β β βββ Workable.java (Interface)
β β βββ Eatable.java (Interface)
β β βββ Sleepable.java (Interface)
β β βββ Human.java
β β βββ Robot.java
β β βββ Main.java
β βββ Interface Segregation.iml
β
βββ Dependency Inversion/
βββ src/
β βββ Database.java (Interface)
β βββ MySqlDatabase.java
β βββ PostgresDatabase.java
β βββ UserService.java
β βββ Main.java
βββ Dependency Inversion.iml
- Java Development Kit (JDK) 8 or higher
- IntelliJ IDEA or any Java IDE
cd CallByValueAndReference
javac src/*.java
java MainOutput:
5 # x unchanged (call by value)
Ali # string unchanged
null # wrapper unchanged
1 2 3 # array changed (call by reference)
2 # object changed (call by reference)
cd Constructor
javac src/*.java
java MainOutput:
age is 0
name is null
amount is 0.0
# ... (multiple constructor outputs)
cd Inheritance
javac src/*.java
java MainOutput:
I'm Parent Class
0
null
# ... (inherited properties and methods)
cd Static_Members
javac src/*.java
java MainOutput:
Done register
name is Ali
ID is 1
Faculty is Computer Science
Done register
name is Ahmed
ID is 2
Faculty is Computer Science
cd "SOLID Principles/Single Responsibility"
javac src/*.java
java MainOutput: (Varies by principle)
- Primitive types are passed by value
- Objects and arrays are passed by reference
- Strings are immutable (behave like primitives)
- Default constructor is called if you don't define one
- Parameterized constructors allow flexible initialization
- Constructor chaining (
this()) reduces code duplication - Copy constructors create independent copies
- Reduces code duplication through code reuse
- Parent class properties/methods inherited by child
- Child can override parent methods
protectedmodifier allows controlled access
- Shared by all instances of a class
- Accessed via
ClassName.staticVar - Useful for counters, constants, utility methods
- Instance variables use
this.var
| Principle | Key Takeaway |
|---|---|
| S | One class = One responsibility |
| O | Add features via extension, not modification |
| L | Child can safely replace parent |
| I | Small focused interfaces, not fat ones |
| D | Depend on abstractions, not concrete classes |
- Abstraction: Use interfaces and abstract classes
- Polymorphism: Code to the interface, not implementation
- Composition: Consider over inheritance
- Dependency Injection: Pass dependencies, don't create them
-
Use Access Modifiers Wisely
privatefor internal stateprotectedfor inheritancepublicfor public API
-
Favor Composition Over Inheritance
- Composition is more flexible
- Inheritance creates tight coupling
-
Program to Interfaces
- Depend on abstractions
- Makes code testable and flexible
-
Keep It Simple
- Single Responsibility Principle
- Avoid over-engineering
-
Test Your Code
- Unit test each class
- Mock dependencies using interfaces
Feel free to:
- Fork this repository
- Add more examples
- Improve documentation
- Create pull requests
This project is open source and available for educational purposes.
This project demonstrates practical implementations of OOP concepts and SOLID principles. Each module is self-contained and can be studied independently. The examples are designed to be clear and educational rather than production-ready.
Happy Learning!