This is a comprehensive Java learning project that demonstrates key concepts through a practical example.
This project teaches:
- Class Design - Different approaches to designing classes (mutable vs immutable)
- Fields, Getters, and Setters - Encapsulation and data hiding
- Constructors - Ensuring objects are created in valid states
- Loops - Different types of loops and when to use each
- Conditionals and Booleans - Control flow and logical operations
- Lambda Expressions - Functional programming in Java
src/
├── Main.java - Main demonstration program
├── Exercises.java - Student exercises (6 levels)
├── ExercisesSolutions.java - Complete solutions to all exercises
├── FullyMutableProduct.java - Example 1: All public fields
├── ProductWithGettersSetters.java - Example 2: Private fields with getters/setters
├── ProductWithConstructor.java - Example 3: Constructor with partial immutability
├── ImmutableProduct.java - Example 4: Fully immutable class (BEST PRACTICE)
├── ProductFilter.java - Functional interface for filtering
├── ProductProcessor.java - Functional interface for processing
└── InventoryManager.java - Lambda demonstration helper class
In IntelliJ IDEA:
- Run demonstrations: Right-click on
Main.java→ Select "Run 'Main.main()'" - Practice exercises: Right-click on
Exercises.java→ Select "Run 'Exercises.main()'" - Check solutions: Right-click on
ExercisesSolutions.java→ Select "Run"
Or from command line (if you have JDK installed):
cd src
javac *.java
java Main # Run demonstrations
java Exercises # Run exercises
java ExercisesSolutions # Run solutions- Study the examples - Run
Main.javaand read through the code - Complete the exercises - Open
Exercises.javaand solve them one by one - Check your work - Compare with
ExercisesSolutions.java - Experiment - Modify the code and see what happens!
FullyMutableProduct product = new FullyMutableProduct();
product.id = 1; // All fields are public
product.name = "Laptop"; // Can be changed anytimeProblems:
- Fields can be null (NullPointerException risk)
- No validation possible
- Values can change unexpectedly
- Not thread-safe
ProductWithGettersSetters product = new ProductWithGettersSetters();
product.setId(1); // Setters can validate input
product.setName("Laptop");Benefits:
- Can add validation in setters
- Can change internal implementation
- Encapsulation of data
Problems:
- Still mutable (values can change)
- Still need null checks
// Must provide id and name at creation
ProductWithConstructor product = new ProductWithConstructor(1, "Laptop");
product.price = 99.99; // Other fields still publicWhat a Constructor Does:
- Ensures required fields are set at object creation
- Cannot create object without providing required values
- Some fields can be made immutable (final, no setter)
// All fields must be provided at creation
ImmutableProduct product = new ImmutableProduct(1, "Laptop", 999.99, 10);
// product.setPrice(100); // Won't compile - no setters!Benefits:
- Thread-safe (can share between threads safely)
- No unexpected changes
- No null checks needed (guaranteed valid values)
- Easier to reason about
- Can be used as Map keys or in Sets
Trade-off:
- To "change" a value, must create new object
for (int i = 0; i < inventory.length; i++) {
System.out.println("[" + i + "] " + inventory[i].getName());
}Use cases:
- Need to know the position/index
- Iterate in reverse order
- Skip elements (iterate by 2s, 3s, etc.)
- Modify elements in place
for (ImmutableProduct product : inventory) {
System.out.println(product.getName());
}Use cases:
- Simpler, cleaner code
- Don't need the index
- Just processing each element
// Example: Stop when budget runs out
double budget = 500.0;
int i = 0;
while (i < inventory.length && budget >= inventory[i].getPrice()) {
budget -= inventory[i].getPrice();
i++;
}Use cases:
- Early exit (stop as soon as item found - don't check remaining items)
- Budget/resource limits (unknown how many iterations until limit hit)
- Multiple exit conditions (cart full OR expensive item found)
- Accumulated thresholds (process until total value exceeds limit)
- Key insight: foreach processes ALL items; while stops when condition met!
boolean isExpensive = product.getPrice() > 100;
boolean isLowStock = product.getQuantity() < 15;
boolean needsReorder = isLowStock && product.getPrice() > 50;
if (isExpensive && isLowStock) {
System.out.println("ALERT: Expensive item with low stock!");
} else if (isLowStock) {
System.out.println("Low stock warning");
} else {
System.out.println("Stock level normal");
}Lambdas allow you to pass behavior as a parameter:
// Filter products over $100
ImmutableProduct[] expensive = manager.filter(product -> product.getPrice() > 100);
// Filter low stock items
ImmutableProduct[] lowStock = manager.filter(product -> product.getQuantity() < 15);
// Process each product
manager.forEach(product -> {
double discounted = product.getPrice() * 0.9;
System.out.println(product.getName() + ": $" + discounted);
});Benefits:
- Flexible, reusable code
- Pass behavior as parameter
- Cleaner than anonymous classes
- Enables functional programming style
With Mutable Objects:
FullyMutableProduct product = new FullyMutableProduct();
product.price = 999.99;
// Later in code...
calculateDiscount(product); // Might change the price!
// Price might have changed unexpectedly!
System.out.println(product.price); // Could be different nowWith Immutable Objects:
ImmutableProduct product = new ImmutableProduct(1, "Laptop", 999.99, 10);
calculateDiscount(product); // Cannot modify the original
// Price is guaranteed to be the same
System.out.println(product.getPrice()); // Still 999.99Mutable classes require null checks:
if (product.name != null) {
String upper = product.name.toUpperCase();
}Immutable classes guarantee no nulls:
// No null check needed - guaranteed to have value
String upper = product.getName().toUpperCase();The project includes 6 levels of exercises in Exercises.java:
- Create mutable and immutable products
- Practice using constructors
- Understand field initialization
- Print products using foreach
- Count items matching criteria
- Basic iteration patterns
- Iterate in reverse order
- Find min/max values
- Use for loops vs foreach
- Shop until budget exceeded
- Search with early exit
- Unknown iteration counts
- Categorize items with if-else
- Use compound boolean expressions
- Complex business logic
- Filter with lambda expressions
- Process items functionally
- Use functional interfaces
Tip: Start with Level 1 and work your way up. Each level builds on previous concepts!
- Use immutable classes for data objects (recommended)
- Validate input in constructors and setters
- Make fields private (encapsulation)
- Use final for fields that shouldn't change
- Choose the right loop for the job
- Use lambdas for flexible, reusable code
The Main.java program demonstrates all concepts in order:
- Class design patterns (4 examples)
- Loop types (for, foreach, while)
- Conditionals and booleans
- Lambda expressions
Each section has detailed comments explaining what's happening and why.
Author: Educational Project for Java Learning
Java Version: Java 21+ (uses modern features)