Skip to content

Lab 3: Writing a Class

In this lab, you will implement a Car class that represents a car. You will learn about non-static attributes and methods. And you will gain experience creating and using objects.

Learning Objectives

After completing this lab, you should be able to:

  • Interpret a UML class diagram and stub out the corresponding code.
  • Implement constructors, accessors, mutators, equals, and toString.
  • Write a test program that calls non-static methods of another class.

Step 1. Getting Started

Do the following steps in VS Code:

  1. Create a lab03 folder under your src/labs folder.
  2. Add two new files: Car.java and Main.java.
  3. Write the package and public class statements.
  4. Write a Javadoc comment with your name and date.

When finished, your Car.java and Main.java should look like this:

VS Code screenshot

Step 2. UML Class Diagram

Based on the diagram below, declare each attribute and write a stub for each method. Do not implement any methods yet, but make the code compile. For example, if a method returns a String, write return ""; as a stub (placeholder). Show your code to the instructor or TA before proceeding to the next step.

classDiagram
    class Car {
        - make: String
        - speed: double
        - year: int
        + Car(make: String, year: int)
        + getMake() String
        + getSpeed() double
        + getYear() int
        + accelerate() String
        + brake()
        + equals(obj: Object) boolean
        + toString() String
    }

Note

The brake() method is void (does not return a value). The word void is usually omitted in UML class diagrams.

Step 3. Constructor, Getters

The purpose of a constructor is to initialize the attributes of this object. Notice the Car class has three attributes: make, speed, and year. The constructor has two parameters: make and year. Use the parameters to initialize this object's make and year. Initialize this object's speed to zero. Then write the Javadoc comment for the constructor.

Note

When writing documentation for constructors, do not use the word create. Constructors do not "create" objects; the new operator creates an object. Once an object has been created, the constructor is called to initialize the object.

The purpose of a getter method is to return the current value of an attribute. Notice that the attributes are private, meaning they can be accessed only in the Car class. Other classes, like Main, will need to use the public getters to access the attributes. Implement each getter by returning the corresponding attribute.

Javadoc comments are not required for most getter methods. However, a Javadoc comment is required if a getter does more than simply return an attribute.

Step 4. Test the Constructor

Before implementing the other methods in Car.java, let's make sure the constructor and getter methods work correctly. Open Main.java and define a main method.

Tip

In VS Code, you can type the word main inside the class, press Ctrl+Space (shortcut for "Trigger suggestion"), and press Enter. That will generate the code for main for you.

However, you should memorize how to write a main method on your own, so that you will be able to do so on a written exam, without assistance from VS Code.

Write a Javadoc comment for main, for example:

/**
 * Test the Car class.
 *
 * @param args command-line arguments
 */

Then add the following code to main:

  1. Create two Car objects.
    • Each car should have a different make and year.
  2. Write System.out.println() statements that show the attributes of car:
    • getMake() should return the make you chose.
    • getSpeed() should return zero (at this point).
    • getYear() should return the year you chose.
    • You should have six println statements in total (3 for each car).
  3. Write one more System.out.println(); in the middle to separate the two cars.

Run the Main program by clicking the button or pressing F5. The output should look something like:

Car #1's make: Nissan
Car #1's year: 2003
Car #1's speed: 0.0

Car #2's make: Tesla
Car #2's year: 2025
Car #2's speed: 0.0

Step 5. Accelerate and Brake

The accelerate() method increases the current speed of the car by 5 miles per hour. After increasing the speed, accelerate() returns a string describing the change. Use the format "15.0 + 5.0 = 20.0" where 15.0 is the old speed and 20.0 is the new speed.

Note

The String.format() method is useful for formatting double values. For example, you can format the speed values to one decimal place as follows:

return String.format("%.1f + 5.0 = %.1f", speed - 5, speed);
Each %.1f in the format string is replaced by an argument. So the first %.1f is the argument speed - 5, and the second %.1f is the argument speed.

Your code must not allow the speed to increase above 150 miles per hour. If the speed is already 150, and accelerate() is called, the speed should remain 150. In that case, the method should return the string "150.0 + 0.0 = 150.0".

The brake() method should decrease the current speed by 5 miles per hour. Your code must not allow the speed to decrease below 0 miles per hour. Notice the brake() method is void, which means no value is returned.

Write Javadoc comments for accelerate() and brake() before moving on to the next step.

Step 6. Test Driving the Car

Add the following code to Main:

  1. Call System.out.println(); to output a blank line.
    • Doing so will separate Step 6's output from Step 4's output.
  2. Call the accelerate() method of the first car object.
    • Print the value returned from accelerate().
    • Call getSpeed(), and print the result.
  3. Call the brake() method of the first car object.
    • Call getSpeed(), and print the result.

The output for this step should look something like:


0.0 + 5.0 = 5.0
Car #1's speed: 5.0
Car #1's speed: 0.0

Step 7. The equals() method

The purpose of the equals() method is to determine if two objects are equivalent. Replace your equals() method with the following partial solution:

@Override
public boolean equals(Object obj) {
    if (obj instanceof Car) {
        Car car = (Car) obj;
        return true;  // TODO compare make and year
    }
    return false;
}

The above code:

  1. Overrides the default implementation of equals().
    • By default, equals() returns true if this and obj are the same object.
  2. Checks if obj, which can be any type of object, is actually a Car object.
    • If not, then equals() returns false at the end by default.
  3. Declares a Car variable named car to reference the same object as obj.

Finish the method by replacing the word true with an expression that determines whether car has the same make and year as this object. In other words, does car.make equal this.make, and does car.year equal this.year?

Warning

In Java, comparing strings using the == operator does not work. You must call one string's equals() method and pass the other string as the argument.

Test your equals() method by adding the following code to Main:

  1. Call System.out.println(); to output a blank line.
  2. Create a third Car object with the same make and year as the first Car object.
  3. Add the following two lines:
    System.out.println("Car #1 equals Car #2: " + car1.equals(car2));
    System.out.println("Car #1 equals Car #3: " + car1.equals(car3));
    

The first line should output false, because the two cars are different. The second line should output true, because the two cars have the same make and year.

Step 8: The toString() method

The purpose of the toString() method is to return a string that represents the object. Replace your toString() method with the provided solution:

@Override
public String toString() {
    return String.format("A %d %s that is going %.1f mph",
        year, make, speed);
}

The above code:

  1. Overrides the default implementation of toString().
    • By default, toString() returns the class name and the memory location of the object. For example, "labs.lab03.Car@5b6f7412".
  2. Uses the String.format() method to build a string based on the object's attributes.

Note

Javadoc comments are not required for @Override methods, because the documentation is already defined by the default implementation. In VS Code, you can hover the mouse over a method's name to see the documentation.

Test your toString() method by adding the following code to Main:

  1. Call System.out.println(); to output a blank line.
  2. Call the accelerate() method:
    • One time for the first car.
    • Two times for the second car.
    • Three times for the third car.
  3. Write System.out.println() statements that print each of the car objects.
    • You don't have to call the toString() method. System.out.println(car1) will automatically call the toString() method of car1.

The output should look something like:


A 2003 Nissan that is going 5.0 mph
A 2025 Tesla that is going 10.0 mph
A 2003 Nissan that is going 15.0 mph

Step 9. Review and Submit

Your final program should call println() 18 times. Therefore, you should have 18 lines of output (including 4 blank lines). If not, double check that you completed the following steps:

  • Step 4: output 7 lines
  • Step 6: output 4 lines
  • Step 7: output 3 lines
  • Step 8: output 4 lines

Tip

You will refer back to this code in a future lab. To make the code easier to remember, add the bullets above as comments to Main.java. For example:

// Step 8: output 4 lines
System.out.println();
car1.accelerate();
car2.accelerate();
car2.accelerate();
...

Submit both Car.java and Main.java to Gradescope. Points will be allocated as follows:

Criterion Points Details
Compile 0 pts Success Required
CompileOfficialTests 0 pts Success Required
Style 2 pts Partial Credit Possible
OfficialTests 8 pts Partial Credit Possible