AP CSA Unit 9 Ultimate Guide

·

14 min read

Welcome! This guide will teach you everything you need to know about Unit 9 for the AP CSA exam. This unit is weighed 5-10% on the exam.

Quick Note

Throughout this guide, I use the this keyword. This simply refers to the current object your referring to. If we had the following constructor for something like an Animal class that has a String attribute of type:

public Animal(String t) {
    type = t;
}

The following would constitute as the same code as well:

public Animal(String type) {
    this.type = type;
}

The this keyword is used to distinguish between the method argument and the class attribute. The this keyword doesn't show up on the AP exam, but they won't take off points if you use it an FRQ.

9.1 - Create Superclasses and Subclasses

Introduction

When one thinks of inheritance, they may think of biological traits passed down from parent to child.

In Java, inheritance is used to build a hierarchy of classes that have similar traits. Parent classes (aka Superclasses) have atrributes and methods that can be inherited by child classes (aka Subclasses)

In Java, each subclass can only have one superclass.

Practice Question

Scroll below answer choices to see the answer

In a particular school system, there will be two types of people: students and teachers. Both types of people will have names, ages, addresses, and ID numbers. Students will also have a grade level and GPA, while teachers of a hire date and a salary.

Which of the following would be the best class design for this scenario?

A. Three independent, unrelated classes: Person, Student, and Teacher
B. A Person superclass, and two subclasses: Student and Teacher
C. A Person superclass. Teacher is a subclass of Student, and Student is a subclass of Teacher.
D. A Student superclass. Teacher is a subclass of Student, and Student is a subclass of Person.
E. Two superclasses, Student, and Teacher, with Person as a subclass of each

The answer would be B because the best choice for a superclass would be Person. Both Student and Teacher have names, ages, etc.; it would be best to put those atributes in a generic superclass, like Person, that can be further extended by subclasses, like Student and Teacher.

Extending Superclasses

The keyword extends is used to create an inheritance relationship between a superclass and a subclass. For example, if we have the following UML diagram (not heavily tested on AP Exam)

UML Diagram

We can see that Rectangle and Circle are both subclasses of GeometricObject. To write this as code, we would write

public class Rectangle extends GeometricObject {
    // ...
}

public class Circle extends GeometricObject {
    // ...
}

What would not be a valid statement would be something such as

public class Rectangle extends Circle {}
public class GeometricObject extends Rectangle {}

Anwyays, Rectangle and Circle not only have access to their class specific methods, but they also have all the public methods from their superclass, GeometricObject. Take a look at the folliwng method calls.

GeometricObject geoObject = new GeometricObject();
Circle circle = new Circle(4);
Rectangle rectangle = new Rectangle(3, 5);

circle.getColor(); // Valid ✅ From superclass
circle.getDiameter(); // Valid ✅
rectangle.getDateCreated(); // Valid ✅ From superclass
rectangle.setWidth(6); // Valid ✅
geoObject.setColor("red"); // Valid ✅
geoObject.getRadius(); // Invalid ❌, this is a method from Circle and not GeometricObject
circle.getWidth(); // Invalid ❌, this is a method from Rectangle and not Circle

9.2 - Creating Constructors for Subclasses

The following class hierarchy diagram will be used for this section as a demonstration. It was taken from AP Classroom.

Class Diagram

Subclass Constructors

Lets do a partial implementation of the Performer class:

public class Performer {
    private String name;
    private int age;

    public Performer() {
        name = "John";
        age = 30;
    }

    public Performer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // ...
}

Constructos are not inherited from superclasses. By default, every time you instantiate an object, the no-arg constructor of the object's superclass will be invoked.

For example, if we implement the Musician class:

public class Musician extends Performer {
    private String instrument;

    public Performer() {
        instrument = "Piano";
    }

    public Performer(String instrument) {
        this.instrument = instrument;
    }

    public String getInstrument() { return instrument; }
    public void playInstrument() {
        // ...
     }
}

If we create an object using the Musician class, the no-arg Performer constructor will be invoked, and then the Musician constructor will be invoked.

Musician musician = new Musician("Guitar"); // Name -> John, Age -> 30, Instrument -> Guitar

Using the super keyword to invoke constructors

The keyword super refers to the superclass of an object and can be used to invoke an object's superclass's methods and constructors. Invoking methods using super is discussed in topic 9.4.

The syntax for calling the constructor of a superclass is:

public class A extends B {
   public A() {
       super(); // can also pass in parameters if needed
   }
}

When we said that an object's superclass constructor is automatically invoked, it really meant that Java would automatically insert super(); before all of the subclass constructor logic.

If you want to call super, it must be the FIRST statement in the constructor.

If we go back to the Musician and Performer classes, if we wanted to add a constructor that allows you to give the Musician a name and an age as well, you would have to call super with the name and age as parameters.

public class Musician {
    // ... previously defined logic above

    public Musician(String name, int age, String instrument) {
        super(name, age);
        this.instrument = instrument;
    }
}


9.3 - Overriding Methods

This is a short and sweet topic, but it has a very important concept.

When writing methods for a subclass, you can do one of the following

  • Inherit public methods from the superclass
  • Write new methods in subclass
  • Override (aka write new implmentation) of a method that is already in the superclass

For example if the Peformer class had a method called perform, any subclass of Performer can write a new implementation for that same method. Lets do it with the Musician class:

public class Musician {
    // ... previous logic
    @Override
    public void perform() {
        System.out.println("Musician performs " + instrument);
    }
}

As you can see, it is generally a good idea to add the @Override annotation to let other developers know that this is an overrided method. This won't be on the AP Exam because they want to test your knowledge on this.

9.4 - Using super to invoke methods

This is pretty self-explantory. You can use super to call a superclass's methods by saying:

super.getName();
super.setAge(76);

It doesn't have to be the first statement in the method.

Practice Question 9.4.1

Scroll below answer choices to see the answer

Consider the following classes:

public class Device {
    private String name;
    private String model;
    public Stadium(String n, String m) {
        name = n;
        model = m;
    }
    public void printInfo() {
        System.out.print("Device with name: " + name + " and model: " + model);
    }
}

public class Laptop extends Device {
    private String os;
    public Laptop(String n, String m, String o) {
        super(n, m);
        os = o;
    }
    public void printInfo() {
        /* Missing Code */
    }
}

The following code segment appears in a class other than Laptop or Device:

Laptop laptop = new Laptop("MacBook", "Pro", "macOS");

The goal is to produce the following output:

MacBook that is the model Pro - runs macOS

Which of the following can be used to replace /* missing code */?

A:
System.out.print(name + " that is the model " + model + " - runs " + os)

B:
super();
System.out.print("- runs " + os)

C:
super.prinfInfo();
System.out.print("- runs " + os)

D:
super.prinfInfo("- runs " + os);

Practice Question 9.4.2

Scroll below answer choices to see the answer

Consider the class definitions shown below:

public class Car {
    private int mileage;
    public Car() {
        mileage = 0;
    }
    public void drive(int miles) {
        mileage += miles;
    }
}

public class ElectricCar extends Car {
    private double battery;
    public ElectricCar() {
        battery = 100;
    }
    public void drive(int miles) {
        /* Missing Code */
    }
}

A Car keeps track of the miles drive (mileage). An ElectricCar is a type of Car that not only tracks the amount of miles drive, but also tracks the battery of life of the car, starting at 100% and is reduced every 4 miles driven. Which of the following would be an appropriate implementation of the drive method in the ElectricCar class?

A:
public void drive(int miles) {
    battery -= (miles / 4.0);
    mileage += miles;
}

B:
public void drive(int miles) {
    battery -= (miles / 4.0);
    super.drive();
}

C:
public void drive(int miles) {
    battery -= (miles / 4.0);
    super.drive(miles);
}

D:
public void drive(int miles) {
    battery -= (miles / 4.0);
    super(miles);
}

The correct answer would be C. Here is what is wrong with the other answers:

  • A: the ElectricCar class doesn't have access to mileage because it is a private variable in Car
  • B: the drive method is being called incorrectly; it never received an integer argument.
  • D: the super keyword is being used incorrectly; it is trying to call the superclass constructor rather than an actual method.

9.5 - Creating References using Inheritance Hierarchies

Quick Vocab

A reference variable is considered polymorphic if it refers to different classes at different points in your code. A reference variable can store a reference to its declaread type, or to any subclass of the declared class.

Example

Lets use the following classes for an example:

public class Book {}
public class Novel extends Book {}
public class Poem extends Book {}
public class Epic extends Poem {}

As you can see, Novel and Poem are both subclasses of Book, and Epic is a subclass of Poem. Look at the following declarations if we assume that each of the classes have a constructor with no parameters.

Book book1 = new Novel();
Book book2 = new Poem();
Poem book3 = new Epic();
Book book4 = new Epic();

All of these reference variables are polymorphic because instead of storing a reference to the declared type (Book), they store a reference to a subclass of the declared type (Novel, Poem, or Epic). The declared type and the actual reference (actual type) need to have an inheritance relationship in which the actual type is a subtype of the declared type.

Poem incorrect = new Book();

This declaration is incorrect because not all books are poems --> not all Book objects are Poem objects.

Practice Question 9.5.1

Scroll below prompt to see the answer

Take a look at the following classes:

public class Picture {
    private String title;
    public Picture() {
        title = "Picture";
    }
    // Other methods
}

public class Painting extends Picture {
    private String artist;
    public Picture(String title, String artist) {
        super(title);
        this.artist = artist;
    }
    // Other methods
}

Declare an ArrayList that can store references to objects of both Picture and Painting. Store a reference to a Picture object with the title "sky" and store a reference to a Painting object with the title "Mona Lisa" and the artist "Leonardo Da Vinci".

To make sure we can store both Picture and Painting objects, we need to declare the ArrayList using the Picture class so that we can store references of Picture objects and subclasses of Picture objects.

ArrayList<Picture> list = new ArrayList<>();
list.add(new Picture("sky"));
list.add(new Painting("Mona Lisa", "Leonardo Da Vinci"));

Practice Question 9.5.2

Scroll below the answer choices to see the answer

Consider the following class declarations:

public class Staff {
    private String name;
    private String position;
    private double salary;
    public Staff(String name, String position, double salary) {
        this.name = name;
        this.position = position;
        this.salary = salary;
    }
}

public class Sales extends Staff {
    private String area;
    public Sales(String name, String position, double salary, String area) {
        super(name, position, salary);
        this.area = area;
    }
}

A different class contains the following method:

public void introduceSelf(Staff stf) {
    // implementation not shown
}

Which of the following method calls is invalid?

A:
introduceSelf(new Staff("John", "Manager", 100000));

B:
Staff staff = new Sales("John", "Manager", 100000, "Plants");
introduceSelf(staff);

C:
Sales sales = new Sales("John", "Manager", 100000, "Plants");
introduceSelf(sales)

D:
introduceSelf(new Sales("John", "Manager", 100000, "Plants"));

E:
Sales sales = new Staff("John", "Manager", 100000);
introduceSelf(sales)

The correct answer choice is E because you can't declare a Sales object and then reference a Staff object. You can't go backwards in the inheritance hierarchy for polymorphism.

9.6 - Polymorphism

Quick Vocab

A method is polymorphic if it is declared in a superclass and overridden in a subclass. Polymorphism is when a non static overridden method that is executed at runtime from the correct class based on the object type.

Practice Question 9.6.1

Scroll below the answer choices to see the answer

Consider the following classes:

public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public String getDescription() {
        System.out.println("I am a person with name " + name);
    }
}

public class Student extends Person {
    private int grade;
    public Person(String name, int grade) {
        super(name);
        this.grade = grade;
    }
    public String getDescription() {
        System.out.println("I am " + name + " and I am a student in grade " + grade);
    }
    public void study() {
        System.out.println("Studying!");
    }
}

The following code segment is occurs in another class different from Student or Person:

Person maria = new Student("Maria", 10);
System.out.println(maria.getDescription());

The following code segment is occurs in another class different from Student or Person and is supposed to display "I am a person with name Maria". Which of the following explains why it does not?

A. A compile time error occurs because maria is declared as type Person but instantiated as type Student.
B. A compile time error occurs because getDescription() is defined in both Person and Student
C. getDescription() is executed from Student because maria is instantiated as a Student object, so "I am Maria and I am a student in grade 10" is displayed instead of only "I am a person with name Maria".
D. Because getDescription() is defined in both Person and Student, both methods are executed and "I am a person with name Maria" and "I am Maria and I am a student in grade 10" are displayed on different lines.

The correct answer is C. A is not correct because a variable declared as a superclass can be a reference to a subclass. B is not correct because the process described in the choice is called method overriding, and it is allowed. D is not correct because a method is always called from the class that is referenced by the variable calling the method.

Note on Object Casting

Casting objects is not covered on the AP exam, but there have been previous MCQs in which the correct usage is to use object casting, but they don't do it. For example, if we have the following code:

Person alex = new Student("Alex", 8);
alex.study();

This code would not compile becuase the study() method is not defined for the Person object alex. To fix this, we would first have to cast the object to a Student, and then call the method.

((Student)alex).study();


9.7 - The Object Superclass

Every class you write in Java extends the class Object in the package java.lang. For the AP Exam, you need to know how to use the toString() and equals() methods of the Object class and how to override them.

The toString() Method

The toString() method returns a String representation of an object. It is executed if you are printing out an actual object and not calling any methods that return anything. Lets implement this method in the Person class from the previous section:

public class Person {
    // previous logic
    @Override
    public String toString() {
        return "Person: " + name;
    }
}

To invoke it, literally print out the object, and it will implicity call the toString() method.

System.out.println(new Person("John"));

The main method would print out "Person: John" instead of the defaault implementation in the Object class. If we printed out a Student object, it would use the Person class's toString() method instead of the Object class's because Person is the next level up.

The equals() Method

The equals() method is another method in Object that tests to see if two objects are equal. When overriding the method, you may need to use the instanceof operator (not tested on AP Exam) to see if an object is an instance of a certain class. Lets implement the Person class equals() method:

public class Person {
    // previous logic
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Person) {
            Person person = (Person) obj;
            return name.equals(person.name);
        }
        return false;
    }
}

The syntax to call the equals() method is:

Person person1 = new Person("John");
Person person2 = new Person("Jimmy");
System.out.println(person1.equals(person2));

Conclusion

This was a huge guide and I hope it helped you in some way. I highly reccomend using this guide other resources like AP Classroom to help you prepare for the AP Exam. Please share this with other AP CSA students because it will help them tremendously!

Sigining off 👋

Please email me if I have any typos in the guide or if something doesn't look right.