Hands-on Programming Exercises — Ch. 11
getArea() on each. The correct formula runs automatically based on the actual shape type.GeometricObject with a field color (String), a constructor, and a method getArea() that returns 0.0. Create subclasses Circle (field: radius), Rectangle (fields: width, height), and Triangle (fields: base, height) that each override getArea() with the correct formula. In main, create a GeometricObject[] array holding one of each, loop through it to print each shape's color and area, then print the total area.class GeometricObject { String color; GeometricObject(String color){ this.color = color; } public double getArea(){ return 0.0; } }. Each subclass calls super(color) and overrides getArea(). In main: GeometricObject[] shapes = { new Circle(5.0,"red"), new Rectangle(4.0,6.0,"blue"), new Triangle(4.0,5.0,"green") }; then loop: for(GeometricObject s : shapes){ System.out.println(s.getColor() + ": area = " + s.getArea()); }. Sum areas in the same loop with total += s.getArea().greet() on each, it doesn't know at compile time which version to call — that's decided at runtime based on the actual type. This is dynamic binding: the JVM looks up the correct method version for the actual object, not the declared type.Person with field name (String), a constructor, and a method greet() that prints "Hello, I'm [name]". Create subclass Student that overrides greet() to print "Hi! I'm student [name]". Create subclass Teacher that overrides greet() to print "Good day. I'm Professor [name]". In main, create a Person[] array with one of each type and call greet() on all — observe dynamic binding in action.class Person { String name; Person(String name){ this.name = name; } public void greet(){ System.out.println("Hello, I'm " + name); } }. Student: class Student extends Person { Student(String name){ super(name); } @Override public void greet(){ System.out.println("Hi! I'm student " + name); } }. Teacher follows the same pattern. In main: Person[] people = { new Person("Ahmad"), new Student("Sara"), new Teacher("Khaled") }; for(Person p : people) p.greet(); — the correct greet() is chosen at runtime based on actual type.Dog as an Animal — that's safe upcasting. But to call a Dog-specific method like fetch(), you need to cast back down explicitly. If the object is actually a Cat, the cast throws a ClassCastException. This exercise makes you practice both directions of casting and the role of instanceof in safe downcasting.Animal with field name (String), a constructor, and a method eat() that prints "[name] is eating". Create subclass Dog with a method fetch() that prints "[name] is fetching the ball". Create subclass Cat with a method purr() that prints "[name] is purring". In main: (1) upcast a Dog to Animal and call eat(), (2) safely downcast back to Dog using instanceof and call fetch(), (3) try to downcast a Cat to Dog — guard with instanceof to avoid the exception.Animal a = new Dog("Rex"); — implicit, no cast needed. Call a.eat(). Downcasting: if (a instanceof Dog) { Dog d = (Dog) a; d.fetch(); }. For the Cat: Animal c = new Cat("Whiskers"); c.eat(); if (c instanceof Dog) { ((Dog) c).fetch(); } else { System.out.println(c.name + " is not a Dog \u2014 cannot fetch!"); }. Always use instanceof before downcasting to avoid ClassCastException.Animal[] array. When processing each patient, the system needs to know the actual type to call the right treatment. The instanceof operator lets you safely identify the type at runtime and call type-specific methods — without risking a ClassCastException.Animal, Dog, and Cat from the previous exercise, and add a new subclass Bird with a method chirp() that prints "[name] is chirping". In main, create an Animal[] array with mixed types. Loop through it: use instanceof to check the type, downcast, and call the type-specific method for each. Also print a count of each type at the end.Animal[] animals = { new Dog("Rex"), new Cat("Whiskers"), new Bird("Tweety"), new Dog("Max"), new Cat("Luna") };. Loop with counters: int dogs=0, cats=0, birds=0; for(Animal a : animals){ if(a instanceof Dog){ ((Dog)a).fetch(); dogs++; } else if(a instanceof Cat){ ((Cat)a).purr(); cats++; } else if(a instanceof Bird){ ((Bird)a).chirp(); birds++; } }. Then print counts.final prevents any subclass from overriding it. Similarly, the penalty rate for overdraft should be a static final constant that nobody can change. This exercise practices using final to protect critical behavior and values.BankAccount with a private field balance (double), a static final constant PENALTY_RATE = 0.05, and a constructor. Add a final method deposit(double amount) that adds to balance. Add a final method withdraw(double amount) that deducts from balance but applies a penalty (balance -= amount * PENALTY_RATE) if the result would go negative. Add getBalance(). Create a subclass SavingsAccount that adds a method addInterest(double rate) that increases balance by balance * rate — but cannot override deposit or withdraw. In main, demonstrate all operations.public static final double PENALTY_RATE = 0.05;. Final method: public final void deposit(double amount){ balance += amount; }. Withdraw with penalty: public final void withdraw(double amount){ if(balance - amount >= 0) balance -= amount; else balance -= amount * PENALTY_RATE; }. SavingsAccount: class SavingsAccount extends BankAccount { SavingsAccount(double b){ super(b); } public void addInterest(double rate){ // need a protected setter or use deposit } }. Tip: make balance protected so SavingsAccount can access it for interest calculation.