Module 6, Part 1: Simple Classes and Objects#
In this Module we study the fundamentals of Java classes and objects.
When designing and writing programs, it is often necessary to model entities that can be described as groups of values, where each value has its its own (possibly distinct) type. In Java, we can represent such entities using classes and objects — and in this section we explore their basic concepts:
How to define a simple class and create objects of that class;
What is a constructor and how it initialises a new object of a class;
How to define (non-static) methods in a class, and how to call such methods on objects of that class.
Note
In Java, classes are a very powerful and ubiquitous building block for programs. In this section we explore a “simple” usage of classes, that roughly correspond to what other programming languages would call a structure or record. Later in the course we will study more advanced notions and usages of classes.
Defining a Simple Class and Creating Objects#
Suppose we want to write a program that handles items in a shop, and each item must contain information about:
the item’s name (which we can model as value of type
String
)the item’s net price (which we can model as a value of type
double
)the item’s VAT (which we can model as a value of type
double
)the item’s availability in the shop (which we can model as a value of type
int
)
In principle we could try representing and handling the information above by defining a bunch of variables and arrays (of different types) throughout our program. However, to make our program more readable, understandable, and easier to maintain, it makes much more sense to define a new type describing a shop item, so that every value of that type represents a complete shop item with all the required information.
To achieve this, in Java we can define a class describing a shop item: let
us call such a class e.g. ShopItem
. The class definition, in turn, can
specify zero or more fields to represent the shop item’s information (i.e.
the item’s name, net price, VAT, and availability).
After our new class ShopItem
is defined, we can write programs that create and
use objects of the class ShopItem
(such objects are also called
instances of the class shopItem
). Intuitively, a class works like a
blueprint for creating objects: see Fig. 18 below.
More precisely:
the class
ShopItem
specifies the names and types of fields (e.g., the name of a shop item is aString
, the VAT of a shop item is anint
eger…), andeach object of the class
ShopItem
actually “contains” a value for each one of those fields.
The ShopItem
type is a structured data type, in the sense that a value
(i.e., an object) of the type ShopItem
“contains” other values (one per
field).
This is shown in detail in Example 37 below.
(A simple class representing a shop item)
The following class definition describes a shop item, with four fields (name
,
netPrice
, vat
, and availability
). Note that, for each field, we specify
the type followed by the field name: e.g. double vat
means that the field
called vat
has type double
. (Try to copy&paste the following class
definition on the Java shell!)
class ShopItem {
String name;
double netPrice;
double vat;
int availability;
}
If you copy&paste the code above on the Java shell, you will see the output:
| created class ShopItem
This means that Java is now aware of the definition of the new class ShopItem
,
and we can define new objects belonging to that class. The intuition is that the
definition of the class ShopItem
works like a blueprint for creating new
objects (or as a “cookie cutter” for creating new “cookies,” see
Fig. 18 above): every object created as an instance
of ShopItem
will contain a field called name
(with a value of type
String
), a field called netPrice
(with a value of type double
), etc.
For instance, we can try:
jshell> var item1 = new ShopItem();
item1 ==> ShopItem@52cc8049
| created variable item1 : ShopItem
This way, we have created a new object of the class ShopItem
(using new
),
and that object has been used to declare the variable item1
: therefore, the
variable item1
has type ShopItem
, and we can use the variable item1
to
access the newly-created object. (The line item1 ==> ShopItem@52cc8049
may
be slightly different on your computer)
The fields of the newly-created object have a default value, that we can see
by printing the fields’ values on screen. To access the fields of the object
associated to the variable item1
, we simply write item1
followed by a dot
.
and the field name. For example:
jshell> System.out.println("Name of 'item1': " + item1.name)
Name of 'item1': null
Note
Here, null
means that no string has been specified for the field name
of the
newly-created object. We will discuss null
values in (much) more detail later
in the course.
jshell> System.out.println("Net price of 'item1': " + item1.netPrice)
Net price of 'item1': 0.0
jshell> System.out.println("VAT of 'item1': " + item1.vat)
VAT of 'item1': 0.0
jshell> System.out.println("Availability of 'item1': " + item1.availability)
Availability of 'item1': 0
We can change the values to the object fields by using assignments. For instance: (copy&paste the following code on the Java shell)
item1.name = "Freesbee";
item1.netPrice = 115;
item1.vat = 25 / 100.0;
item1.availability = 42;
And now, if we print the object fields again, we get:
jshell> System.out.println("Name of 'item1': " + item1.name)
Name of 'item1': Freesbee
jshell> System.out.println("Net price of 'item1': " + item1.netPrice)
Net price of 'item1': 115
jshell> System.out.println("VAT of 'item1': " + item1.vat)
VAT of 'item1': 0.25
jshell> System.out.println("Availability of 'item1': " + item1.availability)
Availability of 'item1': 42
Of course, we can create new objects of the class ShopItem
, and each object
will have its own fields with their own value, which we can read and modify.
For example:
var item2 = new ShopItem();
item2.name = "Newspaper Politiken";
item2.netPrice = 25;
item2.vat = 0.0;
item2.availability = 12;
(Try printing the values of the fields of item2
on screen, and observe that
they are different from the values in the fields of item1
.)
Constructors#
Example 37 shows how to create a new object of our new class (in
this case, ShopItem
) — but it also shows the fields of newly-created objects
contain default values that we may need to change afterwards, by assigning the
desired values. It would be more convenient to directly create objects whose
fields have the desired values. To do that, we can define one or more
constructors, which take some arguments and use them to initialise the
fields of the new object as we need. This is shown in
Example 38 below.
(Defining a constructor for the shop item objects)
Important
If you have tried Example 37 on the Java shell, you will need to
reset the Java shell before trying the code snippets below. To reset the
Java shell, write /reset
and press ⏎
.
Consider the following class definition for ShopItem
— which is based on the
definition in Example 37, plus some additional lines (highlighted):
1class ShopItem {
2 String name;
3 double netPrice;
4 double vat;
5 int availability;
6
7 ShopItem(String name, double netPrice, double vat, int avail) {
8 this.name = name;
9 this.netPrice = netPrice;
10 this.vat = vat;
11 this.availability = avail;
12 }
13}
Lines 7–12 define a constructor that takes 4 arguments (name
of type
String
, netPrice
of type double
, etc.).
Intuitively, the constructor works a bit like a static method, i.e. it is a sub-program that can be called by passing the required arguments. However, the constructor has two key differences:
the constructor above is called when we create a new object of the class
ShopItem
(usingnew
) with the required 4 arguments; andinside the constructor body, the newly-crated object can be accessed through a special predefined variable called
this
(which is not available inside a “normal” static method).
By defining the class ShopItem
with the constructor above, we can create new
objects of the class ShopItem
in a more convenient way, as follows:
jshell> var item3 = new ShopItem("Plant pot", 61.5, 0.25, 23)
item3 ==> ShopItem@52cc8049
| created variable item3 : ShopItem
When new ShopItem("Plant pot", 61.5, 0.25, 23)
is executed, the ShopItem
constructor above is called with the specified actual arguments, and the
following happens:
in the body of the constructor, the special variable
this
automatically refers to the newly-created object;on line 8 of the constructor, the field
name
ofthis
object (i.e.this.name
) is assigned the actual value of the constructor argumentname
(which is"Plant pot"
);on line 9 of the constructor, the field
netPrice
ofthis
object (i.e.this.netPrice
) is assigned the actual value of the constructor argumentnetPrice
(which is 61.5);lines 10 and 11 of the constructor are similar: the fields
this.vat
andthis.availability
are assigned the actual values of the corresponding constructor arguments (which are 0.25 and 23, respectively).
Now, if we print the values of the fields of item3
, we can see that they have
the values specified when we created the object using new
(which in turn
called the constructor):
jshell> System.out.println("Name of 'item3': " + item3.name)
Name of 'item3': Plant pot
jshell> System.out.println("Net price of 'item3': " + item3.netPrice)
Net price of 'item3': 61.5
jshell> System.out.println("VAT of 'item3': " + item3.vat)
VAT of 'item3': 0.25
jshell> System.out.println("Availability of 'item3': " + item3.availability)
Availability of 'item3': 23
ShopItem
class)
Let us consider this task: write a program that creates two shop items, prints them on screen, then increases their net price by 10%, and prints the items on screen again.
The program below is a possible solution to this task. Notice that it uses the
ShopItem
class introduced in Example 38 — but in
order to avoid code duplication, it enriches the class ShopItem
by defining
two static methods
(highlighted) with useful functionality:
description(item)
creates and returns aString
that shows the values of the various fields of the givenitem
;inflateNetPrice(item, percentage)
modifiesitem
by increasing its net price by the given percentage. NOTE: the return type of this static method isvoid
, which means that callinginflateNetPrice(item, ...)
does not produce any result value: the only purpose of that static method is to modify the givenitem
. As a consequence, we cannot callinflateNetPrice(...)
from inside an expression (if you try, you will get a type error, due to the return typevoid
). Correspondingly, the body of the methodinflateNetPrice(...)
does not return any value.
1class ShopPrices {
2 public static void main(String[] args) {
3 var item1 = new ShopItem("Cellphone", 2500, 0.25, 8);
4 var item2 = new ShopItem("Chocolate bar", 25, 0.25, 37);
5
6 System.out.println("Item 1: " + ShopItem.description(item1));
7 System.out.println("Item 2: " + ShopItem.description(item2));
8
9 System.out.print("Applying 10% inflation to net prices... ");
10 ShopItem.inflateNetPrice(item1, 10);
11 ShopItem.inflateNetPrice(item2, 10);
12 System.out.println("Done.");
13
14 System.out.println("Item 1: " + ShopItem.description(item1));
15 System.out.println("Item 2: " + ShopItem.description(item2));
16 }
17}
18
19class ShopItem {
20 String name;
21 double netPrice;
22 double vat;
23 int availability;
24
25 ShopItem(String name, double netPrice, double vat, int avail) {
26 this.name = name;
27 this.netPrice = netPrice;
28 this.vat = vat;
29 this.availability = avail;
30 }
31
32 static String description(ShopItem item) {
33 var str = item.name + " (price: " + item.netPrice + " DKK + "
34 + (item.vat * 100) + "% VAT; "
35 + "availability: " + item.availability + ")";
36 return str;
37 }
38
39 static void inflateNetPrice(ShopItem item, double percentage) {
40 item.netPrice = item.netPrice + (item.netPrice * (percentage / 100.0));
41
42 // This "return" is optional, since this method returns nothing (void)
43 return;
44 }
45}
If you copy&paste the program above in a Java file (called e.g.
ShopPrices.java
) and execute it, you will see the output:
Item 1: Cellphone (price: 2500.0 DKK + 25.0% VAT; availability: 8)
Item 2: Chocolate bar (price: 25.0 DKK + 25.0% VAT; availability: 37)
Applying 10% inflation to net prices... Done.
Item 1: Cellphone (price: 2750.0 DKK + 25.0% VAT; availability: 8)
Item 2: Chocolate bar (price: 27.5 DKK + 25.0% VAT; availability: 37)
(Passing objects as arguments to a method)
The program in Example 39 shows that if you call a method and give it an object as argument, then the method can modify that object. More specifically, you can observe that:
the
main
method of the program calls the static methodShopItem.inflateNetPrice(...)
twice, by providing as arguments theShopItem
objects in variablesitem1
anditem2
;the static method
ShopItem.inflateNetPrice(item, ...)
modifies the object fielditem.netPrice
; andafter the method call returns,
main
prints the values ofitem1.netPrice
anditem2.netPrice
(by callingShopItem.description(...)
) — and we can observe that the values of those fields have changed.
The general intuition here is that when a program calls a method by providing an
object as argument, then the program “passes the control over the whole object”
to the method — and the method might change the values of the object’s fields.
This is what ShopItem.inflateNetPrice(...)
does.
(In the next modules we will see more precisely why and how this happens.)
Non-Static Object Methods#
In Example 39 we have enriched the class ShopItem
with
some static methods that
implement ShopItem
-related functionality, making the code better organised and
more reusable.
However, static methods are not the only tool to achieve this result: we can also implement non-static methods (a.k.a. object method, or simply methods).
From the caller perspective, the difference between a static method of class
ShopItem
and a non-static method of an object of the class ShopItem
is that:
in Example 39 we call e.g.:
ShopItem.description(item1) ShopItem.description(item2)
i.e. we specify the class that contains the static method (
ShopItem.description(...)
) and we provide as argument the object that the static method uses for its computations (item1
and thenitem2
);instead, the corresponding calls to a similar non-static method would look like:
item1.description() item2.description()
which mean: “call the method
description()
of the object in the variableitem1
,” and then do the same with the object initem2
. Notice that we do not need to pass the object as parameter (because we have already selected the object when calling its method).
To achieve this result, we only need to apply a few changes to static methods in Example 39 (that are shown in Example 40 below):
we remove the
static
keyword from the method definition, andwe remove the argument of type
ShopItem
on which the original static methods operates. Instead, inside the method body we can use the the special predefined variablethis
(the same special variable that we also used in the constructor).
Non-static methods can make our code even more compact — and later in the course we will see that non-static methods have other very useful features.
ShopItem
class, version 2)
The program below is a variation of the one in Example 39
where the static methods description
and inflateNetPrice
are converted into
non-static methods. The resulting differences with
Example 39 are highlighted.
Notice that:
the non-static methods do not have the keyword
static
in their definition;in the body of the methods, we use the special predefined variable
this
to access the object that we used to call the method. So, for example:when the code calls
item1.description()
, then the variablethis
on lines 33–35 lets the method access the same object of the variableitem1
;when the code calls
item2.inflateNetPrice(10)
, then the variablethis
on line 40 lets the method access (and modify) the same object of the variableitem2
;
1class ShopPrices {
2 public static void main(String[] args) {
3 var item1 = new ShopItem("Cellphone", 2500, 0.25, 8);
4 var item2 = new ShopItem("Chocolate bar", 25, 0.25, 37);
5
6 System.out.println("Item 1: " + item1.description());
7 System.out.println("Item 2: " + item2.description());
8
9 System.out.print("Applying 10% inflation to net prices... ");
10 item1.inflateNetPrice(10);
11 item2.inflateNetPrice(10);
12 System.out.println("Done.");
13
14 System.out.println("Item 1: " + item1.description());
15 System.out.println("Item 2: " + item2.description());
16 }
17}
18
19class ShopItem {
20 String name;
21 double netPrice;
22 double vat;
23 int availability;
24
25 ShopItem(String name, double netPrice, double vat, int avail) {
26 this.name = name;
27 this.netPrice = netPrice;
28 this.vat = vat;
29 this.availability = avail;
30 }
31
32 String description() {
33 var str = this.name + " (price: " + this.netPrice + " DKK + "
34 + (this.vat * 100) + "% VAT; "
35 + "availability: " + this.availability + ")";
36 return str;
37 }
38
39 void inflateNetPrice(double percentage) {
40 this.netPrice = this.netPrice + (this.netPrice * (percentage / 100.0));
41
42 // This "return" is optional, since this method returns nothing (void)
43 return;
44 }
45}
Concluding Remarks#
You should now have an understanding of two main topics:
How to define and use classes to group values of different types into a single object, and how to define a constructor to initialise a new object (using the special predefined variable
this
);How to organise your code using (non-static) methods that can be called using an object — and how methods can use the special special predefined variable
this
to access the object used to call them, and to read and modify the values of that object’s fields.
With this knowledge, you should be able to understand each line of the programs in Example 34 and Example 40.
References and Further Readings#
You are invited to read the following sections of the reference book: (Note: they sometimes mention Java features that we will address later in the course)
Section 4.1 - “Classes and Objects Revisited”
Section 4.2 - “Anatomy of a Class”
Section 4.4 - “Anatomy of a Method”
Section 4.5 - “Constructors Revisited”
Exercises#
Note
These exercises are not part of the course assessment. They are provided to help you self-test your understanding.
ShopItem
)
Try to extend the class ShopItem
in the Java program in
Example 40 by adding the following
methods:
double price()
This method computes and returns the full price of
this
shop item, by adding the item’s VAT to the item’s net price. For example, consider the following program snippet:var headphones = new ShopItem("Headphones", 700, 0.25, 4); System.out.println("Price (including VAT): " + headphones.price());
The program snippet above should output:
Price (including VAT): 875.0
NOTE: the method must not modify the values of the object’s fields.
boolean equals(ShopItem other)
This method returns
true
ifthis
shop item is equal to theother
shop item given as argument. To be “equal,” the two objectsthis
andother
must have equal values in their corresponding fields. If the two objects are not equal, the method returnsfalse
.For example, consider the following program snippet:
var item1 = new ShopItem("AAA batteries", 30, 0.25, 40); var item2 = new ShopItem("A".repeat(3) + " batteries", 30, 0.25, 40); if (item1.equals(item2)) { System.out.println("The items are equal"); } else { System.out.println("The items are different"); }
The program snippet above should output:
The items are equal
NOTE: the method must not modify the values of the object fields. Also remember that, in order to check whether two
String
objects are equal, you should use the .equals(…) method.
Lab and Weekly Assessments#
During the lab you can try the exercises above or begin your work on the weekly assessments below.
Important
For each assessment, you can download the corresponding handout and submit your solution on DTU Autolab: https://autolab.compute.dtu.dk/courses/02312-E24.
For details on how to use Autolab and the assessment handouts, and how to submit your solutions, please read these instructions.
If you have troubles, you can get help from the teacher and TAs.
01 - Point#
Edit the file Point.java
provided in the handout, and implement a class called
Point
representing a point on the Cartesian plane. The requirements are:
Objects of the class
Point
must have two fields calledx
andy
, both having typedouble
. Therefore, ifp
is an object of typePoint
, we can write e.g.:p.x = 1.0; p.y = 2.0; System.out.println("The point coordinates are: " + p.x + ", " + p.y);
The class
Point
must have the following static method:static void move(Point p, double dx, double dy)
When called, the method modifies the
Point
objectp
by adding to its fieldsx
andy
the valuesdx
anddy
, respectively. (Remember thatvoid
means that the static methodmove(...)
does not return any value.)The class
Point
must have the following static method:static Point translate(Point p, double dx, double dy)
When called, the method creates and returns a new
Point
object whose coordinates are obtained from the coordinatesx
andy
of thePoint
objectp
, by adding the values of the argumentsdx
anddy
, respectively. NOTE: the method must not modify the fields the objectp
.
The handout includes some Java files called Test01.java
, Test02.java
, etc.:
they are test programs that use the class Point
, and they might not compile or
work correctly until you complete the implementation of class Point
in the
file Point.java
. You should read those test programs and try to run them, but
you must not modify them.
When you are done, submit the modified file Point.java
on DTU Autolab.
Hint
You may want to read again how to split a Java program into separate files.
To check whether your solution works with one of the test programs, you will need to compile at least
Point.java
and that test program. For instance, if you want to tryTest01.java
, you will need to first run (on the console):javac Point.java Test01.java
and then you can execute the
main
method contained in the classTest01
, by running:java Test01
02 - Point 2#
Note
This assessment is a variation of 01 - Point, with two goals:
adding a constructor to the class
Point
(as explained in Constructors), andturning the static methods of the class
Point
into non-static methods, (as explained in Non-Static Object Methods).
Therefore, you can solve this assessment by first solving 01 - Point, and then adapting its solution according to the requirements below.
Edit the file Point.java
provided in the handout, and implement a class called
Point
representing a point on the Cartesian plane. The requirements are:
Objects of the class
Point
must have two fields calledx
andy
, both having typedouble
. Therefore, ifp
is an object of typePoint
, we can write e.g.:p.x = 1.0; p.y = 2.0; System.out.println("The point coordinates are: " + p.x + ", " + p.y);
The class must have the following constructor:
Point(double x, double y)
which allows us to create a new
Point
object by writing e.g.:var p = new Point(3.14, 42);
Point
objects must have the following method:void move(double dx, double dy)
When called, the method modifies
this
object by adding to its fieldsx
andy
the valuesdx
anddy
, respectively. (Remember thatvoid
means that the methodmove(...)
does not return any value.)Point
objects must have the following method:Point translate(double dx, double dy)
When called, the method creates and returns a new
Point
object whose coordinates are obtained fromthis
object’s coordinatesx
andy
by adding the values of the argumentsdx
anddy
, respectively. NOTE: the method must not modify the fields ofthis
object.
The handout includes some Java files called Test01.java
, Test02.java
, etc.:
they are test programs that use the class Point
, and they might not compile or
work correctly until you complete the implementation of class Point
in the
file Point.java
. You should read those test programs and try to run them, but
you must not modify them.
When you are done, submit the modified file Point.java
on DTU Autolab.
Hint
The hints given for 01 - Point are also useful for this assessment.
03 - Cars#
Edit the file Car.java
provided in the handout, and implement a class called
Car
representing a car. The requirements are:
Objects of the class
Car
must have four fields called respectively:brand
(e.g."FIAT"
)model
(e.g."Topolino"
)numberPlate
(e.g."EZ 13623"
)color
(e.g."blue"
)
The
Car
class class must have the following static method:static String description(Car car)
When called, the method returns a string with information about the
car
object given as argument. For instance, callingCar.description(c)
(using a car objectc
with the field values listed above) must return a string of the following form:FIAT Topolino, blue [EZ 13623]
The
Car
class must have the following static method:static boolean equals(Car car, Car other)
When called, the method returns
true
if thebrand
,model
,numberPlate
, andcolor
fields of thecar
given as argument are equal to the corresponding fields of theother
argument; otherwise, the method returnsfalse
.The
Car
class must have the following static method:static boolean isAlike(Car car, Car other)
When called, the method returns
true
if thebrand
,model
, andcolor
fields of thecar
given as argument object are equal to the corresponding fields of theother
argument; otherwise, the method returnsfalse
.
The handout includes some Java files called Test01.java
, Test02.java
, etc.:
they are test programs that use the class Car
, and they might not compile or
work correctly until you complete the implementation of class Car
in the
file Car.java
. You should read those test programs and try to run them, but
you must not modify them.
When you are done, submit the modified file Car.java
on DTU Autolab.
Hint
The hints given for 01 - Point are also useful for this assessment.
04 - Cars 2#
Note
This assessment is a variation of 03 - Cars, with two goals:
adding a constructor to the class
Car
(as explained in Constructors), andturning the static methods of the class
Car
into non-static methods, (as explained in Non-Static Object Methods).
Therefore, you can solve this assessment by first solving 03 - Cars, and then adapting its solution according to the requirements below.
Edit the file Car.java
provided in the handout, and implement a class called
Car
representing a car. The requirements are:
Objects of the class
Car
must have four fields called respectively:brand
(e.g."Ford"
)model
(e.g."Fiesta"
)numberPlate
(e.g."AF 54539"
)color
(e.g."red"
)
The class must have a constructor that takes the car’s brand, model, number plate, and colour, and can be called e.g. as:
var c = new Car("FIAT", "Topolino", "EZ 13623", "blue");
Car
objects must have the following method:String description()
When called, the method returns a string with the car information. For instance, calling
c.description()
(usingc
from the previous point) must return a string of the following form:FIAT Topolino, blue [EZ 13623]
Car
objects must have the following method:boolean equals(Car other)
When called, the method returns
true
if thebrand
,model
,numberPlate
, andcolor
fields ofthis
object are equal to the corresponding fields of theother
object; otherwise, the method returnsfalse
.Car
objects must have the following method:boolean isAlike(Car other)
When called, the method returns
true
if thebrand
,model
, andcolor
fields ofthis
object are equal to the corresponding fields of theother
object; otherwise, the method returnsfalse
.
The handout includes some Java files called Test01.java
, Test02.java
, etc.:
they are test programs that use the class Car
, and they might not compile or
work correctly until you complete the implementation of class Car
in the
file Car.java
. You should read those test programs and try to run them, but
you must not modify them.
When you are done, submit the modified file Car.java
on DTU Autolab.
Hint
The hints given for 01 - Point are also useful for this assessment.