Lab 9: Interfaces
Creating your own interfaces is allowed and can happen. More often, though, we use interfaces built into Java that allow us to use built-in Java methods and procedures. This week we'll be implementing two of the most common of these: Comparable, and Serializable.
Comparable is the most straight-forward of these two. A class that implements Comparable must have a method public int compareTo(Object o), which is well described in the Javadoc that is linked to above. Methods that require input objects be Comparable include Arrays.sort(Object[]), which uses compareTo to sort the elements of the input array.
Serialization is the act of converting an object in memory to a compact binary representation. You usually do this for saving to disk. This representation can then be read in to re-create the object again.
A good serialization method (which is built into Java) will save you a lot of grief when it comes time to save your data. The serialization code handles your file format and parsing for you. Take a few minutes to read the short article here that describes what it does.
Serializable is a marker interface, which is to say, no methods must be implemented to implement this interface. Instead, implementing Serializable merely means that you want this object to be able to be serialized and have committed at least a modicum of thought to that process; you don't actually have to implement anything to make that happen. This is one of the nice things about programming in Java - there is a huge amount of useful code you can access for common tasks. In this case, you get all the code for reading and writing you custom data structures to a file just by putting "implements Serializable" in your class. That is a lot of value for very little effort.
Serialization requires using ObjectInputStreams and ObjectOutputStreams to read and write data to the disk. We are not going to go into the details of their use, but you can find a working sample in the Test File section below.
Note - some of the Java interfaces and methods that you will use require imports and others do not. The most commonly used objects are stored in java.lang and do not need to be imported. You need to pay attention to compiler errors and learn to figure out when an import is needed. The "cannot find symbol" error usually means either a misspelling or that a library needs to be imported.
The Assignment
You are going to create a Car class with the following requirements:
- A Car consists of a String make (e.g. "Toyota", "Ford", etc), an int year, and a double cost. For the sake of simplicity, you can keep those three variables public and not worry about set() and get() methods.
- The constructor must take those three values, in that order (String, int, double) and save them to member variables.
- Car must override the toString() method from Object. The new version must return a String in the following format: "year make, $cost".
- Car must implement both the Comparable and Serializable interfaces.
- Java has a static method Arrays.sort(Object[]) for sorting objects in an array. To use this method with your custom classes, you must implement 'Comparable' and add any methods that it requires. Figure out how to do this by reading up on the Comparable interface, and the static Arrays.sort(Object[]) method. (Note - Think back to what you learned implementing the Bubble Sort algorithm. You can sort an array by just comparing two adjacent items at a time.)
- Car objects must be able to be written to and read from disk using ObjectOutputStreams and ObjectInputStreams. You will figure out how to do this by reading up on the Serializable interface.
- Car must be properly javadoc'ed.
- Include a main() method for testing your sorting and read/write functionality. You can use the sample test file below or modify it any way you want.
Test File
Your instructors will use a test file similiar to the one below for grading.
Your code will be tested for two qualities that derive from the interfaces you implemented:
- (Comparable)The ability to sort an array of Car objects by cost using Arrays.sort()
- (Serializable)The ability to write a Car object to a binary file and read it back again
public class Test {
public static void main(String[] args) {
// Create test data
Car[] cars = new Car[5];
cars[0] = new Car("Toyota", 2008, 15000);
cars[1] = new Car("Porsche", 1964, 6000);
cars[2] = new Car("Chevy", 1984, 12500);
cars[3] = new Car("Ford", 1971, 4000);
cars[4] = new Car("Volvo", 1996, 30000);
// Test sorting:
System.out.println("Original list:");
System.out.println("--------------");
for(int i=0; i<cars.length; i++){
System.out.println(cars[i]);
}
System.out.println();
System.out.println("Sorted by cost (lowest to highest):");
System.out.println("-----------------------------------");
Arrays.sort(cars);
for(int i=0; i<cars.length; i++){
System.out.println(cars[i]);
}
// Test writing serial data to file
try {
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("object.data"));
output.writeObject(cars);
output.close();
} catch (Exception e) {
System.out.println(e);
}
// Test reading serial data from our file
Car[] carsFromFile = new Car[5];
try {
ObjectInputStream input = new ObjectInputStream(new FileInputStream("object.data"));
carsFromFile = (Car[])(input.readObject());
input.close();
} catch (Exception e) {
System.out.println(e);
}
System.out.println();
System.out.println("Read list from file:");
System.out.println("--------------------");
for(int i=0; i<carsFromFile.length; i++){
System.out.println(carsFromFile[i]);
}
}
}
Understanding Serialized files
The test code above will produce a binary file named "object.data".
Run "cat object.data" to dump the raw file to the screen. It should be mostly unreadable, but have several recognizable words in it. The serialization code stores ints and floats in their binary representations. 'cat' incorrectly interprets these as ASCII. This causes the strange characters. Some of your Strings should be visible since they are mostly stored as ASCII.
Run "strings object.data" to parse the binary data file. This program searches for collections of printable characters and assumes they are strings. You should see all of your original Strings from the program, plus some extraneous characters. This program does a good job at finding strings, but often mistakes binary code for ASCII.
Run "xxd object.data" to see the binary data in hex format. This program displays the address of each byte, the hex of each byte, and an ASCII conversion if it is printable. If you look hard, you can find your int and double values now. The int '2008' converts to '0000 07d8' in hex. If you used that number as a model year in your test data, you should be able to find it in the hex dump. Look for it near the name of its make.
Note - it is not important that you learn to read a serialized file manually. It is good enough that your program can read it. But sometimes it is useful to know how to look under the hood and pull information directly out of binary files.
What to hand in
Car.java
Your file must be in a directory named 'Lab09'
Delete any *.class or other unneeded files prior to submitting
Delete all of your javadoc files. We will recreate them ourselves.
Delete the object.data file if created by your test script
When you run the submit script, the directory and its contents get zipped and sent to your instructor. Ask for help early if you are having problems running the submit script.
Make sure that you run the correct submit script for your instructor:
- CDR Blenkhorn: /courses/blenk/submit Lab09
- Dr. Taylor: /courses/taylor/submit Lab09