Java 101 Classes and Objects v1.3.1
Java 101 Classes and Objects v1.3.1
Diagrammatic presentation of the classes for objects declared in the short examples
Stopwatch example
UML class diagram notation explained
Stopwatch Class name
- double startTime Visibility modifiers:
- double elapsedTime Fields '-' means private
- boolean isRunning '+' means public
+ Stopwatch() Constructor
+ void start()
Methods Names of static fields
+ void stop() and static methods are
+ double getElapsedTime() underlined.
In the object-oriented programming paradigm (OOP)1, the object is the fundamental element of
abstraction and the core building block for programs. The aim is to organize the program into
smaller parts (objects) that are as easy as possible to understand, test, maintain, and reuse.
Abstraction is a technique for reducing complexity of computer systems to a manageable level.
In class-based OOP languages (Java, C#, ...), we design and define classes for creating objects.
That is, in Java, every object is created from a class.
1. In the Java run-time environment, there is a predefined set of classes like Scanner,
DecimalFormat, String etc. and you can create any number of new classes. The content
of the predefined Java class library is documented in the Java API Specification.
2. Related Java classes can be bundled into packages to make the classes easier to find and
use2, to avoid class name conflicts, and to control access to individual classes.
3. An executable Java program or any set of compiled Java classes3 and related files (images
etc.) can be packaged in a Java Archive File (JAR file) for distribution of the software4.
Encapsulation is a mechanism of wrapping the data and code acting on the data together as a
single unit called object. That is, an object can encapsulate both data (several different data items
in a single object) and behaviours. The behaviours of an object are defined by defining the methods
of the object. Encapsulation supports robustness, testability, maintainability, and reusability of the
code.
1. To promote encapsulation, the data held in an object should be declared with private visibility.
That is, the data held in an object should be manipulated only by that object.
2. The set of methods of an object that have public visibility define the interface between the object
and the rest of the system.
Visibility Instance fields Instance methods
private Enforce encapsulation Provide services to the other methods of the object
public Violate encapsulation Provide services to the rest of the system
Inheritance is about reusability and programming by difference. In Java, you can derive any number
of new classes from an existing class5. In doing this, you can reuse the fields and methods of the
existing class without having to design, write, test, and debug them again. The class, which inherits
the properties of other, is known as subclass (derived class / child class) and the class whose
properties are inherited is known as superclass (base class / parent class).
1
See https://en.wikipedia.org/wiki/Programming_paradigm for more information about programming paradigms.
2
For example, 'import java.util.Scanner' allows the use of the class Scanner from the java.util package.
3
The Java source texts are not included in the JAR file for distribution.
4
Examples of this type of software distribution are JDBC drivers for SQL Server, MariaDB and Sqlite. They are distributed
as single JAR files that you include in your Java projects.
5
If we do not explicitly derive a class from another class, then Java derives the class from a class called Object.
6
See https://en.wikipedia.org/wiki/Polymorphism_(computer_science) for more details. To discuss the details of the
concept of polymorphism is not in the scope of this presentation.
Kari Silpiö © Java 101: Classes and Objects 3(24)
All rights reserved 11.7.2019 v1.3.1
The very basic (optional) items that we write inside of a class body are the following:
Class-level items
Fields - class fields (static), also called class variables
Methods - class methods (static)
Instance-level items
Fields - instance fields (non-static9), also called instance variables
Methods - instance methods (non-static)
Constructors
The code of a very simple standalone Java program can be written in a single class declaration.
7
NB! The term 'class' is ambiguous in software development. In this guide, the term 'class' normally refers to a Java
class declaration.
8
Normally, the source code of each class is saved in a separate text file. The name of the text file must be the same as
the class name. The file name extension must be .java. We consider Java interfaces to be special classes, too.
9
Non-static = defined without the static keyword. By default, fields and methods are non-static.
10
If we leave out the public keyword, then this class is not accessible from outside of the package that it belongs.
Kari Silpiö © Java 101: Classes and Objects 4(24)
All rights reserved 11.7.2019 v1.3.1
A method library class is a class used to store a group of related reusable, static methods. A typical
method library class does not have the main method.
Below is a part of the declaration of the Math class from the Java class library11. In the class
declaration below, there is one class field (PI) and one class method (max).
The public visibility modifier of the class Math is public says that anyone can use this class12.
The modifiers of the field PI are public, static, and final.
A class field is a The public modifier says that anyone can use this field13.
single variable The static modifier says that this field is a class field.
whose existence
The final modifier says that the value of this field cannot change after initialization. That is,
is not dependent
on any object. PI is defined to be a constant. By the Java naming convention, names of constants are written
in all uppercase. A constant can be public because its value cannot be changed14.
The modifiers of the method max are public and static.
The public modifier says that anyone can call this method.
The static modifier says that this method is a class method.
To access a public class field/method from outside of the class declaration you must write the name
of the class and a dot before the field/method name. You can access the public fields and call the
public methods of a method library class from any program. That is, you design, write and test the
code only once and then you can use the code many times – without duplicating the code.
11
The Math class belongs to the java.lang package. For convenience, the Java compiler automatically imports the
java.lang package because the package contains classes that are fundamental to the design of the Java language.
12
The available visibility modifiers for classes are the following:
public (the class is visible to anyone)
no visibility modifier (the class is visible only in the code of the classes of the same package)
13
The available visibility modifiers for fields and methods are the following:
public (visible to anyone)
protected (visible in the code of the classes of the same package and in the code of all subclasses
no visibility modifier (visible in the code of the classes of the same package)
private (visible in the code of the class)
14
Another example of a public constant is the length field of an array. It gets its final value when the array is created.
Kari Silpiö © Java 101: Classes and Objects 5(24)
All rights reserved 11.7.2019 v1.3.1
NB! Java is not a 100 % object-oriented language15. This far, our examples have been primarily
demonstrating procedural programming in Java.
In procedural programming the focus is on breaking down a programming task into a set of
variables, data structures (e.g. arrays), subroutines (e.g. methods) and modules (e.g. method
libraries).
In object-oriented programming (OOP) the focus is on breaking down a programming task into
objects that encapsulate both the data and the methods. That is, in OOP we define and create
objects of our own types.
15
In a pure class-based OOP-language, (1) all classes are classes for objects and (2) all data is presented as objects. In
Java (and C#), in addition to classes for objects, we can create simple program classes and method library classes that
are not used as classes for objects. In addition to objects, Java manages data that is presented by using primitive types
(e.g. int, double, boolean, and char). In addition to imperative programming and object-oriented programming, Java
supports event-driven programming and functional programming (to some extent only).
Kari Silpiö © Java 101: Classes and Objects 6(24)
All rights reserved 11.7.2019 v1.3.1
In object-oriented Java programming, we create classes for objects and instantiate16 these classes.
The aim is to organize the program into such classes/objects that are as easy as possible to
understand, test, maintain, and reuse.
An object has A software object can be used to represent a real object in the problem domain. For example, we
behaviors and can create one object per bank account, each with behaviours (deposit money, withdraw money,
attributes. get balance etc.) and attributes (account number, balance etc.) that we need to represent. The
The values of the
values of the object's attributes define its state. For example, a part of a bank account's state is its
object's attributes
define its state. current balance.
Only the object
itself should be In Java, we create objects by instantiating classes for objects.
allowed to directly The declaration of a class for objects acts as a blueprint of an object. It specifies what kind of
change its state.
behaviours and attributes each object created from the class will have.
In Java, the public methods of an object define its behaviours.
In Java, the fields of an object define its attributes.
We can create any number of objects from one class.
In the code of a typical Java class for objects, there are the following instance-level items:
Fields instance fields (non-static), also called instance variables
Methods instance methods (non-static)
Constructors also called object constructors.
Stopwatch example
Let's consider a concrete example that demonstrates and clarifies the idea of software objects and
the key concepts mentioned this far.
Suppose that we want to measure elapsed time in a Java program. In the real world, we can use a
stopwatch for precise timing. A very simple stopwatch has a digital display that can be started and
stopped at will for exact timing. Let's apply the object-oriented approach and determine the
behaviours and attributes that we need to represent in a Stopwatch object.
Next, we have to consider how we can do the elapsed time calculation in Java. The class method
currentTimeMillis of the System class returns the current time in milliseconds. If we use the
currentTimeMillis method to get the start time and end time, then we can calculate the elapsed
time in seconds as follows: (end time - start time) / 1000. If we save the start time and elapsed time
to variables of type double, then we do not lose the fractions of a second in our calculations. To
keep the example short and simple, we do not consider converting the result to hours, minutes,
and seconds.
16
To instantiate = to represent (an abstraction) by a concrete instance
To Instantiate a class = to create an instance of the class = to create an object from the class
Kari Silpiö © Java 101: Classes and Objects 7(24)
All rights reserved 11.7.2019 v1.3.1
The new operator To create a new object from a class, we use the new operator, which returns a reference to the
always creates new object. We can create any number of Stopwatch objects.
exactly one Stopwatch timer_1 = new Stopwatch();
object.
Stopwatch timer_2 = new Stopwatch();
NB! An object itself is never stored in a variable. Instead, a variable can store a reference to an
object. If a variable can hold a reference to an object, we can call the variable reference variable17.
Stopwatch object
A reference is a reference variable
'link' (memory startTime: 0.0
timer_1:
address) that
reference elapsedTime: 0.0
indicates where
an object is
stored. Stopwatch object
reference variable
startTime: 0.0
timer_2:
reference elapsedTime: 0.0
A constructor is The system automatically invokes an object constructor when an object is being created from the
a special method class. An object constructor is a special type of method that has the same name as the class and
that initializes a
no return type. The constructor of the class should initialize the fields of the newly created object.
newly created
object If we do not explicitly define a constructor for our class, then Java creates behind the scenes a
default constructor that takes no parameters and performs no special operations.
17
A reference variable is like an entry in an address book. The entry tells you where to find what you are looking for.
Kari Silpiö © Java 101: Classes and Objects 8(24)
All rights reserved 11.7.2019 v1.3.1
In the first example, we use the Stopwatch class and measure how long it takes to iterate a loop
one billion times.
MeasureElapsedTime.java (A simple program class)
public class MeasureElapsedTime {
public static void main(String[] args) {
Stopwatch timer = new Stopwatch();
timer.start();
for (int i = 1; i <= 1_000_000_000; i++) {
}
timer.stop();
System.out.println("Elapsed time: " + timer.getElapsedTime() + " s");
}
}
In the next example, we measure how long it takes to join 100000 short strings with the '+' operator
and how long it takes to join 100000 short strings with a StringBuilder. For this purpose, we create
two separate objects from the Stopwatch class.
timer_1.start();
for (int i = 1; i <= 100_000; i++) {
firstText = firstText + "Hello! ";
}
timer_1.stop();
timer_2.start();
for (int i = 1; i <= 100_000; i++) {
secondText.append("Hello! ");
}
timer_2.stop();
NB! Normally, we distribute Java classes in their machine-readable bytecode (.class files). That is,
the source code of the Stopwatch class (Stopwatch.java) should exist only once (it should not be
duplicated). After we have compiled and thoroughly tested the code, we can distribute the class
by copying the file Stopwatch.class to other Java projects. If we have to make any changes to the
code later, then we should modify the original source code, test the new version thoroughly and
then distribute the new version of the file Stopwatch.class.
Kari Silpiö © Java 101: Classes and Objects 9(24)
All rights reserved 11.7.2019 v1.3.1
Any field declared without the static modifier is an instance field. Instance fields are associated
with instances of the class (objects). Thus, every Course object we create has its own instance fields
called code and name. To keep the first version of the class as simple as it can be, we do not include
any methods or constructors18 yet and we define the fields with public visibility19.
We can instantiate the class (create an object from the class) with the new operator and access
the object's public fields as follows:
Course firstCourse = new Course();
Course currentCourse = new Course();
firstCourse.code = "swd1tf001";
firstCourse.name = "Introduction to software engineering";
currentCourse.code = "swd4tf014";
currentCourse.name = "Programming 1";
18
(1) Since we do not explicitly define a constructor for our Course class, the Java compiler will create behind the scenes
a default constructor that takes no parameters and performs no special operations (in addition to implicitly calling its
direct superclass's parameterless constructor). (2) The Course class's direct superclass is the Object class since we do
not explicitly derive the Course class from any other class with the extends keyword. This keyword,
superclasses/subclasses, and inheritance will be discussed later in detail (in another guide).
19
Normally, all instance fields should be private. In this example, we will later change them to private.
Kari Silpiö © Java 101: Classes and Objects 10(24)
All rights reserved 11.7.2019 v1.3.1
Version 2: Class for objects, constructor and toString method added, no encapsulation
Let's improve the Course class as follows:
Simplify the initialization of the object's instance fields by defining a constructor that accepts
two input parameters and copies their values to the instance fields of the object.
Simplify the printing of the object's data (for testing purposes only) by defining the toString
method that returns a representation of the object's data as a String.
Any method declared without the static keyword is an instance method. An instance method
operates always on an instance of a class (an object). That is, you cannot call an instance method
without a reference to an object20.
Now we can instantiate the Course class with parameters and print course data as below.
Course firstCourse =
new Course("swd1tf001", "Introduction to software engineering");
Course currentCourse = new Course("swd4tf014", "Programming 1");
System.out.println(firstCourse);
System.out.println(currentCourse);
20
For example, Scanner is a class for objects. If you try to execute the call Scanner.nextline(), you get the following
error message: "Cannot make a static reference to the non-static method nextLine() from the type Scanner".
21
When we want to get a Course object's data to be printed, we can simply pass a reference to the Course object to the
println method as follows:
System.out.println(firstCourse);
One of the overloaded versions of the println method accepts a reference to an object as its input parameter. The
method automatically calls the toString method of the object to get a String to be printed. Of course, we can
alternatively call the toString method explicitly as below.
System.out.println(firstCourse.toString());
Kari Silpiö © Java 101: Classes and Objects 11(24)
All rights reserved 11.7.2019 v1.3.1
Now the code that is external to the Course class declaration cannot directly retrieve or change the
values of the instance fields of a Course object. Instead, the code below uses a setter (setName) to
change a field value and a getter (getName) to retrieve a field value.
Course firstCourse = new Course("swd1tf001", "Introduction to SWE");
Course currentCourse = new Course("swd4tf014", "Programming 1");
22
In encapsulation, the data held by an object (in its instance fields) is hidden from other objects of other classes, and
can be accessed only through the methods of the object itself. The public methods provide an interface to the object.
Kari Silpiö © Java 101: Classes and Objects 12(24)
All rights reserved 11.7.2019 v1.3.1
Java handles values of the primitive types (int, double, char, boolean etc.) directly. When you
assign a value to a variable of a primitive type, the value is saved directly to the variable.
variable
int year = 2019; year: 2019
Classes and array types are known as reference types because Java handles objects and arrays by
reference only. That is, an object itself is never stored in a variable. When you assign an object or
array to a variable, you are actually setting the variable to hold a reference23 to that object or
array. If a variable contains a reference to an object, we can call the variable reference variable.
Suppose that the Country class has instance fields called name (String) and population (int), a
parametric constructor, and getters and setters for the instance fields.
Country myCountry = new Country("Finland", 5501043);
An object exists as long as there exists a reference to the object. You cannot destroy an object
explicitly. Instead, the system performs automatic garbage collection. That is, the system
automatically erases any unreferenced objects (called orphans) after some delay.
The special value null means an absence of a reference ("no reference"). Null can be assigned to
any reference variable and it is the default value for all reference types.
Country myCountry; // The initial value of myCountry is null
System.out.println(myCountry.getName()); // ERROR: "Null Pointer Exception"
myCountry = new Country("Finland", 5542141);
System.out.println(myCountry.getName()); // Prints "Finland"
myCountry = null;
System.out.println(myCountry.getName()); // ERROR: "Null Pointer Exception"
NB! Only a reference to an object is copied when you copy the content of one reference variable
to another reference variable. This is called reference copy. An assignment operation itself does
never create a new object. A new object is created with the new operator only24. The example
below demonstrates how the reference copy works.
Country bigCountry = new Country("India", 1388232693);
Country smallCountry = new Country("Tuvalu", 9863);
Question: Can you figure out why the last print operation prints 9864 as the population?
Can you draw the variables and objects on paper based on the code above?
23
Behind the scenes, a reference is the memory address at which the object or array is stored.
24
There are two exceptions to the rule. The system automatically creates new array objects from array initializers (e.g.
{1, 2, 3}) and provides a reference to a String object per each String literal (e.g. "Hello!").
Kari Silpiö © Java 101: Classes and Objects 13(24)
All rights reserved 11.7.2019 v1.3.1
Arrays can store references to objects in addition to primitive values25. When you assign an object
to an array element, you are actually setting the array element to hold a reference to that object.
The easiest way of creating an array of object references is to use an array initializer. In the example
below, we create three Course objects26 and assign their references to the array.
Course[] courseArray = {
new Course("swd4tf014", "Programming 1"),
new Course("swd4tf003", "Data Management and Databases"),
new Course("swd8tf040", "Database Developer")};
Creating an Another way to create an array of object references is with the new operator. In this case, Java
array with initializes the array elements with nulls.
the new
operator Please notice that the new operator always creates exactly one object. That is, creating an array of
does not Courses does not create Course objects themselves. The Course objects must be created separately
create the and their references should be assigned to the array elements.
objects.
Instead, each Course[] courseArray = new Course[3];
element of
courseArray[0] = new Course("swd4tf014", "Programming 1");
the array is
courseArray[1] = new Course("swd4tf003", "Data Management and Databases");
initialized to
courseArray[2] = new Course("swd8tf040", "Database Developer");
null.
Let's print names of all courses. When we iterate the array, we can copy each object reference to
a separate reference variable and then use the variable to access the object.
for (int i = 0; i < courseArray.length; i++) {
Course courseObject = courseArray[i];
System.out.println(courseObject.getName());
}
Question: Can you figure out what the code below prints?
Course courseObject = courseArray[0];
courseObject.setName("Hello!");
System.out.println(courseArray[0].getName());
25
Strictly speaking, instead of "arrays of objects" we have arrays of object references in Java.
26
We reuse the Course class declaration from a previous example.
Kari Silpiö © Java 101: Classes and Objects 14(24)
All rights reserved 11.7.2019 v1.3.1
Normally, arrays are of a fixed length. That is, when you create an array you must know in advance
how many items needs to be saved in the array. In Java, an ArrayList is a dynamic data structure
that can grow and shrink27. Typically, you use an ArrayList instead of an array when you do not
know the number of items in advance.
First, we have to import the java.util.ArrayList class. Then we can create a new ArrayList as
follows:
ArrayList<Course> courseList = new ArrayList<Course>();
In the code above, <Course> specifies that only references to Course objects are allowed in this
ArrayList. Once we have created an ArrayList object, we can append elements to the end of the
list with the add method:
courseList.add(new Course("swd4tf014", "Programming 1"));
In Java, lists do not allow the normal array indexing operation. The neatest way of accessing the
items in a List is to use the enhanced for loop. The code below prints names all courses.
for (Course courseObject : courseList) {
System.out.println(courseObject.getName());
}
If you use the normal for loop to iterate an ArrayList, then you can get the element value at the
specified index by calling the get method of the ArrayList object.
for (int i = 0; i < courseList.size(); i++) {
System.out.println(courseList.get(i));
}
The comprehensive example below shows the basic use of the ArrayList.
CourseListExample.java
import java.util.ArrayList;
public class CourseListExample {
public static void main(String[] args) {
ArrayList<Course> courseList = new ArrayList<Course>();
courseList.add(new Course("swd4tf014", "Programming 1"));
courseList.add(new Course("swd4tf003", "Data Management and Databases"));
courseList.add(new Course("swd8tf040", "Database Developer"));
for (Course courseObject : courseList) {
System.out.println(courseObject.getName());
}
}
}
The ArrayList class provides a lot of functionality over its contents. It provides methods to insert a
new item at the specified position in the list, remove an item, empty the list, sort the list etc.
27
An ArrayList in Java is like an 'array' in JavaScript.
Kari Silpiö © Java 101: Classes and Objects 15(24)
All rights reserved 11.7.2019 v1.3.1
8 Wrapper classes
Java does not allow us to directly save primitive values in an ArrayList. Instead, we can take
advantage of the corresponding wrapper class (Integer) as below.
ArrayList<Integer> integerList = new ArrayList<Integer>();
int sum = 0;
integerList.add(5); // Autoboxing; int → Integer
integerList.add(2); // Autoboxing; int → Integer
integerList.add(3); // Autoboxing; int → Integer
Primitive wrapper classes also contain useful static methods and constants. To invoke a static
method of a class, we do not need to create an object from that class. For example, Strings can be
converted to numeric values as below.
int intValue = Integer.parseInt("123");
long longValue = Long.parseLong("999_999_999_999");
double doubleValue = Double.parseDouble("7.25");
Strings can be converted to wrapper objects of other types as follows:
Integer x = Integer.valueOf("123");
Long y = Long.valueOf("999_999_999_999");
Double z = Double.valueOf("7.25");
A string representation of an integer as a binary number and the opposite can be created as below.
All numeric wrapper classes provide this type of operations for binary, octal and hex values.
String str = Integer.toBinaryString(5); // returns "101"
int intValue = Integer.parseInt("1110", 2); // returns 14
Each numeric wrapper class has also a set of constants, like MAX_VALUE and MIN_VALUE etc.
int largestIntValue = Integer.MAX_VALUE;
int smallestIntValue = Integer.MIN_VALUE;
28
The primitive wrapper classes are Byte, Short, Integer, Long, Float, Double, Character, Boolean and Void.
These are specially treated classes in Java. The autoboxing mechanism is applicable to these classes only.
Kari Silpiö © Java 101: Classes and Objects 16(24)
All rights reserved 11.7.2019 v1.3.1
Suppose that we want to create error messages with timestamps and keep count of the number
of error messages that we have created. For this purpose, we declare a class for objects
(ErrorMessage) and include the following in the class declaration:
A private instance field (message) for holding an error message text in an object
A public instance method (getMessage) for retrieving an error message from the object
A public constructor for initializing the error message field of an object
A private class field29 (messageCount) for keeping the count of ErrorMessage objects
A public class method (getMessageCount)for retrieving the count of ErrorMessage objects.
The complete Java class declaration is as below.
ErrorMessage.java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
Now we can create error messages, print the messages, and print the count of messages.
ErrorMessage firstMessage = new ErrorMessage("Driving too fast.");
ErrorMessage secondMessage = new ErrorMessage("Engine overheated.");
System.out.println(ErrorMessage.getMessageCount() + " messages created");
System.out.println(firstMessage);
System.out.println(secondMessage);
29
A class field is associated with the class in which it is defined, rather than with an instance of the class. A class field is
a single variable that is shared among all objects of a class. That is, a class field is visible in all methods of the class.
NB! The code in instance methods of a class can access also the class fields and invoke the class methods of the class,
but the code in a class method of a class cannot access the instance fields and invoke instance methods of the class.
30
Of course, timestamps will be different when the code is executed later again.
Kari Silpiö © Java 101: Classes and Objects 17(24)
All rights reserved 11.7.2019 v1.3.1
When a sort algorithm manipulates a given set of values, it must be able to compare two values.
In the case of values of primitive types, the comparison of values is straightforward: "if (a > b)".
But, operators like '<' and '>' do not work with objects in Java.
Let's reuse the Course class from a previous example and try to sort an array of Course object
references.
Course[] courseArray = {new Course("C2", "C#"), new Course("C1", "Java")};
Arrays.sort(courseArray);
When we run the code, the program crashes and the system prints the following message:
"java.lang.ClassCastException: Course cannot be cast to java.lang.Comparable". The sort operation
fails because Java does not know how to compare Course objects.
But, ... Strings are objects and we can sort an array of String object references as below.
String[] stringArray = {"Frank", "Katherine", "Aaron"};
Arrays.sort(stringArray);
Strings cannot be compared with operators. Instead, we must do string comparison31 as follows:
if (firstWord.compareTo(secondWord) < 0) {
System.out.println(firstWord + ", " + secondWord);
} else {
System.out.println(secondWord + ", " + firstWord);
}
The String class has a method called compareTo that compares this String to another String. The
method returns a negative integer if this String is before the other String in the ordering, a positive
integer if this String is after the other String in the ordering, and zero if the strings are equal in the
ordering. That is, a String object itself knows how to compare itself to another String object.
31
or compare Strings in a case-insensitive way as follows: firstWord.compareToIgnoreCase(secondWord)
Kari Silpiö © Java 101: Classes and Objects 18(24)
All rights reserved 11.7.2019 v1.3.1
10.2 Sorting an array with the compareTo method (using the Comparable interface)
Let's try to solve the problem of comparing Course objects by adding a method called compareTo
to the Course class. The method's return values should follow the same logic as the return values
of the compareTo method of the String class. We want to order courses by course code. The code
of the compareTo method of the Course class is the following:
public int compareTo(Course anotherCourse) {
return code.compareTo(anotherCourse.code);
}
In the method, we compare the course code held in this Course object to the course code held in
another Course object. But, ... when we run the program, the sort operation still fails and the
program crashes with the same error message as before. This is because we haven't promised Java
that our compareTo method can compare Course objects.
The standard way to define a comparison method for a class and promise Java that it can compare
objects created from the class is to make the class to implement the Comparable interface32 as
below.
32
See https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html. The intention of the Comparable
interface is to provide a common mechanism for comparing one object to another. In Java, an interface in is an
abstract type that is used to specify a behavior that certain classes must implement.
The Comparable interface requires the class to implement a compareTo method that takes a single parameter of
the type mentioned within the angle brackets.
The compareTo method should return a negative integer, zero, or a positive integer as this object is less than, equal
to, or greater than the other object.
The Arrays.sort method approves to sort references to such objects that are created from a class that implements
the Comparable interface. This guarantees that the objects have the compareTo method.
Kari Silpiö © Java 101: Classes and Objects 19(24)
All rights reserved 11.7.2019 v1.3.1
10.3 Sorting an array with a Comparator object (using the Comparator interface)
If we (1) want to have more than one option for the sorting order or (2) we do not want to add the
compareTo method to a class declaration, then we can create a separate custom class that
implements the Comparator interface and a method called compare that can compare two objects
of the desired type. From this class we can create a 'comparator object' and pass it to a sort method
(such as Arrays.sort or Collections.sort) to allow precise control over the sort order.
CourseNameComparator.java
import java.util.Comparator;
public class CourseNameComparator implements Comparator<Course>
{
public int compare(Course a, Course b) {
return a.getName().compareTo(b.getName());
}
}
33
To oversimplify: Based on the lambda expression, Java creates an anonymous class that contains the compare method.
Java creates the body of the compare method based on the body of the lambda expression. When we run the program,
Java creates an object from the anonymous class on demand and calls its compare method when needed.
In the real life, the Java compiler does some clever optimisation and delegates the construction of object
representations of lambda expressions to runtime. Usually, Java does not create an anonymous class behind the scenes.
Kari Silpiö © Java 101: Classes and Objects 20(24)
All rights reserved 11.7.2019 v1.3.1
In the example below, we sort an array in descending order (z-a) with a lambda expression.
String[] nameArray = {"John", "Ann", "Zeke"};
If there is a single parameter only, then we can also omit the parentheses. The lambda expression
below evaluates to true if the parameter value is an even number. Otherwise, it evaluates to false.
x -> x % 2 == 0
A stream represents a sequence of elements and supports different kinds of operations upon those
elements. Intermediate operations (e.g. filter(), distinct(), sorted()) return a new stream
so we can chain multiple intermediate operations. Terminal operations are either void (e.g.
forEach()) or return a non-stream result (e.g. toArray()).
Arrays.stream() returns a stream with the specified array as its source.
filter() returns a stream consisting of the elements of this stream that pass the given test.
sorted() returns a stream consisting of the elements of this stream in ascending order.
distinct() returns a stream consisting of the distinct elements of this stream.
forEach() performs the given action for each element of this stream.
34
See https://en.wikipedia.org/wiki/Functional_programming. To discuss the details of functional programming is not
in the scope of this presentation. The given example of functional programming in Java is included here just to give you
an idea where lambda expressions can be used. However, please notice how the declarative programming approach
simplifies the code a lot in the example. Note: Lambdas are not OOP-specific constructs..
Kari Silpiö © Java 101: Classes and Objects 21(24)
All rights reserved 11.7.2019 v1.3.1
In the example below, the Course class must implement the Comparable interface and the
compareTo method of the Course class must compare courses in the desired way.
Collections.sort(courseList);
In the example below, we use a lambda expression to compare course names. The Course class is
not required to implement the Comparable interface and have the compareTo method.
// Sort the list in ascending order by course name
Collections.sort(courseList, (a, b) -> a.getName().compareTo(b.getName()));
for (Course courseObject : courseList) {
System.out.println(courseObject);
}
35
See https://en.wikipedia.org/wiki/Sorting_algorithm
36
See https://en.wikipedia.org/wiki/Insertion_sort. Insertion sort is relatively efficient for small arrays. The algorithm
itself is pretty easy to understand (see the animation). The more efficient data sorting algorithms (e.g. Quick sort) use a
more advanced programming technique called recursion that is out of scope of this presentation.
Kari Silpiö © Java 101: Classes and Objects 23(24)
All rights reserved 11.7.2019 v1.3.1
A data structure, such as an array or ArrayList can be understood independently of the element
type it manipulates. A generic class (parameterized class) can be used to describe the concept of
an ArrayList in type-independent manner.
In a declaration of a generic class, the class name is followed by a comma-separated list of type
parameters in angle brackets. Within the class declaration, we can use these type parameters
almost anywhere where we can use a class name. However, we cannot use a plain type parameter
to create a new object or allocate memory for an array (e.g. new T() and new T[5] are illegal).
When we create an object from a generic class, we write a class name in angle brackets after the
name of the generic class as follows: ArrayList<Course> courseList = new ArrayList<Course>();
In the example below, the type parameter E after the class name represents the element type37 of
the ArrayList2. In the body of the class, we use the type parameter E instead of any class type
whenever we should mention the type of an element of the ArrayList2.
37
To oversimplify: A type parameter acts as a 'placeholder' for a name of an actual concrete class.
The Java naming convention for type parameters is the following: E for "element", T for "type", and V for "value".
Kari Silpiö © Java 101: Classes and Objects 24(24)
All rights reserved 11.7.2019 v1.3.1
List of key concepts. List of Java keywords and Java API classes used in the examples
Term Comments and wrap-up
autoboxing - automatic conversion between a primitive value and corresponding wrapper object
class - program class | method library class | class for objects
class for objects - the Java construct to implement a user-defined data type, providing a template to
create and manipulate objects
class members - fields, methods, constructors etc.
field - a variable defined outside of all methods of the class
class field - a single class-level static variable that is shared among all objects of the class
instance field - an object-level variable, each object has a separate copy of the field
class method - a class-level static method, requires no object to be created
instance method - an object-level method, invoked with respect to a particular object
constructor - object constructor, executed automatically when an object is created
getter - a public accessor method that returns the value of a field (or a derived value)
setter - a public mutator method that changes the value of a field
toString - a public method that returns the string representation of the object
constant - a static final variable, it cannot be changed once initialized
constructor overloading - a class can have several constructors with different parameter lists
encapsulation - protecting the state of objects with private fields and public methods
enhanced for loop - e.g. for (Course course : courseList) { System.out.println(course); }
garbage collection - the automatic background process of deleting unreferenced objects (orphans)
interface - specifies a set of methods a class must have if it implements the interface
lambda expression - In Java, lambdas can be passed and returned, as they were objects. They can
simplify code by replacing single-method objects. E.g. (x, y) -> x - y
null - null represents a reference to no object.
object - an instance of a Java class, can have behaviors (methods) and attributes (fields)
object-oriented programming (OOP)
primitive data type - a non-class data type; int, double, char, boolean etc.
reference - object reference, acts as a link to an object
reference copy - only a reference (a link) is copied, no object is copied
reference type - classes, arrays, interfaces; value of reference type is a reference to an object
reference variable - object reference variable; a variable that can hold a reference to an object
visibility modifier - access modifier; public, private etc.
wrapper class - A primitive wrapper class allows a primitive value to be managed as an object.
Special Java API classes and their methods used in the examples
Class Method Class Method
ArrayList add Integer parseInt
get toBinaryString
size valueOf
Arrays sort Long parseLong
Collections sort valueof
LocalDateTime now Double parseDouble
LocalDateTimeFormatter format valueOf
ofPattern
Interface Method
Comparable compareTo
Comparator compare
Please make sure that you understand each concept and keyword mentioned above.