Introduction
Java is an object-oriented language, so understanding what objects are and how to use them is crucial to using Java effectively. An object in OOP is a lot like an object in the real-world. In the real-world, the term “object” is very non-specific. An object can refer to basically anything, whether it’s a book, a planet, a receipt, a red blood cell, an engine, or a dog. These are all types of objects. These objects vary greatly in terms of their size, shape, colour, complexity, purpose, and so on.
Object’s are often composed of or associated with other objects, and these objects may communicate or interact with each other in various ways. For example, a car is made up of thousands of objects such as wheels, doors, windows, lights, seats, a radio, pedals, an engine, etc. When driving a car, various objects/components must interact in a certain way to move the car forward i.e. a human presses on the accelerator pedal, which delivers fuel into the engine, which turns the crankshaft, which powers the drivetrain, which turns the wheels. This chain of communication works because every object in the chain has been designed to interface with its connecting objects.
That is the sort of thing OOP tries to emulate–a number of separate but connected objects that interact to achieve a goal. Of course, OOP does not deal with real objects, but “virtual” objects that are defined and created by you, the programmer. These objects can take on any form, complexity, and responsibilities you see fit. You are limited only by your imagination. In practice, the types of objects you’ll deal with depends on the application you’re building.
State and Behaviour
Two key properties of both real and virtual objects are state and behaviour. Take a lamp, for example. A lamp can be on or off, which are two possible states. A lamp can also turn on and off, which are two behaviours. Typically, behaviour affects state. That is, turning on the lamp puts it in an on state and turning off the lamp puts it in an off state. You can think of state as data about an object, or as an object’s attributes. Here are some more examples of the lamp’s state:
- brightness
- hue (the emitted colour)
- colour (the colour of the lamp itself)
- height/width/length (physical dimensions of the lamp)
- weight
- timer duration (for an auto-off function)
- time remaining on timer
- power efficiency rating
- heat output
The behaviour of an object is about what it can do. For example, the lamp can:
- Increase/decrease brightness
- Change hue
- Turn timer on/off
- Increase/decrease timer duration
- Increase/decrease height (if extendable)
When it comes to OOP, we use variables to represent an object’s state and methods to represent an object’s behaviour.
- State = Data = Variables
- Behaviour = Actions = Methods
Example: Employee Objects
Some of the simplest objects you can define are record-like objects. For example, imagine a company that keeps files/records of all its employees but on paper only. Here are three of those files:
Employee File 1
Name: Michael Orwell
Address: 123 Indigo Rd.
DoB: 1993/11/20
Job Title: Project Manager
Salary: £41,093
Employee File 2
Name: Veronika O’Neill
Address: 789 Yorkminster St.
DoB: 1998/01/15
Job Title: Junior Developer
Salary: £23,476
Employee File 3
Name: Lester Wilcox
Address: 456 Lioborough Ln.
DoB: 1991/05/01
Job Title: Senior Developer
Salary: £35,950
Each file represents a different employee and contains relevant data about them. Let’s look at how we might replicate this in the OOP world.
- First, create a new Java project.
- If you’re on the welcome screen, click Create New Project.
- If you already have a project open, go to File > New > Project….
- Ensure Java is selected and click Next.
- Check Create project from template and click Next.
- Choose a project name (e.g. EmployeeManagementApp), project location, and package.
- Click Finish.
To start, we can take all the fields in the files and turn them into variables.
public class Main { public static void main(String[] args) { String name; String address; String dateOfBirth; String jobTitle; double salary; } }
The first four are String
types because they are textual data. While you could also make salary a String
, I’ve decided to make it a double
because it’s numeric data. There are more suitable types that could be applied but we’ll keep it simple for now. Currently, the variables have no values, so let’s set them to the values of a particular employee like this:
public class Main { public static void main(String[] args) { String name = “Michael Orwell”; String address = “123 Indigo Rd.”; String dateOfBirth = “1993/11/20”; String jobTitle = “Project Manager”; String salary = 41093; } }
But what about the other employees? We could declare further sets of variables like name2
, address2
, dateOfBirth2
, jobTitle2
, and salary2
, and then name3
, address3
, dateOfBirth3
, etc, but we could have 100 employees to deal with so this clearly isn’t the way to go. The problem is we lack structure. It would be nice if we could bundle all this related data together and store it all under one name, like some sort of composite data type. The good news is you can, with objects.
Class: Employee
What we’ll do is create some objects to act as virtual records that perform the same job as the real records i.e. to represent employees in the company. Each object will contain the same set of fields (variables) but hold different data depending on the employee. But before we can do this, we need to write a class. A class defines a type of object, from which many of those objects can be created. You can think of a class as the design, and an object as the product. This is similar to the real-world where a design, such as a blueprint, can be used to construct an object, such as a house. The design may be used over and over again to construct as many houses as required. So, just like you need a house blueprint to construct house objects, you need an Employee class used to construct Employee objects.
Let’s create the Employee
class. First, right-click the src folder and go to New > Java Class. Type Employee and hit Enter. It should look this:
public class Employee { }
Note that when you define a class, you are also defining a new type. You already know of certain pre-existing types such as int
, double
, char
, String
, Scanner
, etc. Employee
is simply another type that can be added to that list. This means we can now create Employee
objects and declare variables of type Employee
to hold those objects. We create an Employee
object the same way as any other object, by using the new
keyword. Back in the main
method, we can write this:
public class Main { public static void main(String[] args) { Employee emp = new Employee(); } }
On the left side is simply a variable called emp
(short for “employee”), and it’s type is Employee
(meaning it can hold an Employee
object). The Employee
object is created on the right side. However, if you look back at the Employee
class, you’ll notice that it’s empty. That means any Employee
objects (such as emp
) will also be empty, which means these objects have no state (fields/variables) nor behaviours (methods).
Instance Variables
Let’s add the variables from before to the Employee
class:
public class Employee { String name; String address; String dateOfBirth; String jobTitle; double salary; }
Now that the Employee
class has some variables, any Employee
objects created from it will possess a copy of these variables. For this reason, they are known as object variables or instance variables (instance is another term for object). Therefore, emp
has a copy of these variables, which we can access by using the .
e.g. emp.name
, emp.address
, etc. With that in mind, let’s make emp
represent employee Michael Orwell by assigning its variables the relevant values:
public class Main { public static void main(String[] args) { Employee emp = new Employee(); emp.name = "Michael Orwell"; emp.address = "123 Indigo Rd."; emp.dateOfBirth = "1993/11/20"; emp.jobTitle = "Project Manager"; emp.salary = 41093; } }
Note: if you get errors, make sure both classes (Main
and Employee
) are in the same package.
Line 4 sets name
to “Michael Orwell”, line 5 sets address
to “123 Indigo Rd.”, line 6 sets dateOfBirth
to “1993/11/20”, line 7 sets jobTitle
to “Project Manager”, and line 8 sets salary
to 41093. Wherever emp
goes, all this data goes with it. Next, let’s print out some information about this employee:
public class Main { public static void main(String[] args) { Employee emp = new Employee(); emp.name = "Michael Orwell"; emp.address = "123 Indigo Rd."; emp.dateOfBirth = "1993/11/20"; emp.jobTitle = "Project Manager"; emp.salary = 41093; System.out.printf("The employee %s is a %s and earns £%.2f annually.\n", emp.name, emp.jobTitle, emp.salary); } }
[Run] The employee Michael Orwell is a Project Manager and earns £41093.00 annually.
Line 10 simply prints out a line of information using the variables of emp
, specifically the employee’s name, job title, and salary. It is presented in a human-readable way.
Instantiating More Employee Objects
Let’s instantiate two more Employee
objects the same way as the first.
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); emp1.name = "Michael Orwell"; emp1.address = "123 Indigo Rd."; emp1.dateOfBirth = "1993/11/20"; emp1.jobTitle = "Project Manager"; emp1.salary = 41093; Employee emp2 = new Employee(); emp2.name = "Veronika O'Neill"; emp2.address = "789 Yorkminster St."; emp2.dateOfBirth = "1998/01/15"; emp2.jobTitle = "Junior Developer"; emp2.salary = 23476; Employee emp3 = new Employee(); emp3.name = "Lester Wilcox"; emp3.address = "456 Lioborough Ln."; emp3.dateOfBirth = "1991/05/01"; emp3.jobTitle = "Senior Developer"; emp3.salary = 35950; System.out.printf("The employee %s is a %s and earns £%.2f annually.\n", emp1.name, emp1.jobTitle, emp1.salary); System.out.printf("The employee %s is a %s and earns £%.2f annually.\n", emp2.name, emp2.jobTitle, emp2.salary); System.out.printf("The employee %s is a %s and earns £%.2f annually.\n", emp3.name, emp3.jobTitle, emp3.salary); } }
[Run] The employee Michael Orwell is a Project Manager and earns £41093.00 annually. The employee Veronika O'Neill is a Junior Developer and earns £23476.00 annually. The employee Lester Wilcox is a Senior Developer and earns £35950.00 annually.
Two more Employee
objects have been created on lines 10 and 17. They are named emp2
and emp3
. Both emp2
and emp3
have been filled with a particular employee’s data. I’ve also renamed emp
to emp1
for consistency (line 3). Note that all three of these Employee
instances (emp1
, emp2
, and emp3
) are individual and distinct, and contain their own copy of the variables. Line 24, like before, prints out information about emp1
. Line 25 does the same thing with emp2
and line 26 does the same thing with emp3
. As we can see from the output, each object contains different data.
Instance Methods
Let’s give Employee
objects the ability to do things by defining methods in the class. For example, instead of duplicating the same line to print information about each employee, let’s have the Employee
objects do this themselves:
public class Employee { String name; String address; String dateOfBirth; String jobTitle; double salary; void printInfo() { System.out.printf("Employee %s is a %s and earns £%.2f annually.\n", name, jobTitle, salary); } }
Employee
now has a method called printInfo
. It has a void
return type because it doesn’t return any data. It’s parentheses are empty because it doesn’t require any data. In the body of this method is a print statement like the one’s in main
. As you can see, it can access name
, jobTitle
, and salary
directly. This is because methods are able to access the variables in the same class. Back in main
, we can replace the three print statements with calls to said method:
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); emp1.name = "Michael Orwell"; emp1.address = "123 Indigo Rd."; emp1.dateOfBirth = "1993/11/20"; emp1.jobTitle = "Project Manager"; emp1.salary = 41093; Employee emp2 = new Employee(); emp2.name = "Veronika O'Neill"; emp2.address = "789 Yorkminster St."; emp2.dateOfBirth = "1998/01/15"; emp2.jobTitle = "Junior Developer"; emp2.salary = 23476; Employee emp3 = new Employee(); emp3.name = "Lester Wilcox"; emp3.address = "456 Lioborough Ln."; emp3.dateOfBirth = "1991/05/01"; emp3.jobTitle = "Senior Developer"; emp3.salary = 35950; emp1.printInfo(); emp2.printInfo(); emp3.printInfo(); } }
[Run] Employee Michael Orwell is a Project Manager and earns £41093.00 annually. Employee Veronika O'Neill is a Junior Developer and earns £23476.00 annually. Employee Lester Wilcox is a Senior Developer and earns £35950.00 annually.
Lines 24-26 call the printInfo
method on all three objects. It may seem obvious, but when you call a method in an object, that method will act with respect to that particular object’s variables. For example, in emp1
we set name
to “Michael Orwell”, jobTitle
to “Project Manager”, etc. Therefore, calling emp1.printInfo()
means that these variables/values will be used by the method. Similarly, emp2.printInfo()
will work with emp2
‘s variables, and emp3.printInfo()
will work with emp3
‘s variables.
With this change, the outcome is still the same but, now, if we want the informational message to change, we only have to change it in one place, the printInfo
method, instead of potentially many places. For instance, lets have the method print out the information in a more professional manner.
public class Employee { String name; String address; String dateOfBirth; String jobTitle; double salary; void printInfo() { System.out.printf("Employee: %s | Address: %s | DoB: %s | Role: %s | Salary: £%.2f.\n", name, address, dateOfBirth, jobTitle, salary); } }
Employee: Michael Orwell | Address: 123 Indigo Rd. | DoB: 1993/11/20 | Role: Project Manager | Salary: £41093.00. Employee: Veronika O'Neill | Address: 789 Yorkminster St. | DoB: 1998/01/15 | Role: Junior Developer | Salary: £23476.00. Employee: Lester Wilcox | Address: 456 Lioborough Ln. | DoB: 1991/05/01 | Role: Senior Developer | Salary: £35950.00.
Nothing major has changed here, just the way the information is presented.
References and the Object Heap
When you instantiate an object, it is placed in an area of memory called the heap. This is where all the objects in your program reside. Let’s use the Employee
class from above as an example. In main, we can simply type new Employee();
:
public class Main { public static void main(String[] args) { new Employee(); } }
This is a perfectly valid program. When it’s run, it will create an Employee
object and place it on the heap, along with any other objects. Figure 11.3 depicts this.

As can be seen, the object contains the five variables as defined in the Employee
class. The 0x01 is the memory address of the object, which represents its location in memory. Objects are destroyed when the program ends so this object would be destroyed immediately after creation.
Let’s now assign a variable to the object.
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); } }
The variable emp1
does not contain the Employee
object directly, but rather a reference to it, as shown in Figure 11.4.

Figure 11.4 shows that emp1
references the object by storing its address (0x01). So, even though we may say “emp1
is/stores an Employee
object”, what we really mean is “emp1
contains a reference to an Employee
object”. You don’t have to be fully aware of the reference when interacting with the object because Java automatically fetches the object and performs the stated action. For example, in the code emp1.printInfo()
, Java looks at the reference in emp1
, finds the corresponding object, and invokes the printInfo()
method on it. However, you do have to be aware of references when passing objects around the application, such as into different variables. For example, what happens when we create another Employee
variable and set it to emp1
?
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = emp1; } }
On line 4, another variable emp2
is declared and set to emp1
. But what does this do exactly? You might think that emp1
‘s object is copied into emp2
, so we end up with two identical Employee
objects, one in emp1
and one in emp2
. That’s not what happens. Object’s are never copied in this way. Java variables follow the pass-by-value rule, which means the variable’s value is copied i.e. the reference. So, the reference in emp1
(0x01) gets copied into emp2
, and we end up with this:

The reference in emp1
is copied into emp2
so both variables reference the one and only object. This means you can interact with the object through either variable. Note that, in reality, we don’t know exactly what a reference looks like because its down to the implementation of the JVM (Java Virtual Machine). It may not be as simple as just the memory address of the object. Just know that a variable stores something that connects it to the object, which we call a reference.
An Un-Referenced Object
The following program contains two variables that each reference an object.
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); } }
Line 3 creates an Employee
object, which is referenced by emp1
. Line 4 creates another Employee
object, which is referenced by emp2
. Figure 11.5 shows this.

What happens if we reassign emp1
to emp2
‘s object?
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); emp1 = emp2; } }
The reference in emp2
gets copied into emp1
, which means emp1
‘s original object no longer has a reference to it, as shown in Figure X.

When an object has no references, it is lost and you cannot regain it, which means we have an object sitting in memory taking up space and is no longer usable. This is an issue in older programming languages because you had to manually manage the memory yourself. You had to make sure you created and deleted objects appropriately lest you risk a memory leak (where disused objects are not properly disposed of and take up more and more memory). In modern languages such as Java, for the most part you don’t have to worry about this issue because of something called a garbage collector. This is a program that runs behind the scenes with your Java program. The garbage collector periodically checks for objects that have no references and automatically deletes them, freeing up the memory for other objects/entities.
Null Reference
If you do not want a variable to reference an object, you can set it to null at any point. For example:
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); emp1 = null; } }
On line 3, emp1
has a reference to an Employee
object, as shown in Figure 11-X:

On line 4, emp1
is set to null, which also clears the reference, as shown in Figure 11-X:

The object is now available for garbage collection.
Note that in all these examples, we have not been setting the objects’ variables i.e. name
, address
, etc. Object variables are given default values depending on their type. int
s default to 0, double
s to 0.0, boolean
s to false, and String
s to null.
Primitive and Reference Types
All types in the Java language fall under one of two categories: primitive types and reference types. There are only eight primitive types in the Java language: byte
, short
, int
, long
, float
, double
, char
, and boolean
. Everything else is a reference type, such as Employee
, Scanner
, String
, DecimalFormat
, ArrayList
, arrays, and the thousands of other types we’ve yet to explore. Most reference types are class types from which objects can be created. As covered in the last section, reference types are named as such because reference variables hold a reference to the object, not the object itself. Conversely, primitive variables simply hold their value. That’s really all there is to it.
An Array Of Objects
One of the most useful things you can do with these kinds of objects is put them in some sort collection for processing, such as an array. You can make an array out of any type, including user-defined types such as Employee
. First let’s instantiate three Employee objects without using an array.
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); Employee emp3 = new Employee(); } }
Lines 3-5 create three Employee
objects: emp1
, emp2
, and emp3
. This is what it currently looks like:

Next, we can set the fields in each object:
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); Employee emp3 = new Employee(); emp1.name = "Michael Orwell"; emp1.address = "123 Indigo Rd."; emp1.dateOfBirth = "1993/11/20"; emp1.jobTitle = "Project Manager"; emp1.salary = 41093; emp2.name = "Veronika O'Neill"; emp2.address = "789 Yorkminster St."; emp2.dateOfBirth = "1998/01/15"; emp2.jobTitle = "Junior Developer"; emp2.salary = 23476; emp3.name = "Lester Wilcox"; emp3.address = "456 Lioborough Ln."; emp3.dateOfBirth = "1991/05/01"; emp3.jobTitle = "Senior Developer"; emp3.salary = 35950; } }
Lines 7-11 assigns values to the variables in emp1
, lines 13-17 do the same for emp2
, and lines 19-23 do the same for emp3
. Next, let’s create an Employee array:
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); Employee emp3 = new Employee(); emp1.name = "Michael Orwell"; emp1.address = "123 Indigo Rd."; emp1.dateOfBirth = "1993/11/20"; emp1.jobTitle = "Project Manager"; emp1.salary = 41093; emp2.name = "Veronika O'Neill"; emp2.address = "789 Yorkminster St."; emp2.dateOfBirth = "1998/01/15"; emp2.jobTitle = "Junior Developer"; emp2.salary = 23476; emp3.name = "Lester Wilcox"; emp3.address = "456 Lioborough Ln."; emp3.dateOfBirth = "1991/05/01"; emp3.jobTitle = "Senior Developer"; emp3.salary = 35950; Employee[] empArr = new Employee[3]; } }
Line 25 creates an Employee
array called empArr
with room for three Employee
objects. We haven’t put any objects in the array so currently all its elements are null. Arrays are also objects and live on the heap, too, as shown here:

empArr
, at the bottom-left, is a variable that holds a reference to the actual array. Again, the array is filled with null values by default. Knowing what the default value will be for a particular type is quite easy. The default value for ALL reference types is simply null. The default value for primitive types is basically 0 or some equivalent value.
Next, let’s assign the array elements to the Employee
objects:
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); Employee emp3 = new Employee(); emp1.name = "Michael Orwell"; emp1.address = "123 Indigo Rd."; emp1.dateOfBirth = "1993/11/20"; emp1.jobTitle = "Project Manager"; emp1.salary = 41093; emp2.name = "Veronika O'Neill"; emp2.address = "789 Yorkminster St."; emp2.dateOfBirth = "1998/01/15"; emp2.jobTitle = "Junior Developer"; emp2.salary = 23476; emp3.name = "Lester Wilcox"; emp3.address = "456 Lioborough Ln."; emp3.dateOfBirth = "1991/05/01"; emp3.jobTitle = "Senior Developer"; emp3.salary = 35950; Employee[] empArr = new Employee[3]; empArr[0] = emp1; empArr[1] = emp2; empArr[2] = emp3; } }
Line 26 assigns index 0 to emp1
in the array, meaning the reference is copied from emp1
to index 0. Line 27 does the same with emp2
(into index 1), and line 28 does the same with emp3
(into index 2). This is what we end up with:

As you can see, the variable empArr
holds a reference to the array (0x04), which in turn holds references to the Employee
objects (0x01, 0x02, 0x03). This means Java has to resolve two references when going through empArr
(the array itself and then one of the objects). The variables emp1
, emp2
, emp3
still have their references, which means we have two ways to access each object. For example, if we wanted to modify the object at 0x03 we can do it through either emp3
or empArr[2]
, as they both reference that object. Specifically, if we wanted to change jobTitle
to “Accountant”, we can do it either through emp3
:
emp3.jobTitle = "Accountant";
or through empArr
:
empArr[2].jobTitle = "Accountant"
Again, when going through empArr
, Java needs to resolve two references. So, behind the scenes Java looks at the reference in empArr
(0x04), finds the array, looks at the reference in index 2 (0x03), finds the object, and then sets jobTitle
to “Accountant”. The same would be true if using an ArrayList
. This isn’t a problem in terms of performance or anything like that but it’s just something to be aware of when moving objects about.
Using a Foreach Loop on the Array
We can loop through array empArr
like any other, and process each element in turn. The following program prints out each employee’s details:
public class Main { public static void main(String[] args) { Employee emp1 = new Employee(); Employee emp2 = new Employee(); Employee emp3 = new Employee(); emp1.name = "Michael Orwell"; emp1.address = "123 Indigo Rd."; emp1.dateOfBirth = "1993/11/20"; emp1.jobTitle = "Project Manager"; emp1.salary = 41093; emp2.name = "Veronika O'Neill"; emp2.address = "789 Yorkminster St."; emp2.dateOfBirth = "1998/01/15"; emp2.jobTitle = "Junior Developer"; emp2.salary = 23476; emp3.name = "Lester Wilcox"; emp3.address = "456 Lioborough Ln."; emp3.dateOfBirth = "1991/05/01"; emp3.jobTitle = "Senior Developer"; emp3.salary = 35950; Employee[] empArr = new Employee[3]; empArr[0] = emp1; empArr[1] = emp2; empArr[2] = emp3; for (Employee e : empArr) { System.out.printf("The employee %s is a %s and earns £%.2f annually.\n", e.name, e.jobTitle, e.salary); } } }
Line 29 is a foreach loop that prints out the details of every employee. You can read it as “for each Employee
e
in empArr
, do line 30″. In other words, each time the loop repeats, e
will reference the next object in the array. Line 30 uses e
to print out some information about the employee currently referenced by e
i.e. e.name
, e.jobTitle
, and e.salary
. So, on the first iteration, e
references the Employee
object at index 0, and line 30 will print out their details. On the next iteration, e
will contain the Employee
object at index 1, therefore line 30 prints that object’s values. The next iteration, e
will contain the Employee
object at index 2 and line 30 will print that object’s values. This results in the output:
The employee Michael Orwell is a Project Manager and earns £41093.00 annually. The employee Veronika O'Neill is a Junior Developer and earns £23476.00 annually. The employee Lester Wilcox is a Senior Developer and earns £35950.00 annually.
Practical Use of Class Employee
At this point, you may be wondering how Employee
fits into an actual application. Remember the GuestList program back in chapter 9? It allowed you to add and remove guests to and from a list. It used an ArrayList
of String
to store all the names of all the guests. String
s are fine when you only want to store one piece of data, like a name, but if you want to store more data about each guest, such as their address, phone number, the time they arrived, their dietary requirements, etc, then String
is not very practical. When you have an aggregation of data, there is no single built-in type that fits the bill, so you have to roll out your own. This is what we’ve achieved with the Employee
class, and because Employee
is a type, we can create an ArrayList
of Employee
to store any and all Employee
objects in one place. You’ll typically want to use ArrayList
s instead of arrays to store collections of items as they are far more flexible and easier to use. This is how to create an ArrayList
of Employee
:

(1) ArrayList<Employee> employees
declares a variable named employees
, whose type is ArrayList
(of Employee
). The ArrayList
is named “employees” (plural) because it’s for the purpose of holding many Employee
objects. Other reasonable names are “emps”, “employeeList”, “empList”, etc.
(2) new ArrayList<>()
is the instantiation of the ArrayList
object. Notice that the angle-brackets are empty. In older versions of Java you would have to write “Employee” there, like this: new ArrayList<Employee>()
, but newer versions of Java can infer the type from the context so you don’t have to specify that it’s an ArrayList
of Employee
. In other words, Java sees that the variable’s type is ArrayList
of Employee
, and reasonably assumes that we want it to reference an ArrayList
of the same type. Therefore, we can just write new ArrayList<>()
and Java will create an ArrayList
of Employee
implicitly.
Let’s look at a program that uses an ArrayList
of Employee
to store employee data. The program is very basic. It allows the user to do two things: add employees to the list and view employees currently in the list.
import java.util.ArrayList; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); ArrayList<Employee> employees = new ArrayList<>(); while (true) { System.out.println("1 | Add Employee"); System.out.println("2 | Display Employees"); System.out.print("Option: "); int option = sc.nextInt(); sc.nextLine(); switch (option) { case 1: { System.out.println(); System.out.println("- NEW EMPLOYEE -"); Employee emp = new Employee(); System.out.print("Name: "); emp.name = sc.nextLine(); System.out.print("Address: "); emp.address = sc.nextLine(); System.out.print("DoB: "); emp.dateOfBirth = sc.nextLine(); System.out.print("Job Title: "); emp.jobTitle = sc.nextLine(); System.out.print("Salary: "); emp.salary = sc.nextDouble(); employees.add(emp); break; } case 2: { System.out.println(); if (employees.isEmpty()) { System.out.println("No employee data found."); } else { for (Employee emp : employees) { emp.printInfo(); } } break; } } System.out.println(); } } }
Line 6 creates a Scanner
. Remember that Scanner
and Employee
are fundamentally the same in that they are both classes. This means a new Scanner
object gets placed on the heap and variable sc
contains a reference to it. Line 7 creates the ArrayList
of Employee
(also placed on the heap with employees
containing a reference to it). Line 9 is the start of a while
loop that contains the core of the program. First, lines 10-11 display two menu items: “Add Employee” and “Display Employees”. Lines 12-13 obtain the user’s input into option
. Line 14 consumes the \n
that is left behind so it is not wrongly read by future Scanner
calls. Line 16 is a switch statement that has two cases, one for each option.
If the user enters 2, then case 2 displays all the employees in the list. Although, it first checks if the list is empty on line 38 using the ArrayList
‘s isEmpty
method. This method returns true if the ArrayList
is empty, false if it’s not. If it’s empty, line 39 prints “No employee data found.”. If it’s not empty, the else block prints all the employees in the ArrayList
. It does this using a foreach loop.
If the user enters 1, case 1 asks for all the details about the employee to add to the list. First, line 19 prints a header simply to be informative. Line 20 creates a new Employee
object called emp
. Line 21 asks for the employee’s name. Line 22 uses sc.nextLine()
to obtain this information from the user and sets the name
variable in emp
to it. Lines 23-30 do the same thing with the other four variables in emp
. Finally, line 31 adds emp
to employees
. How does line 31 work exactly? Remember that emp
is a local variable that is declared in the switch statement (line 20). (This means it no longer exists when the switch statement ends.) emp
contains a reference the Employee
object that is created on the same line. When line 31 is reached, we can see that emp
is passed to the ArrayList
‘s add
method. Again, it’s the reference inside emp
that gets passed to the method. The add
method puts this reference in the first available slot in the ArrayList
(index 0 since the ArrayList
is empty initially). Therefore, the ArrayList
contains a reference to the Employee
. Here is a sample run of the program:
[Run] 1 | Add Employee 2 | Display Employees Option: 2 No employee data found. 1 | Add Employee 2 | Display Employees Option: 1 - NEW EMPLOYEE - Name: Justine Oakley Address: 49 Calmford Gardens DoB: 12/09/1994 Job Title: Human Resources Salary: 20105 1 | Add Employee 2 | Display Employees Option: 2 Employee: Justine Oakley | Address: 49 Calmford Gardens | DoB: 12/09/1994 | Role: Human Resources | Salary: £20105.00. 1 | Add Employee 2 | Display Employees Option: 1 - NEW EMPLOYEE - Name: Rylan Tempest Address: 2 Lemonzest Orchard DoB: 30/01/2000 Job Title: Sales Representative Salary: 23372 1 | Add Employee 2 | Display Employees Option: 2 Employee: Justine Oakley | Address: 49 Calmford Gardens | DoB: 12/09/1994 | Role: Human Resources | Salary: £20105.00. Employee: Rylan Tempest | Address: 2 Lemonzest Orchard | DoB: 30/01/2000 | Role: Sales Representative | Salary: £23372.00. 1 | Add Employee 2 | Display Employees Option:
As we can see, the user first entered 2. In case 2, because employees
is empty, the if statement on line 36 prints “No employee data found.” Next, the user enters 1. Case 1 creates an Employee
object and fills the object with data inputted by the user. Finally, line 31 adds this new object it to the list. The user enters option 2 again and the foreach loop on line 40 loops through the list and calls every object’s printInfo
method. There’s only one Employee
in the list so only one line of data is printed. Then the user repeats the above steps by adding another employee to the list and then displaying the list again, which outputs both Employee
‘s in the list.
Using the console isn’t the most pleasant way of interacting with a program. When you learn how to create a GUI (graphical user interface), an application of this nature will be much more enjoyable to use (and to program). There’s also the issue that when the application exits, we lose all the Employee
objects and any data they carry. This is because objects are stored in memory, not permanent storage. When you learn about file manipulation and databases, you’ll be able to save and load this data so it is not lost between sessions.