Hands-on Programming Exercises — Ch. 9 & 10
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.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; }.this lets you explicitly access the instance variable. This exercise makes you practice distinguishing between local and instance scope.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.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.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.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; }.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.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.new each one!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.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;.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.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.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).length(), contains(), charAt(), equals(), and toUpperCase().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.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.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).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!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)!price × quantity for each). This is a very common real-world pattern!Product class with fields name (String), price (double), and quantity (int). Create an array of 3 products. Loop through the array to: (1) print each product, (2) find the most expensive product, (3) calculate the total inventory value (sum of price × quantity).Product(String name, double price, int quantity) { this.name = name; this.price = price; this.quantity = quantity; }. Array: Product[] products = { new Product("Laptop", 800.0, 5), new Product("Phone", 500.0, 12), new Product("Tablet", 350.0, 8) };. Print: for (Product p : products) System.out.println(p.name + ": $" + p.price + " x " + p.quantity);. Most expensive: for (Product p : products) if (p.price > expensive.price) expensive = p;. Value: for (Product p : products) totalValue += p.price * p.quantity;..equals(), and find the highest-paid employee. This combines arrays of objects with String comparison and arithmetic.Employee class with fields name (String), department (String), and salary (double). Create an array of 4 employees. Loop to: (1) print each employee with a 10% bonus, (2) count how many work in "IT" using .equals(), (3) find the employee with the highest salary.Employee(String name, String department, double salary) { this.name = name; this.department = department; this.salary = salary; }. Array: Employee[] employees = { new Employee("Sami", "IT", 3000), new Employee("Nour", "HR", 2800), new Employee("Rami", "IT", 3500), new Employee("Dana", "HR", 2900) };. Bonus: for (Employee e : employees) { double bonus = e.salary * 0.10; System.out.println(e.name + " (" + e.department + "): Salary=" + e.salary + ", Bonus=" + bonus); }. Count: if (e.department.equals("IT")) itCount++;. Top: if (e.salary > top.salary) top = e;.Car has an Engine that is built specifically for it — the engine is created inside the Car constructor, not passed from outside. If the car is destroyed, the engine goes with it. This is composition: a strong "owns" relationship where the part cannot exist independently of the whole.Engine class with fields horsepower (int) and type (String). Create a Car class with a field model (String) and an Engine field. The Car constructor takes model, hp, and engineType and creates the Engine inside using new Engine(hp, engineType). Add a display() method. Show that when the car is set to null, the engine is gone too.Engine(int horsepower, String type) { this.horsepower = horsepower; this.type = type; }. Car constructor: Car(String model, int hp, String engineType) { this.model = model; this.engine = new Engine(hp, engineType); }. Key: new Engine(hp, engineType) is created INSIDE the Car — that's composition. Display: System.out.println("Car: " + model + ", Engine: " + engine.horsepower + "HP " + engine.type);.Team has a captain and a topScorer, but players exist independently — they were recruited from outside and can join another team if this one is dissolved. This is aggregation: a weak "uses" relationship where the parts are passed in and survive on their own.Player class with fields name (String) and position (String). Create a Team class with name (String), captain (Player), and topScorer (Player). The Team constructor receives Player objects as parameters (aggregation — not created inside). Add a display() method. Show that players still exist after the team is set to null.Player(String name, String position) { this.name = name; this.position = position; }. Team constructor: Team(String name, Player captain, Player topScorer) { this.name = name; this.captain = captain; this.topScorer = topScorer; }. Key: players are passed in, NOT created inside — that's aggregation. Display: System.out.println("Team: " + name); System.out.println(" Captain: " + captain.name + " (" + captain.position + ")"); System.out.println(" Top Scorer: " + topScorer.name + " (" + topScorer.position + ")");.Department owns a Course that was designed specifically for it — composition (created inside). But the Instructor teaching the course was hired from outside and can teach elsewhere — aggregation (passed in). This exercise combines both relationships in one class, reinforcing the difference.Course class with fields title (String) and credits (int). Create an Instructor class with fields name (String) and specialization (String). Create a Department class that uses composition for Course (created inside with new Course(courseTitle, credits)) and aggregation for Instructor (passed in). Add a display() method. Show that the Instructor survives after the department is closed.Course(String title, int credits) { this.title = title; this.credits = credits; }. Instructor: Instructor(String name, String specialization) { this.name = name; this.specialization = specialization; }. Department constructor: Department(String name, String courseTitle, int credits, Instructor instructor) { this.name = name; this.course = new Course(courseTitle, credits); this.instructor = instructor; }. Key: new Course(...) = composition (inside), this.instructor = instructor = aggregation (passed in). Display: print name, course.title, course.credits, instructor.name, instructor.specialization.