Back to Week 5
Lab Session

Week 5: Lab — Thinking in Objects

Hands-on Programming Exercises — Ch. 9 & 10

Progress0 / 8 completed

1Encapsulated & Immutable Student

Think about it: A university system stores student records. Once a student ID and name are registered, they should never change — that's immutability. But the GPA can be updated through a controlled setter with validation (0.0–4.0 only). This combines encapsulation (private fields, getters/setters) with immutability (no setter for id/name).
Create an ImmutableStudent class with private fields: id (int), name (String), and gpa (double). Provide getters for all fields, but a setter only for gpa with validation (0.0 to 4.0). The id and name are set only through the constructor and can never be changed. In main, create a student, try to update gpa with valid and invalid values.
Expected OutputID: 1001, Name: Ali, GPA: 3.5 Setting GPA to 5.0 (invalid)... ID: 1001, Name: Ali, GPA: 3.5 Setting GPA to 3.9 (valid)... ID: 1001, Name: Ali, GPA: 3.9
Your Code
Declare: private int id; private String name; private double gpa;. Constructor: ImmutableStudent(int id, String name, double gpa) { this.id = id; this.name = name; this.gpa = gpa; }. Only provide getters for id and name (no setters!). Setter for gpa: public void setGpa(double gpa) { if (gpa >= 0.0 && gpa <= 4.0) this.gpa = gpa; }.

2Variable Scope Explorer

Think about it: Understanding scope prevents bugs caused by variable shadowing. When a local variable has the same name as an instance variable, the local one takes priority. Using this lets you explicitly access the instance variable. This exercise makes you practice distinguishing between local and instance scope.
Create a ScopeTest class with an instance variable x = 10. Add a method demonstrate(int x) that receives a parameter named x (same name!). Inside the method, print: (1) the local parameter x, (2) the instance variable using this.x, (3) create a local variable y and print it. Then call the method from main with argument 50.
Expected OutputParameter x: 50 Instance x: 10 Local y: 99 Back in main — cannot access y here
Your Code
Instance variable: int x = 10;. Method: public void demonstrate(int x) { System.out.println("Parameter x: " + x); System.out.println("Instance x: " + this.x); int y = 99; System.out.println("Local y: " + y); }. The parameter x shadows the instance variable, so use this.x to access the instance one.

3Static Counter & Constant

Think about it: A pizza shop wants to track how many pizzas have been ordered and apply a fixed tax rate to every order. The order count should be shared across all Pizza objects (static variable), and the tax rate should be a constant that nobody can change (static final). This exercise practices static variables, static constants, and static methods.
Create a Pizza class with: instance fields name (String) and price (double), a static variable orderCount = 0, and a static final constant TAX_RATE = 0.16. The constructor increments orderCount. Add a method getTotalPrice() that returns price + price * TAX_RATE. Add a static method getOrderCount(). Create 3 pizzas in main.
Expected OutputMargherita total: 11.6 Pepperoni total: 16.24 Hawaiian total: 13.92 Total orders: 3
Your Code
Fields: String name; double price; static int orderCount = 0; public static final double TAX_RATE = 0.16;. Constructor: Pizza(String name, double price) { this.name = name; this.price = price; orderCount++; }. Method: public double getTotalPrice() { return price + price * TAX_RATE; }. Static: public static int getOrderCount() { return orderCount; }.

4Passing Objects — Circle Resizer

Think about it: When you pass a primitive to a method, it gets a copy — changes don't affect the original. But when you pass an object, the method receives a copy of the reference — so it can modify the original object's fields! This is a fundamental concept to understand in Java.
Create a Circle class with a private field radius, a constructor, getter, and setter. Write two static methods in the test class: doubleValue(int n) that doubles a primitive (won't affect the original), and doubleRadius(Circle c) that doubles a circle's radius (WILL affect the original). Demonstrate both in main.
Expected OutputBefore: num = 5 After doubleValue: num = 5 Before: radius = 10.0 After doubleRadius: radius = 20.0
Your Code
Primitive method: public static void doubleValue(int n) { n = n * 2; } — this only changes the local copy. Object method: public static void doubleRadius(Circle c) { c.setRadius(c.getRadius() * 2); } — this modifies the actual object because c is a reference to the same object.

5Array of Objects — Student Roster

Think about it: A professor needs to manage a roster of students. Using an array of objects, you can store multiple Student objects and loop through them to find the highest GPA, calculate the average, or print all students. Remember: creating the array doesn't create the objects — you must new each one!
Create a Student class with fields name (String) and gpa (double). Create an array of 4 students. Loop through the array to: (1) print each student, (2) find the student with the highest GPA, (3) calculate the average GPA.
Expected Output--- Roster --- Ali: 3.5 Sara: 3.9 Omar: 3.2 Lina: 3.7 Best student: Sara (GPA: 3.9) Average GPA: 3.575
Your Code
Constructor: Student(String name, double gpa) { this.name = name; this.gpa = gpa; }. Array: Student[] students = { new Student("Ali", 3.5), new Student("Sara", 3.9), new Student("Omar", 3.2), new Student("Lina", 3.7) };. Print: for (Student s : students) System.out.println(s.name + ": " + s.gpa);. Best: for (Student s : students) if (s.gpa > best.gpa) best = s;. Average: for (Student s : students) sum += s.gpa;.

6Aggregation vs. Composition — School System

Think about it: A School has an Address that is created when the school is built — that's composition (created inside). A School also has a Principal who was hired from outside — that's aggregation (passed in). This exercise asks you to implement both in one class.
Create an Address class with a field city. Create a Principal class with a field name. Create a School class that uses composition for Address (created inside the constructor with new Address(city)) and aggregation for Principal (passed in as a parameter). Add a display() method. Show that the Principal exists independently after the school is gone.
Expected OutputSchool: Petra School, City: Amman, Principal: Dr. Ahmad Principal still exists: Dr. Ahmad
Your Code
Address: Address(String city) { this.city = city; }. Principal: Principal(String name) { this.name = name; }. School constructor: School(String name, String city, Principal principal) { this.name = name; this.address = new Address(city); this.principal = principal; }. Key: new Address(city) = composition (inside), this.principal = principal = aggregation (passed in).

7String Processing — Password Validator

Think about it: A registration form needs to validate passwords. Using String methods, you can check length, whether it contains certain characters, and compare values. This exercise practices length(), contains(), charAt(), equals(), and toUpperCase().
Write a program that validates a password string. Check: (1) length is at least 8 using length(), (2) contains "A" or "a" using contains(), (3) first character is uppercase using charAt(0) and Character.isUpperCase(). Also demonstrate == vs .equals() with two identical strings created differently.
Expected OutputPassword: HelloJava123 Length OK (>= 8): true Contains 'a': true Starts with uppercase: true --- String Comparison --- s1 == s2 (literals): true s1 == s3 (new String): false s1.equals(s3): true
Your Code
Length: System.out.println("Length OK (>= 8): " + (password.length() >= 8));. Contains: System.out.println("Contains 'a': " + password.contains("a"));. Uppercase: System.out.println("Starts with uppercase: " + Character.isUpperCase(password.charAt(0)));. Comparison: System.out.println("s1 == s2 (literals): " + (s1 == s2)); etc.

8BigDecimal — Precise Invoice Calculator

Think about it: Financial applications must be exact. Using double, 0.1 + 0.2 = 0.30000000000000004. That error compounds across millions of transactions! BigDecimal gives exact results. This exercise also uses Integer.parseInt() for parsing quantities (wrapper classes).
Create an invoice calculator using BigDecimal. Parse quantity strings using Integer.parseInt() (wrapper class). Calculate subtotal = price × quantity using multiply(). Apply 16% tax using multiply(). Calculate total using add(). Compare results with double arithmetic. Use String constructor for BigDecimal!
Expected Output--- BigDecimal Invoice --- Item: Widget, Qty: 3, Price: 19.99 Subtotal: 59.97 Tax (16%): 9.5952 Total: 69.5652 --- double comparison --- double total: 69.56520000000001
Your Code
Subtotal: BigDecimal subtotal = price.multiply(quantity);. Tax: BigDecimal taxRate = new BigDecimal("0.16"); BigDecimal tax = subtotal.multiply(taxRate);. Total: BigDecimal total = subtotal.add(tax);. Always use the String constructor: new BigDecimal("0.16") not new BigDecimal(0.16)!