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
ShopItemspecifies the names and types of fields (e.g., the name of a shop item is aString, the VAT of a shop item is aninteger…), andeach object of the class
ShopItemactually “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.
Fig. 18 Defining a class is conceptually similar to building a cookie cutter. Then, creating objects of that class is conceptually similar to using that cookie cutter to make cookies: each cookie (i.e., each object) will have a shape (i.e., fields and values) determined by the cookie cutter (i.e., by the class) used to create it. (Image from https://www.amazon.de/-/en/Stainless-Cutters-Biscuit-Biscuits-Accessories/dp/B08YJ6RLCB)#
Example 37 (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.
Example 38 (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
thisautomatically refers to the newly-created object;on line 8 of the constructor, the field
nameofthisobject (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
netPriceofthisobject (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.vatandthis.availabilityare 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
Example 39 (A program using the 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 aStringthat shows the values of the various fields of the givenitem;inflateNetPrice(item, percentage)modifiesitemby 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)
Remark 16 (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
mainmethod of the program calls the static methodShopItem.inflateNetPrice(...)twice, by providing as arguments theShopItemobjects in variablesitem1anditem2;the static method
ShopItem.inflateNetPrice(item, ...)modifies the object fielditem.netPrice; andafter the method call returns,
mainprints the values ofitem1.netPriceanditem2.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 (item1and 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
statickeyword from the method definition, andwe remove the argument of type
ShopItemon 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.
Example 40 (A program using the 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
staticin their definition;in the body of the methods, we use the special predefined variable
thisto access the object that we used to call the method. So, for example:when the code calls
item1.description(), then the variablethison lines 33–35 lets the method access the same object of the variableitem1;when the code calls
item2.inflateNetPrice(10), then the variablethison 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
thisto 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.
Exercise 26 (Adding more methods to 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
thisshop 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
trueifthisshop item is equal to theothershop item given as argument. To be “equal,” the two objectsthisandothermust 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
Stringobjects 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
Pointmust have two fields calledxandy, both having typedouble. Therefore, ifpis 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
Pointmust have the following static method:static void move(Point p, double dx, double dy)
When called, the method modifies the
Pointobjectpby adding to its fieldsxandythe valuesdxanddy, respectively. (Remember thatvoidmeans that the static methodmove(...)does not return any value.)The class
Pointmust have the following static method:static Point translate(Point p, double dx, double dy)
When called, the method creates and returns a new
Pointobject whose coordinates are obtained from the coordinatesxandyof thePointobjectp, by adding the values of the argumentsdxanddy, 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.javaand 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
mainmethod 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
Pointinto 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
Pointmust have two fields calledxandy, both having typedouble. Therefore, ifpis 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
Pointobject by writing e.g.:var p = new Point(3.14, 42);
Pointobjects must have the following method:void move(double dx, double dy)
When called, the method modifies
thisobject by adding to its fieldsxandythe valuesdxanddy, respectively. (Remember thatvoidmeans that the methodmove(...)does not return any value.)Pointobjects must have the following method:Point translate(double dx, double dy)
When called, the method creates and returns a new
Pointobject whose coordinates are obtained fromthisobject’s coordinatesxandyby adding the values of the argumentsdxanddy, respectively. NOTE: the method must not modify the fields ofthisobject.
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
Carmust have four fields called respectively:brand(e.g."FIAT")model(e.g."Topolino")numberPlate(e.g."EZ 13623")color(e.g."blue")
The
Carclass class must have the following static method:static String description(Car car)
When called, the method returns a string with information about the
carobject given as argument. For instance, callingCar.description(c)(using a car objectcwith the field values listed above) must return a string of the following form:FIAT Topolino, blue [EZ 13623]
The
Carclass must have the following static method:static boolean equals(Car car, Car other)
When called, the method returns
trueif thebrand,model,numberPlate, andcolorfields of thecargiven as argument are equal to the corresponding fields of theotherargument; otherwise, the method returnsfalse.The
Carclass must have the following static method:static boolean isAlike(Car car, Car other)
When called, the method returns
trueif thebrand,model, andcolorfields of thecargiven as argument object are equal to the corresponding fields of theotherargument; 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
Carinto 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
Carmust 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");
Carobjects must have the following method:String description()
When called, the method returns a string with the car information. For instance, calling
c.description()(usingcfrom the previous point) must return a string of the following form:FIAT Topolino, blue [EZ 13623]
Carobjects must have the following method:boolean equals(Car other)
When called, the method returns
trueif thebrand,model,numberPlate, andcolorfields ofthisobject are equal to the corresponding fields of theotherobject; otherwise, the method returnsfalse.Carobjects must have the following method:boolean isAlike(Car other)
When called, the method returns
trueif thebrand,model, andcolorfields ofthisobject are equal to the corresponding fields of theotherobject; 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.