You are already familiar with storing data in everyday life. For example, I write shopping lists down because my human memory is not good at accurately recalling this information. I can share this data by giving my shopping lists to someone else. I can change it, copy it, and dispose of it.
Just like a shopping list, software needs to write stuff down so that it can store and send data. You can't expect your supplier's staff to memorize contact details for hundreds or thousands of their customers, so you expect them to store all your contact details. This is great because you don't have to re-enter your details every time you use their systems.
Computers are much better at reliably holding vast quantities of data in memory than humans - as long as they remain switched on and working! But what if they are switched off, have closed applications, or have too much to store at once in-memory? You still want to have saved data so that you can retrieve it later. This long-term storing of data is called persisting. In other words, the data persists beyond the lifetime of the application.
But how is this actually applied when writing applications?
Great question! That's why I've provided you with a concrete situation (detailed below) that we'll use throughout this course to practice applying new concepts as you learn them. So, let's dive in and persist. 😉
Introducing a Case Study in Selling Robots
You've been asked to help out the Smith family business: Smiths' Robot Bits! They run a small shop that sells parts people can use to build robots:
The business has been going well. Their customer base has grown, and the Smiths can no longer remember all their client details. Currently, they keep a handwritten notebook, but that's hard to share with several new people in the office, and it's starting to come apart. The range of robot parts they sell has grown, and they have taken on apprentices to help in the warehouse and to sent parts by mail. Their existing manual system no longer works.
So, they have decided to commission some developers (you and me) to build a software application to help them run their business. Together, we will write the part of the software that stores information for the shop staff.
To start with, let's think about what we need to store - to persist - to help our happy shop. We will focus on two outline user stories, or use cases:
Use case #1: Customers
The shop staff wants to record details of its customers so that they can:
Add orders to existing customers without having to ask for their contact details each time.
Send details of promotions to previous customers.
This list of people will be maintained (created, edited) by one person, but needs to be viewable by all the staff.
Use case #2: Robots
The second is that the shop should have an electronic catalog of the components that it sells, to:
Help the shop staff manage and track what is available and what its characteristics are.
Publish it online so that potential customers can browse it.
Together we will build software to fulfill these cases, using a variety of technologies to build up your skills. Each chapter looks at a new technology; I will show you how to use it for the customers, you will have a chance to use it for the robot part catalog, and I will show you a sample solution.
To start with, we need to know how to store these details. And how do you store information? By persisting data, of course! And for that, you need to know about something called serialization.
Using Serialization to Persist Your Data
In addition to saving, you often also want to share data between software applications on different computers, so that lots of people can all refer to the same dataset (such as customer details or a product catalog). While keeping and sending data are two slightly different tasks with different needs, they tend to be solved similarly: take the data that is in memory and write it out as a series of bytes to some storage or transmission medium, like a disk. When you want to recall it, you read it back from that series of bytes. This is called serialization and deserialization.
Serializing means to do one thing after another. When persisting data, you usually have some medium (a file on disk), and you tell the software to take the data you want to store and write out each detail one after the other. It's similar to what people do when they write things down as reminders, or to share it with others.
How does this work in code?
Say you want to store contact details for a friend, Sam Jones. Sam lives at 12 Letsbe Avenue, Royston Vasey, MK2 3AU, UK, has an email sam.jones@openclassrooms.co.uk, and a mobile phone +44 7700 900081. You need a class to store Sam's details, and a class to persist it.
I'll show you one way of setting that up:
Let's look at that in more detail. Here is a simple data object class for storing a person's details:
public class Person {
public String name;
public String address;
public String email;
public String telephone;
}
This is called a data object because all it does is wrap, or encapsulate, a set of associated data.
When it comes to serializing the details to a file, you need to associate the property with its content value. For our Sam example, Sam Jones
would go in the name
field, and sam.jones@openclassrooms.co.uk
in the email
field.
You can usually tell the difference between a name, an address, an email, and a telephone number. However, with software, you have to explicitly detail which values go with which property. A simple way to do this is to write out the details in a specific order and read them in using the same order. Write out the name first, address second, and so on, and read name back in first, address second, and so on — the order of serializing and deserializing defines which value goes with which property.
In the case of a person, you can tell the software to write the information out one line at a time, using the Java IO library, as follows:
public void serialisePerson(Person personToStore) throws IOException {
BufferedWriter store = new BufferedWriter(new FileWriter("Store.txt"));
store.write(personToStore.name);
store.newLine();
store.write(personToStore.address);
store.newLine();
store.write(personToStore.email);
store.newLine();
store.write(personToStore.telephone);
store.newLine();
store.close();
}
This creates and opens a writer from the Java IO library that writes to the file Store.txt
in the local directory, wraps it in the BufferedWriter
, which gives the handy newLine
method used further down, and then writes out each person property one after the other. The writer must be closed to ensure all the content is written and the file released for use by other applications - such as readers.
If you populate a person object with Sam’s details above, and run the above code, it will create a file Store.txt
that contains the following:
Sam Jones
12 Letsbe Avenue, Royston Vasey, MK2 3AU
sam.jones@openclassrooms.co.uk
+44 7700 900081
To read it back in - to deserialize it - read each property detail in the same order:
public Person deserialisePerson() throws IOException {
BufferedReader store = new BufferedReader(new FileReader("Store.txt"));
Person storedPerson = new Person();
storedPerson.name = store.readLine();
storedPerson.address = store.readLine();
storedPerson.email = store.readLine();
storedPerson.telephone = store.readLine();
store.close();
return storedPerson;
}
This is the mirror of the storing code; you create a reader that opens the file Store.txt in the local directory, wrap that in a BufferedReader to provide the convenient readLine method, then read each line into the person property one after the other.
Success! We have persisted a person’s details so that it can be retrieved later - or even by a different computer system. 💪
Try It Out for Yourself
Now it's your turn to serialize and deserialize a robot part. In addition to what we've already accomplished, the Smiths' Robot Bits shop also needs to store descriptions of the various robot parts that are for sale for their online catalog.
Think about what properties you might need to store for a robot part. Once you're ready, complete the following tasks:
Write a simple
RobotPart
class like thePerson
class above that contains those properties.Write a
RobotPartPersister
that serializes and deserializes theRobotPart
to disk.
As soon as you're done, scroll down and check your work against my solution. 😎
Serializing Solution
Here I'll show you how another example of how to write a persister using the built-in streaming library, this time for robot parts:
For reference, here is a simple RobotPart example; yours will hopefully be more imaginative!
public class RobotPart {
public String name;
public String description;
public String supplier;
public int weight;
}
The matching RobotPartPersister that has a serializing method (that writes each property of a part to the file) and a deserializing method (that reads each property from a file into a new part instance):
public class RobotPartPersister {
public void serialisePart(RobotPart aPart) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("Store.txt"));
writer.write(aPart.name);
writer.newLine();
writer.write(aPart.description);
writer.newLine();
writer.write(aPart.supplier);
writer.newLine();
writer.write(aPart.weight);
writer.newLine();
writer.close();
}
public RobotPart deserialisePart() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("Store.txt"));
RobotPart part = new RobotPart();
part.name = reader.readLine();
part.description = reader.readLine();
part.supplier = reader.readLine();
part.weight = Integer.parseInt(reader.readLine());
reader.close();
return part;
}
}
Testing Your Solution
For one thing, you can't hear too often: it's important to test! You can do this using unit tests, but for now, you can write a simple test as follows.
To recap, first add a toString()
method to the RobotPart class; most integrated development environments will have a way to create this automatically:
public class RobotPart {
public String name;
public String description;
public String supplier;
public int weight;
@Override
public String toString() {
return "RobotComponent [name=" + name + ", description=" + description + ", supplier=" + supplier + ", weight=" + weight + "]";
}
}
Secondly, add a method that populates an object, serializes it, then reads the contents of the file and compares the one written with the one read, as follows:
public static void main(String[] args) throws IOException {
RobotPart sharklaseradapter = new RobotPart();
sharklaseradapter.name = "Shark Laser Adapter";
sharklaseradapter.description = "Collar and fittings for mounting laser on shark. Shark not included";
sharklaseradapter.supplier = "Dr Nono";
sharklaseradapter.weight = 5000;
RobotPartPersister persister = new RobotPartPersister();
persister.serialisePart(sharklaseradapter);
RobotPart storedpart = persister.deserialisePart();
if (!sharklaseradapter.toString().equals(storedpart.toString())) throw
new RuntimeException("Store failed! "+storedpart+" does not match "+sharklaseradapter);
}
This gives you a quick and dirty way of running a simple test because you can run it as a standalone application without requiring any other libraries. It also provides an example use of the object so that when another programmer looks at it, they too can see one way of using it.
Let's Recap!
You've learned how to use the readers and writers in the built-in Java streaming library to store objects to a file on disk. You can also use them to write to networks, printers, logs, consoles, and more.
Remember:
Persistance refers to storing data long term.
Writing out a single object instance to a disk file is called serializing, and reading it back in is called deserializing.
Use serializing to persist object instances over time (you can store it for days, months, years) and to share it (you can send the file to someone else to read into their system).
This is a straightforward way of persisting objects by serializing them. However, it is quite fragile - if you change anything in person or RobotPart, then you might not be able to read in old stored objects. In the next chapter, we will look at storing properties using key-value pairs that will solve this problem.