Module 2, Part 2: Console I/O, Conditionals, Strings#
This is the second part of Module 2, and a follow-up to Module 2, Part 1: a First Taste of Java. This second part introduces some more elements of Java, allowing us to write more interesting and interactive programs. We address three main topics:
performing input and output (I/O) operations on the console (a.k.a. terminal);
performing computations depending on conditions;
more details on using strings.
To introduce these topics, we consider a practical problem: writing an interactive program that computes the area of various geometric shapes.
Goal: an Interactive Program that Computes Areas#
Our goal is to write a program that runs on the console (a.k.a. terminal) and behaves as follows:
Asks the user to write their name;
Greets the user by using their name;
Asks the user to write the name of a geometric shape;
Checks what shape has been specified by the user:
if the shape is
"rectangle"
, the program asks the user to specify its width and height (both having typedouble
), and tells whether the rectangle is a square (when width and height are equal). Then, the program computes the area, and prints the result;if the shape is
"circle"
, the program asks the user to specify its radius (having typedouble
), computes the area, and prints the result;otherwise (i.e. if the shape is something else), the program displays an error message.
Finally, the program says goodbye and ends.
This flowchart visualises the intended program behaviour:
Based on our first taste of Java, we know how to address some parts of this program: displaying a greeting, computing an area. However, we have not yet seen how to:
receive user inputs from the console (points 1 and 3), and
execute either point 4.1, 4.2, or 4.3, depending on a condition (i.e. which shape has been specified by the user). In other words, we do not yet know how to write Java code to perform the rhombus-shaped steps in the flowchart.
(An area calculator program)
The following Java program behaves according to the description above, and uses some Java constructs that we have not yet seen. In the rest of this module we discuss all these new constructs.
Note
You can run the program below by copy&pasting it in a text editor (e.g. VS
Code), saving it in a file called AreaCalculator.java
, and then running on a
terminal (in the same folder where you saved the file):
java AreaCalculator.java
1class AreaCalculator {
2 public static void main(String[] args) {
3 var scanner = new java.util.Scanner(System.in);
4 scanner.useLocale(java.util.Locale.ENGLISH);
5
6 System.out.println("Hello, human. What is your name?");
7
8 var name = scanner.nextLine();
9 System.out.println("Nice to meet you, " + name + "!");
10 System.out.println("Please write the name of a geometric shape:");
11
12 var shape = scanner.nextLine();
13 if (shape.equals("rectangle")) {
14 System.out.println("What is the width?");
15 var width = scanner.nextDouble();
16 System.out.println("What is the height?");
17 var height = scanner.nextDouble();
18
19 if (width == height) {
20 System.out.println("The specified rectangle is a square");
21 } else {
22 System.out.println("The specified rectangle is not a square");
23 }
24
25 var area = width * height;
26 System.out.println("The area is: " + area);
27 } else if (shape.equals("circle")) {
28 System.out.println("What is the radius?");
29 var radius = scanner.nextDouble();
30
31 var area = radius * radius * Math.PI;
32 System.out.println("The area is: " + area);
33 } else {
34 System.out.println("I don't know the shape '" + shape + "' :-(");
35 }
36
37 scanner.close();
38 System.out.println("Goodbye!");
39 }
40}
Console I/O#
In our first Java program we
displayed some console outputs, by using System.out.println(...)
. We now
discuss the topic of console input and output in more detail — and this will
let us begin an exploration of the
Java Application Programmer Interface (API), which is a library of
ready-to-use software components that we can utilise to develop our programs.
The Standard Input and Output Streams#
Java programs can read inputs and display outputs by using two predefined objects provided by the Java API:
System.out
can be used to display outputs on the console. To produce an output, we need to call (or invoke) one of the methods provided bySystem.out
: for example, the methodprintln
takes one argument, displays its value on the console, and moves to the beginning of the next line on the console. We have used this method in our first Java program: we wroteSystem.out.println(message)
to display the value of the argumentmessage
.System.in
can be used to receive inputs from the console. This object has a limited set of methods: it only supports reading sequences of bytes, which makes the handling of inputs quite cumbersome. For this reason, we create and use instead a more convenient Scanner object.
The objects System.in
and System.out
are called standard input
stream and standard output stream, respectively: you can imagine that they
carry “streams” of text characters — one flowing from the console keyboard to
the Java application (input), and another flowing from the Java application to
the console display (output), as shown in Fig. 13 below. This
design descends from the teleprinter and video terminals from the 1960s and 1970s.
Creating and Using a Scanner for Reading Inputs#
On the Java shell, we can create a scanner object that reads inputs from the keyboard, as follows:
jshell> var s = new java.util.Scanner(System.in)
scanner ==> java.util.Scanner[...]
| created variable s : Scanner
Let us examine this in detail:
On the first line, we use the keyword
new
to construct (i.e., create) a new object of a desired class;In this example, we write
new java.util.Scanner(System.in)
, where:java.util.Scanner
is the class name, which is provided by the Java API; (more specifically,java.util
is the name of a Java package that, among other things, contains a class calledScanner
);to construct the desired object of the class
java.util.Scanner
, we provide an argument — and in this case, we provideSystem.in
.
We use the result of
new ...
to declare a variable calleds
. Correspondingly, on the 2nd line above, the Java shell tells us that variables
now gives us access to the object constructed bynew ...
(such an object is represented asjava.util.Scanner[...]
).The last line printed by the Java shell tells us that the variable
s
has typeScanner
(which is abbreviated fromjava.util.Scanner
).
We can now use the variable s
to call the methods provided by the
object we have just created; moreover, since we constructed this Scanner
object by “attaching” it to System.in
(which we provided as argument when
using new ...
), this Scanner
object will allow us to receive inputs from the
console.
Scanner
object)
Before continuing, it is recommended to configure the localisation of the
Scanner
object, i.e. the conventions used by the scanner e.g. to recognise
numbers with fractional parts. To do that, you can call the method useLocale
,
and (for the lectures and exercises of this course) specify the English
localisation:
jshell> s.useLocale(java.util.Locale.ENGLISH)
This will ensure that the scanner will apply the English language standards and
recognise e.g. the input 3.14
as a number with fractional digits. If you don’t
configure the localisation, then the Scanner will apply your system defaults:
for instance, if your system language is set to Danish, the Scanner
object
will not recognise 3.14
as a number, but it will recognise 3,14
instead.
For instance, we can use the Scanner object
to read the next line of input
text and use it to declare a variable:
jshell> var inputLine = s.nextLine()
The method s.nextLine()
is now waiting for us to write something and
press ⏎
(return key). For example, if we write This is just a test
and
press ⏎
, the Java shell will then display:
inputLine ==> "This is just a test"
| created variable inputLine : String
Therefore, the method s.nextLine()
has read a whole line of console
input and has produced a corresponding String
, which has been used to declare
the variable inputLine
. From now on, we can use the variable inputLine
to
access the text just received from the console. In fact, we can try:
jshell> System.out.println("The input was: '" + inputLine + "'")
The input was: 'This is just a test'
The Scanner
object also provides other convenience methods for reading
numbers. For example, you can try:
jshell> var width = s.nextDouble()
Again, the method waits for us to provide a number (possibly with decimal
digits). If we write e.g. 4.25
and press ⏎
, we will see:
width ==> 4.25
| created variable width : double
Therefore, the method s.nextDouble()
has read the console input and
returned the corresponding value 4.25 (of type double
), which has been used to
declare the variable width
.
Objects of class Scanner
provide other similar convenience methods to read the
console input and return various types of values. For instance, we could use:
s.nextInt()
, which produces a value of typeint
;s.nextBoolean()
, which produces a value of typeboolean
.
(Closing a scanner)
When we are done using a scanner s
, we should close it, by calling the method
s.close()
(the parentheses ()
mean that the method does not take any
argument). In Example 12 you can observe that a
Scanner
object is created at the beginning of the program, and closed at the
end.
After being closed, a scanner instance object will report an error (as an exception) if it is used to read further inputs.
Scanner
objects)
If a Scanner
object receives an unexpected input, it will report an error
(as an exception) and stop working. For example, if you use
s.nextInt()
on the Java shell, then type Hello!
and press ⏎
, you
will see:
jshell> s.nextInt()
Hello!
| Exception java.util.InputMismatchException
| at Scanner.throwFor (Scanner.java:939)
| at Scanner.next (Scanner.java:1594)
| at Scanner.nextInt (Scanner.java:2258)
| at Scanner.nextInt (Scanner.java:2212)
| at (#8:1)
After this, error, the Scanner
object becomes unusable: any attempt to
read inputs using the variable s
will cause an error. (We will see how to
handle errors later in the course)
Note
The class java.util.Scanner
is part of the Java API; if you are curious, you
can have a look at
its official documentation.
(Terminology: “value” or “object”? “Type” or “class”?)
In the explanation above, we have said that java.util.Scanner
is the class
of the object constructed with new ...
. Instead, in
previous examples, we have
talked about the type of values (e.g. value 42
has type int
).
So, what is the difference between “class” and “type,” and between “object” and “value”?
The term “type” can refer to both primitive data types (
int
,boolean
,double
, …) and classes (such asjava.util.Scanner
). In other words, a class is a specific kind of type with advanced features that we will explore later in the course. Therefore, it is correct to say either:the variable
s
has typeScanner
; orthe variable
s
has classScanner
.
In Java, the term “value” is often generically used to refer to two things:
a primitive value, which is instance of a primitive data types (e.g.
42
is an instance ofint
,false
is an instance ofboolean
,3.14
is an instance ofdouble
, …); oran object, which is an instance of a class (such as
java.util.Scanner
).
We will see later in the course that primitive values and objects have significantly different behaviours (and we will see the first signs of their difference in this very Module, when we will discuss how to compare strings).
(Terminology: statements vs. expressions)
When talking about Java code constructs, we often use the terms “statement” and “expression.” You can follow the intuition presented below:
Expressions compute and produce a result value (e.g. a number, or an object). The act of computing the result of an expression is called evaluation, and we often say that an expression “evaluates to” a result. For instance:
2
is an expression, because it produces the value 2 itself;x
(i.e. the name of a variable) is an expression, because it produces the value previously given tox
;2 + 3
is an expression that uses the operator+
to compute and produce the value 5; (or in other words,2 + 3
evaluates to the result 5)s.nextLine()
is an expression, because it produces a result (aString
with the input received from the console);new java.util.Scanner(System.in)
is an expression, because it produces a result: a newly-created object of the classScanner
. (We will reprise the exact behaviour ofnew
later in the course.)
Therefore, an expression can be written in any point of Java program where a value is required: when the program runs, the required value is obtained by computing the expression. For instance:
the operator
+
requires two numerical values to compute a result (e.g.2 + 3
evaluates to the result 5). Therefore, we can use as arguments for+
any expression that produces a number. E.g. we can write(2 * 3) + 5
, and the result will be computed by first computing the expression2 * 3
, and then adding its result to5
;to declare a variable, we write
var x = ...
, and we need to provide a value instead of...
. Therefore, we can provide an expression like2 + 3
ors.nextLine()
ornew java.util.Scanner(...)
— and the result of the expression will become the value ofx
.
Note
For more details, see Section 2.4 (“Expressions”) of the reference book.
Statements do not produce a result value: their purpose is to have an effect on the program and its execution. For instance:
var x = 2 + 3
is a statement that declares the variablex
, which can be used later in the program (but the statement itself does not produce any result value);if (x) { ... }
(which we will discuss shortly) is a statement that checks whether the expressionx
produces the valuetrue
, and if so, executes the code in...
(but the statement itself does not produce any result value).
A Java program is essentially a sequence of statements delimited by semicolons
;
and/or surrounded by curly brackets{
…}
. When the program runs, the statements are executed one after the other — and executing a statement may require computing one or more expressions written as part of the statement (e.g. to execute the statementvar x = 2 + 3
, the expression2 + 3
is computed, and its result value5
is finally used to declarex
).
The distinction between expressions and statements will become clearer with practice: the suggestion is to pay attention to how the terms “statement” and “expression” are used in these lecture notes, and refer back to this remark in case of doubts.
Conditional Statements and Expressions#
The Java programming language provides various ways to regulate the control flow of a program, i.e. the order in which different part of a program are executed, depending whether a certain condition is true or false. In this Module we address:
We also see in more detail how to write the conditions as Boolean Expressions
In this Module we also address some other ways to control computations depending
on conditions: conditional expressions and switch
constructs.
Important
Conditional expressions and “switch” constructs are not strictly necessary for writing beginner Java programs: every task that can be solved using them can also be solved using if-then-else statements. Therefore, make sure you understand how to use if-then-else before going further. With practice, you will see that can contitional expressions and “switch” constructs can help you write Java code that is simpler and easier to read and maintain.
If-Then-Else Statement#
The if-then-else statement has the following syntax:
1if (bool_expr) {
2 // Statements that are executed when the result of 'bool_expr' is 'true'
3} else {
4 // Statements that are executed when the result of 'bool_expr' is 'false'
5}
6// Other statements can follow (optionally)
where bool_expr
can be any expression of type boolean
. When an if-then-else
statement is executed, the following happens:
first, the expression
bool_expr
is evaluated, and its result is checked. Subsequently,if the result of
bool_expr
is the valuetrue
, the statements inside the first pair of curly brackets{
…}
(lines 1–3) are executed;otherwise (i.e. if the result of
bool_expr
is the valuefalse
), the statements inside the pair of curly brackets{
…}
afterelse
(lines 3–5) are executed;
finally, the execution continues with the statements (if any) that follow the whole if-then-else statement (line 6).
When represented as a flowchart, the if-then-else statement looks as follows — and whenever we spot the following pattern in a flowchart, we should consider using an if-then-else statement when writing the corresponding Java program.
Note
Conceptually, the if-then-else statement serves the same purpose of
conditional jumps in BlinkenBits: it
checks whether a condition (written as a boolean
expression) produces the
result true
or false
, and depending on this, it makes the execution “jump”
to a certain part of the code. Indeed, behind the scenes, Java translates
if-then-else into “jump” machine instructions.
The key difference is that if-then-else statements in Java are considerably easier to write and read than assembly jumps, because we do not need to worry about registers for computing the condition, nor memory addresses for jumping: we just need to structure our code depending on what we need to be executed when the condition is true or false.
(Using if-then-else on the Java shell)
We can experiment with the if-then-else statement on the Java shell. Let us
first declare two variables (a
and b
):
jshell> var a = 10; var b = 42
a ==> 10
| created variable a : int
b ==> 42
| created variable b : int
Now, let us create a variable that contains the result of a comparison between
a
and b
: let us call this variable isAGreaterThanB
(notice below that its
type is boolean
and its value is false
).
jshell> var isAGreaterThanB = a > b;
isAGreaterThanB ==> false
| created variable isAGreaterThanB : boolean
Then, copy&paste the following Java code on the Java shell, and press ⏎
:
1if (isAGreaterThanB) {
2 System.out.println("'" + a + " > " + b + "' is TRUE :-)");
3} else {
4 System.out.println("'" + a + " > " + b + "' is FALSE :-(");
5}
6System.out.println("We got past the if-then-else statement");
What will happen is:
on line 1, the if-then-else statement checks whether the conditional expression (in this case, the variable
isAGreaterThanB
) has the valuetrue
orfalse
. Since its value isfalse
, the execution “jump” to the block of code under theelse
(line 4) — which displays the string"... is FALSE :-("
;finally, the execution continues with the code that follows the whole if-then-else statement, which displays the string
"We got past..."
.
Therefore, we will see the following output on the Java shell:
'10 > 42' is false :-(
We got past the if-then-else statement
We can revise the example above by writing the condition expression directly
in the if-then-else statement, so we do not have to create the variable
isAGreaterThanB
. In fact, we can write:
1if (a > b) {
2 System.out.println("'" + a + " > " + b + "' is TRUE :-)");
3} else {
4 System.out.println("'" + a + " > " + b + "' is FALSE :-(");
5}
6System.out.println("We got past the if-then-else statement");
Java executes this if-then-else by first computing the expression a > b
(line
1); since its result is false
, the execution “jumps” to the block of code
under the else
, and produces the same output we have seen before. (Try it on
the Java shell!)
To see a different outcome, you can now try the following:
assign to variable
a
a different value that is bigger thanb
, e.g.:jshell> a = b * 2 a ==> 84 | assigned to a : int
Then, copy&paste again the second if-then-else example above (the one containing the condition expression
a > b
) on the Java shell, and execute it. Observe that now the output is:'84 > 42' is TRUE :-) We got past the if-then-else statement
Tip
On the Java shell, instead of copy&pasting code snippets that have been already used, you can press the upwards arrow key
↑
to navigate the history of previous code snippets, and press⏎
to execute the one you select.
(Changing (re-assigning) a variable)
As you can see in the Example 13 above, in Java it is
possible to declare a variable (e.g. var a = 10
) and later change that
variable with a new assignment (e.g. by writing a = b * 2
). You can even
assign a new value to a variable by using its own previous value: for
instance, if a
has value 10, and you write a = a + 3
, the following happens:
first, the expression
a + 3
is computed using the current value ofa
, that is 10. Therefore,a + 3
evaluates to 13;then, the value 13 is assigned to
a
. Therefore, from now on,a
has value 13; if you executea = a + 3
again, thena + 3
will produce the result 16, hencea
will get the value 16.
This may appear odd if you are used to mathematical terminology, where variables
are declared once and cannot be changed afterwards. However, we have seen
something very similar happen in
BlinkenBits, e.g. when we write
r0 <- r0 + r1 to change the value contained in the register r0
with the
result of the sum of the current value in r0
plus the value in r1
.
Indeed, the intuition here is that
in Java, a “variable” works like a register (or a memory location): it
contains a value that can be changed while the program runs.
To do that in Java, we just write an assignment; therefore, when e.g.
a = b * 2
is executed, the old value that was contained in a
is
overwritten with the result of the expression b * 2
(so, if b
has value 42,
the variable a
gets the new value 84).
Note that, when a variable is reassigned, then its type must be respected:
e.g. if the variable has type int
, then we can only reassign a new value of
type int
.
If-Then Statement#
We can also write an if-then statement without the “else” part:
1if (bool_expr) {
2 // Statements that are executed when the result of 'bool_expr' is 'true'
3}
4// Other statements can follow (optionally)
which is just shorthand for:
1if (bool_expr) {
2 // Statements that are executed when the result of 'bool_expr' is 'true'
3} else {
4}
5// Other statements can follow (optionally)
In other words, when bool_expr
gives the result false
, then the else
code
block is executed (lines 3–4) — but since that block is empty, the program
just continues its execution with the statements that follow on line 5.
When represented in a flowchart, the if-then statement looks as follows — and whenever we spot the following pattern in a flowchart, we should consider using an if-then statement when writing the corresponding Java program.
(Using if-then on the Java shell)
Using the “if-then” statement, we can write an example similar to the one in Example 13, as follows:
1var message = "FALSE :-(";
2if (a > b) {
3 message = "TRUE :-)";
4}
5System.out.println("'" + a + " > " + b + "' is " + message);
When this code runs, the following happens:
line 1 declares a variable
message
with the string"FALSE :-("
;line 2 checks the condition
a > b
:if the condition is true, then the block of code on lines 2–4 is executed. Consequently, the value of the variable
message
is changed with the string"TRUE :-)"
. Then, the execution continues on line 5;otherwise (i.e. if the condition on line 2 is false), the execution just continues on line 5 — and consequently, the value of the variable
message
is not changed and remains the string"FALSE :-("
;
finally, on line 5, a string with the current values of the variables
a
,b
, andmessage
is displayed.
You can experiment by running the code snippet above on the Java shell, using
different values of a
and b
. For example, if a
is 10 and b
is 42,
then the System.out.println(...)
on line 5 displays:
'10 > 42' is FALSE :-(
Otherwise, if e.g. a
is 84 and b
is 42, then the System.out.println(...)
on line 5 displays:
'84 > 42' is TRUE :-)
Note
While experimenting with the code snippet above, the Java shell may display additional messages like:
| modified variable message : String
| update overwrote variable message : String
The Java shell is just letting us know that a variable (in this case, message
)
has been redeclared. This is not a problem for this examples.
Boolean Expressions#
We have seen that, when writing
if-then-else
or if-then statements, or
conditional expressions,
we need to provide an expression of type boolean
that determines what is
computed and executed next (depending on whether the boolean expression
evaluates to true
or false
).
We have already seen
some expressions that produce boolean results by comparing two numbers
using the relational operators <
(“less than”), >
(“greater than”) or
==
(“equal”); we have also used some of these operators in the
examples above. Java also provides the relational operators <=
(“less than or
equal to”), >=
(“greater than or equal to”), and !=
(“not equal to”):
they also compare numbers and produce true
or false
as a result.
Java also allows to write expressions of type boolean
using 3 logical
operators listed in Table 7 below.
Operator |
Description |
Example |
Result |
---|---|---|---|
|
Logical “and” |
|
|
|
Logical “or” |
|
|
|
Logical “not” |
|
|
(Logical operators)
Our task is to write a program that checks the value of the variable speed
(of
type int
), and:
if the value of
speed
is between 10 and 50 (included), then the program prints “Your speed is OK”;otherwise (i.e. if the value of
speed
is less than 10 or more than 50), the program prints “Your speed is outside the range 10-50!”;
We can solve this problem in different ways, by using different relational and
logical operators to compute whether the speed
is within the limits.
We could write a boolean expression that produces
true
ifspeed
is between 10 and 50 (included), and producesfalse
otherwise. Using the logical operator&&
, we can write:(speed >= 10) && (speed <= 50)
Then, we can use the expression above as the condition of the following if-then-else statement:
if ((speed >= 10) && (speed <= 50)) { System.out.println("Your speed is OK"); } else { System.out.println("Your speed is outside the range 10-50!"); }
As an alternative approach, we could write a boolean expression that evaluates to
true
ifspeed
is outside the limits (i.e. either smaller than 10 or greater than 50), and evaluates tofalse
otherwise. Using the logical operator||
, we can write:(speed < 10) || (speed > 50)
Then, we can use the expression above as the condition of the following if-then-else statement (notice that, compared to the previous solution, here we need to flip the two branches of if-then-else, because the expression is
true
when the speed is outside the limits):if ((speed < 10) || (speed > 50)) { System.out.println("Your speed is outside the range 10-50!"); } else { System.out.println("Your speed is OK"); }
As a further alternative approach, we could write a boolean expression that evaluates to
true
ifspeed
is not outside the limits (i.e.speed
is not smaller than 10 and not greater than 50), and evaluates tofalse
otherwise. Using the logical operators!
and&&
, we can write:!(speed < 10) && !(speed > 50)
Then, we can use the expression above as the condition of the following if-then-else statement:
if (!(speed < 10) && !(speed > 50)) { System.out.println("Your speed is OK"); } else { System.out.println("Your speed is outside the range 10-50!"); }
You can try the expressions and if-then-else statements above on the Java shell
by first declaring a variable called speed
(of type int
); you can try
assigning different values to speed
(e.g. 2, 33, 60, …) and see how
different values alter the result of the boolean expressions, and the output
displayed by the if-then-else statements. You can also try other variations of
the boolean expressions and if-then-else statements.
(Nesting if-then-else and if-then statements)
An if-then-else or if-then statement may execute any sequence of Java statements depending on the result of the boolean condition. This means that we can nest other if-then-else or if-then statements. For instance, consider again this case from Example 15:
if (!(speed < 10) && !(speed > 50)) {
System.out.println("Your speed is OK");
} else {
System.out.println("Your speed is outside the range 10-50!");
}
Now, suppose that we want our program to explicitly tell whether the speed is too fast (above the 10-50 range) or too slow (below the 10-50 range). We can do it as follows:
1if (!(speed < 10) && !(speed > 50)) {
2 System.out.println("Your speed is OK");
3} else {
4 // If the program execution is here, then either speed < 10 or speed > 50
5 if (speed < 10) {
6 System.out.println("You are going too slow!");
7 } else {
8 // If the program execution is here, then speed > 50
9 System.out.println("You are going too fast!");
10 }
11}
You can experiment with the program above on the Java shell, by changing the
values assigned to the variable speed
.
Tip
We can make this last program a bit nicer-looking by leveraging the fact that,
in Java, curly brackets are optional if they only enclose a single statement.
Since the if-then-else on lines 5–10 is a single statement, we can remove the
comment on line 4 and the surrounding curly brackets {
…}
(lines 3 and 11),
and reformat the program as follows:
if (!(speed < 10) && !(speed > 50)) {
System.out.println("Your speed is OK");
} else if (speed < 10) {
System.out.println("You are going too slow!");
} else {
// If the program execution is here, then speed > 50
System.out.println("You are going too fast!");
}
Conditional Expression#
Note
This section of the lecture notes is not strictly necessary for writing beginners’ Java programs. However, learning when and how to use the conditional expressions may make you Java programs easier to write and read.
Example 14 above shows a pattern that may often emerge
when writing a program: the value of a variable (in this case, message
)
depends on a condition (in this case, a > b
). For situations like this, Java
offers a very handy tool that can make our programs simpler: conditional
expressions, which have the following syntax:
bool_expr ? expr_if_cond_true : expr_if_cond_false
Where bool_expr
must be an expression of type boolean
, while
expr_if_cond_true
and expr_if_cond_false
are two expressions of the same
type (e.g. both int
, both String
, etc.).
The conditional expression is computed as follows:
first,
bool_expr
is computed, and its result is checked:if the result of
bool_expr
istrue
, thenexpr_if_cond_true
is computed, and its result becomes the result of the whole conditional expression;otherwise (i.e. if the result of
bool_expr
isfalse
), thenexpr_if_cond_false
is computed, and its result becomes the result of the whole conditional expression.
(Using conditional expressions on the Java shell)
Consider this code snippet:
1var message = (a > b) ? "TRUE :-)" : "FALSE :-(";
2System.out.println("'" + a + " > " + b + "' is " + message);
You can experiment with this code by copy&pasting it on the Java shell, and
using different values for the variables a
and b
.
For example, when a
is 10 and b
is 42, the condition a > b
(line 1) is
false
; consequently, the conditional expression result is the string
"FALSE :-("
— and that string is used in the declaration of the variable
message
. Therefore, the System.out.println(...)
(line 2) displays:
'10 > 42' is FALSE :-(
Instead, when a
is 84 and b
is 42, the condition a > b
(line 1) is
true
; consequently, the conditional expression result is the string
"TRUE :-)"
— and that string is used in the declaration of the variable
message
. Therefore, the System.out.println(...)
(line 2) displays:
'84 > 42' is TRUE :-)
(Area calculator using conditional expressions)
The following Java program is very similar to the one in Example 12, and (from the user perspective) it behaves in the same way. The difference is that the highlighted lines perform their task (i.e., printing whether the rectangle is a square or not) with a conditional expression (instead of an if-then-else statement):
on line 19, the result of the conditional expression is used to declare the variable
squareOrNot
(which is a string). After that line is executed, the variablesquareOrNot
may contain either"a square"
or"not a square"
, depending on whether the expressionwidth == height
istrue
orfalse
;then, on line 20, the value of the variable
squareOrNot
is printed, following the string"The specified rectangle is "
. Therefore, the user will see a different output depending on the value of the variablesquareOrNot
.
1class AreaCalculator {
2 public static void main(String[] args) {
3 var scanner = new java.util.Scanner(System.in);
4 scanner.useLocale(java.util.Locale.ENGLISH);
5
6 System.out.println("Hello, human. What is your name?");
7
8 var name = scanner.nextLine();
9 System.out.println("Nice to meet you, " + name + "!");
10 System.out.println("Please write the name of a geometric shape:");
11
12 var shape = scanner.nextLine();
13 if (shape.equals("rectangle")) {
14 System.out.println("What is the width?");
15 var width = scanner.nextDouble();
16 System.out.println("What is the height?");
17 var height = scanner.nextDouble();
18
19 var squareOrNot = (width == height) ? "a square" : "not a square";
20 System.out.println("The specified rectangle is " + squareOrNot);
21
22 var area = width * height;
23 System.out.println("The area is: " + area);
24 } else if (shape.equals("circle")) {
25 System.out.println("What is the radius?");
26 var radius = scanner.nextDouble();
27
28 var area = radius * radius * Math.PI;
29 System.out.println("The area is: " + area);
30 } else {
31 System.out.println("I don't know the shape '" + shape + "' :-(");
32 }
33
34 scanner.close();
35 System.out.println("Goodbye!");
36 }
37}
Switch Statement#
Note
This section of the lecture notes is not strictly necessary for writing
beginners’ Java programs. However, learning when and how to use the switch
statement instead of if-then-else may make your Java programs easier to write
and read.
Suppose we need to write a program that, given a number n
, works as follows:
if
n
is between 0 and 2, the program prints the corresponding word (i.e. “zero”, “one”, or “two”)otherwise, the program prints “WAT!”
We can do this by nesting several if-then-else
and/or if-then
statements,
for instance as follows:
1if (n == 0) {
2 System.out.println("zero");
3} else {
4 if (n == 1) {
5 System.out.println("one");
6 } else {
7 if (n == 2) {
8 System.out.println("two");
9 } else {
10 System.out.println("WAT!");
11 }
12 }
13}
This deep nesting can be hard to read — but we can leverage the fact that, in Java, curly brackets are optional if they only enclose a single statement. Therefore, we can remove some curly brackets and reformat the code above as:
1if (n == 0) {
2 System.out.println("zero");
3} else if (n == 1) {
4 System.out.println("one");
5} else if (n == 2) {
6 System.out.println("two");
7} else {
8 System.out.println("WAT!");
9}
This pattern is quite common, and contains a bit of redundancy: we specify
multiple times that we are checking whether a same expression (n
) is equal to
different constant values (0, 1, …). Luckily, Java provides a handy tool to
make this kind of code more straightforward: the switch statement, which
allows us to rewrite the code above as follows:
1switch (n) {
2 case 0 -> {
3 System.out.println("zero");
4 }
5 case 1 -> {
6 System.out.println("one");
7 }
8 case 2 -> {
9 System.out.println("two");
10 }
11 default -> {
12 System.out.println("WAT!");
13 }
14}
The meaning of the code above is:
compute the result of the expression given as argument to
switch
(line 1)check whether that value is equal to one of the values provided in the various
case
s (lines 2, 5, 8) — and if so, execute the code that follows the corresponding arrow->
;otherwise, execute the code that follows
default -> ...
.
(Omitting curly brackets in if-then-else, if-then, and switch statements)
The last few code examples could be made shorter. Since curly brackets are optional if they only enclose a single statement, we could have written:
1if (n == 0)
2 System.out.println("zero");
3else if (n == 1)
4 System.out.println("one");
5else if (n == 2)
6 System.out.println("two");
7else
8 System.out.println("WAT!");
This is, however, a bit risky and not recommended, because it makes it easy to introduce mistakes. For instance, suppose that we want to update the program to make it print “I don’t know this number” after “WAT!”. We may write a program like:
1if (n == 0)
2 System.out.println("zero");
3else if (n == 1)
4 System.out.println("one");
5else if (n == 2)
6 System.out.println("two");
7else
8 System.out.println("WAT!");
9 System.out.println("I don't know this number");
This program is syntactically valid and Java can run it. By looking at it, it
may seem that the System.out.println(...)
on line 9 belongs to the else
on
line 7, and is only executed when n
is not 0, 1, nor 2 — and indeed, the
program may seem to work correctly, e.g. when n
has value 42 or 1000.
However this program has a bug and does not always behave as we want! In
fact, if you try the program on the Java shell, you will see that it always
prints I don't know this number
, even when e.g. n
has value 1. This is
because the last else
(line 7) only captures the first statement that follows
it (line 8). Therefore, this program is equivalent to:
1if (n == 0) {
2 System.out.println("zero");
3} else if (n == 1) {
4 System.out.println("one");
5} else if (n == 2) {
6 System.out.println("two");
7} else {
8 System.out.println("WAT!");
9}
10System.out.println("I don't know this number");
Instead, what we want is is:
1if (n == 0) {
2 System.out.println("zero");
3} else if (n == 1) {
4 System.out.println("one");
5} else if (n == 2) {
6 System.out.println("two");
7} else {
8 System.out.println("WAT!");
9 System.out.println("I don't know this number");
10}
Summing up, the advice is: be careful when you omit curly brackets in if-then and if-then-else statements. It is better to use a few more curly brackets (even if they are not strictly needed) and avoid bugs that may be difficult to find and fix.
Instead, omitting brackets when using the switch statement is safer: if you make a mistake and omit curly brackets in the “wrong way,” Java will immediately report syntax errors and will not even run your program. The “switch” example above can be simplified as:
1switch (n) {
2 case 0 -> System.out.println("zero");
3 case 1 -> System.out.println("one");
4 case 2 -> System.out.println("two");
5 default -> System.out.println("WAT!");
6}
If we try to add a new System.out.println(...)
right after the one on line 5,
as follows…
1switch (n) {
2 case 0 -> System.out.println("zero");
3 case 1 -> System.out.println("one");
4 case 2 -> System.out.println("two");
5 default -> System.out.println("WAT!");
6 System.out.println("I don't know this number"); // Invalid syntax
7}
…then this last program will not run: the Java shell will produce a bunch of syntax errors. Therefore, we immediately know that something is wrong.
To add a new statement to the default
case (or in other case
s) of the
switch without causing syntax errors, we must use curly brackets:
1switch (n) {
2 case 0 -> System.out.println("zero");
3 case 1 -> System.out.println("one");
4 case 2 -> System.out.println("two");
5 default -> {
6 System.out.println("WAT!");
7 System.out.println("I don't know this number");
8 }
9}
Switch Expression#
Note
This section of the lecture notes is not strictly necessary. However,
learning when and how to use the switch
expression instead of if-then-else or
conditional expressions may make your Java programs easier to write and read.
Java also provides a switch expression, that is intuitively similar to a conditional expression, and allows us to rewrite the code we used to illustrate the switch statement in a more concise way:
1var number = switch (n) {
2 case 0 -> "zero";
3 case 1 -> "one";
4 case 2 -> "two";
5 default -> "WAT!";
6};
7System.out.println(number);
The meaning of the switch
expression above is:
compute the result of the expression given as argument to
switch
(line 1);check whether that result is equal to one of the constant values provided in the various
case
s (lines 2, 3, 4) — and if so:compute the result of the expression that follows the corresponding arrow
->
, andthat result becomes the result of the whole
switch
expression;
otherwise (i.e. if none of the
case
s is equal to the result of the argument ofswitch
):compute the result of the expression that follows
default -> ...
(line 5);that result becomes the result of the whole
switch
expression.
In the code snippet above, the result of the switch
expression (which has type
String
) is used to declare the variable number
(line 1), that is displayed
(line 7).
More on Strings in Java#
We now reprise the topic of String
s in Java, to highlight their
features
and the possible pitfalls when comparing two strings.
Some Useful String Methods#
A very important fact is that in Java, String
is not a
primitive data type:
instead, String
is a class, and when we write a string literal like
"Hello"
, we are creating an object of class String
.
The consequence of this fact is that String
objects have methods that we can
invoke (just like we can
invoke methods of Scanner objects).
We can try such methods on the Java shell. First, let’s create a String
object to declare a variable str
that we will use for our experiments:
jshell> var str = "Hello!"
| created variable str : String
We can compute the length of str
by calling the method str.length()
,
which takes no arguments and produces a result of type int
(in this case, 6):
jshell> var len = str.length()
len ==> 6
| created variable len : int
We can retrieve the character at a certain position of str
by calling
str.charAt(pos)
: this method takes an argument (pos
, which must be
an int
) that is the desired character position (counting from 0); then, the
method produces a result of type char
.
jshell> var char0 = str.charAt(0)
char0 ==> 'H'
| created variable char0 : char
jshell> var char3 = str.charAt(3)
char3 ==> 'l'
| created variable char3 : char
Important
If we call str.charAt(...)
with a character position beyond the
length of str
, we get an error (as an exception), as shown below.
(Remember that, since character positions are counted from 0, the last
character of str
is at position str.length() - 1
)
jshell> str.charAt(str.length())
| Exception java.lang.StringIndexOutOfBoundsException: String index out of range: 6
| at StringLatin1.charAt (StringLatin1.java:48)
| at String.charAt (String.java:1517)
| at (#36:1)
We can compute the uppercase version of str
by calling the method
str.toUpperCase()
, which takes no arguments and produces a new String
:
jshell> var strUp = str.toUpperCase()
strUp ==> "HELLO!"
| created variable strUp : String
Similarly, we can compute the lowercase version of str
by calling the method
str.toLowerCase()
, which takes no arguments and produces a new String
:
jshell> var strLow = str.toLowerCase()
strLow ==> "hello!"
| created variable strLow : String
We can also compute a string obtained by repeating str
, by calling
str.repeat(n)
(where n
is an int
specifying the number of repetitions).
jshell> var str3 = str.repeat(3)
str2 ==> "Hello!Hello!Hello!"
| created variable str3 : String
Note
String
objects provide many more methods: you don’t need to learn them by
heart, but they will become familiar with frequent usage. For details, you can
always refer to the
Java API documentation for the String
class.
Comparing String
s for Equality or Alphabetically#
In previous examples we have seen that we can compare numbers for equality using
the operator ==
, which returns a boolean
value (true
or false
). For
example:
jshell> 5 == 2 * 6 - 7
$6 ==> true
| created scratch variable $6 : boolean
This may lead us to believe that we can also use ==
to compare String
objects. For instance, the string "BlahBlah"
should be equal to the result of
"Blah".repeat(2)"
. But if we try, we get the result false
!
jshell> "BlahBlah" == "Blah".repeat(2)
$7 ==> false
| created scratch variable $7 : boolean
This happens because Java String
s are objects — and when used on
objects, the operator ==
does not behave as one might expect.
We will see the precise reason later in the course. For now, it is important to
remember that to check whether two String
objects are equal, we must use the
method .equals(...)
instead of ==
. When we call e.g. str1.equals(str2)
,
the method produces true
if the two strings str1
and str2
are equal
character-by-character; otherwise, the method produces false
. For instance:
jshell> "BlahBlah".equals("Blah")
$8 ==> false
| created scratch variable $8 : boolean
jshell> "BlahBlah".equals("Blah".repeat(2))
$9 ==> true
| created scratch variable $9 : boolean
String
s)
The “switch” statement works as
one might expected when used on String
s: i.e. it compares the string
contents against the constant strings provided in the various case
s, using
the method .equals(...)
.
Consider the following snippet of Java code that, given a String
variable
num
containing a number written in English, prints the corresponding numerical
value:
1switch (num) {
2 case "zero" -> System.out.println("0");
3 case "one" -> System.out.println("1");
4 case "two" -> System.out.println("2");
5 default -> System.out.println("WAT!");
6}
When the switch
statement above checks the various case
s, it uses
num.equals("zero")
on line 2, num.equals("one")
on line 3, etc.
(As usual, you can try the code above on the Java shell: declare a variable
num
of type String
, and experiment by copy&pasting the code above and
running it after assigning different values to num
.)
We conclude this overview with another handy method: str1.compareTo(str2)
produces an int
value, that can be either:
negative, if
str1
comes alphabetically beforestr2
;0 (zero), if
str1
andstr2
are equal character-by-character; orpositive, if
str1
comes alphabetically afterstr2
.
For instance:
jshell> "AAA".compareTo("AAB")
$10 ==> -1
| created scratch variable $10 : int
jshell> "AAA".compareTo("AAA")
$11 ==> 0
| created scratch variable $11 : int
jshell> "AAB".compareTo("AAA")
$12 ==> 1
| created scratch variable $12 : int
Concluding Remarks#
In this module we have seen how to perform input/output on the console, and how to control execution flow of a program by using the if-then-else and if-then statements. We have also studied in more details how to write boolean expressions, and more details and features of Strings.
Along the way, we have also introduced some important concepts: using Java classes and objects (using the Scanner class as a practical application), and the difference between values and objects, and types and classes, and between expressions and statements.
With this knowledge, you should be able to understand each line of the program in Example 12.
We have also addressed some more advanced tools: conditional expressions, the Switch Statement and Switch Expression: you do not need to master these more advanced tools yet — but in some situations, they can be used instead of if-then-else statements and conditional expressions to write simpler and more maintainable programs.
References and Further Readings#
You are encouraged to read the following sections of the reference book: (Note: they sometimes mention Java features that we will address later in the course)
Section 2.1 - “Character Strings”
Section 2.6 - “Interactive Programs”
Section 5.1 - “Boolean Expressions”
Section 5.2 - “The
if
Statement”Section 5.3 - “Comparing Data”
Section 6.2 - “The Conditional Operator”
You can also have a look at Section 6.1 of the reference book
(“The switch
Statement”) — but notice that the book uses a different and
“more traditional” syntax for switch
statements. You should be aware of
that “traditional” syntax, because you may see it used in other programs. These
lecture notes adopt a more modern syntax for switch
(often called “enhanced
switch
”) that is easier to write and read, and less prone to introducing bugs.
If you are curious, you can learn why the modern syntax for switch
was
introduced by reading the (now implemented)
Java Enhancement Proposal 361.
You can find the complete documentation of the Java API here: https://docs.oracle.com/en/java/javase/21/docs/api/index.html.
Exercises#
Note
These exercises are not part of the course assessment. They are provided to help you self-test your understanding.
(Experimenting with comparisons)
Read the following Java expressions, and figure out whether each one is a valid comparison, and if so, what is the result. To verify whether your answers are correct, you can execute each expression on the Java shell.
2 = 2
2 == 2
2 == 1 + 1
2 == "2"
"AAA" == "A".repeat(3)
"AAA".equals("A".repeat(3))
(Boolean expressions)
Assume that variable x
has value 1, and y
has value 2.
Read the following Java expressions: each one has type boolean
, and your task
is to figure out what is its result. To verify whether your answers are
correct, you can execute each expression on the
Java shell (after declaring the
two variables x
and y
with values 1 and 2, respectively).
Tip
Keep in mind that the logical operators
&&
and||
have very low precedence with respect to other operators: for example, the expression2 > 3 || x == y
is equivalent to(2 > 3) || (x == y)
(i.e. the sub-expressions2 > 3
andx == y
are evaluated first, and then their results are used to compute||
).If an expression looks complex, or its evaluation seems unclear, try to manually add parentheses around its sub-expressions, and check whether the original expression and the version with added parentheses evaluate to the same result.
If you are in doubt about operator precedence when writing your Java programs, just add parentheses to make sure that sub-expressions are evaluated in the order you expect. Writing unnecessary parentheses may not be very elegant — but forgetting necessary parentheses is much worse, because it would introduce bugs in your programs!
false
true
true == false
false == false
x != y
x < 3 + y
y < x + 3
(x + y > 3) == false
false != x < 3
(x == y) == false
!false
!true
!true == false
!(true == false)
true && false
false || true
x + y > 3 && x < y
x + y == 3 || x < 4
x < y && (3 * 4 == 2 * 6 - 1) == !(3 < x)
Lab and Weekly Assessments#
During the lab you can try the exercises above or begin your work on the weekly assessments below.
Note
These weekly assessment follow the ones in part 1 of this Module.
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.
03 - Print the Maximum#
The file Maximum.java
provided in the handout contains some Java code that
reads three consecutive values (of type int
) from the console, and uses those
values to declare the three variables a
, b
and c
, respectively.
Your task is to edit the file Maximum.java
to make it print which variable has
a value that is greater than the other two, and the value of that variable.
For example, if the program input from the console is:
10
42
7
Then the program must print:
Variable b has the greatest value: 42
You can assume that one of the values read from the console will be greater than
the other two. When you are done, submit the modified file Maximum.java
on
DTU Autolab.
Hint
To read the input value from the console, you should create and use a Scanner object.
You may start with a simplified problem: find and print the maximum between two variables
a
andb
. Then you could expand your solution to handle three variables, by nesting if-then-else statements.Before writing your solution as a
.java
program, it may be helpful to experiment on the Java shell:declare three variables
a
,b
, andc
(of typeint
), using values of your choice;write a code snippet to identify the maximum between
a
,b
, andc
;change the values of
a
,b
, andc
, and check whether your code snippet can still identify the maximum between them.
You may be able to simplify your code by using conditional expressions.
04 - Conversion from Seconds#
Edit the file Conversion.java
provided in the handout, and write a program
that reads from the console a number of seconds (an int
value), and displays
how many days, hours, minutes and seconds the specified number of seconds
corresponds to. Then, submit the modified file Conversion.java
on DTU Autolab.
For example, if the console input is the number 238577, the program should print:
238577 seconds equals 2 days, 18 hours, 16 minutes and 17 seconds.
Hint
To read the input value from the console, you should create and use a Scanner object.
If you have
n
seconds andn
has typeint
, then:to know how many minutes fit in
n
seconds, you can computen / 60
(which is an integer division);to know how many seconds remain after removing from
n
all the minutes, you can computen % 60
— where%
is the remainder operator in Java.
The program must only print on the console the result of the conversion (according to the example above) and nothing else.
05 - Password Check#
Edit the file Password.java
provided in the handout, and write a program that:
reads a
String
from the console, representing a password; andchecks the length of the password:
if the length is between 5 and 8 characters, the program prints
Password length OK
;if the password is shorter than 5 characters, the program prints
Password too short
;if the password is longer than 8 characters, the program prints
Password too long
.
Then, submit the modified file Password.java
on DTU Autolab.
06 - Line-Point Distance#
Edit the file Distance.java
provided in the handout, and write a program that
reads from the console the values of 4 variables (of type double
), in the
following order:
\(a\) and \(b\) — coefficients of the equation of a line \(y = ax + b\);
\(x_0\) and \(y_0\) — the coordinates of a point.
Then, the program must compute and display on the console the distance between the point and the line.
The distance is computed with the following formula:
If the computed distance is (say) 0.52, the program must print on the console:
The distance is 0.52
When you are done, submit the modified file Distance.java
on DTU Autolab.
Hint
To compute the absolute value and the square root in the formula, you can use
Math.abs(...)
andMath.sqrt(...)
, respectively. For instance:jshell> Math.abs(-2 * 3.0) $1 ==> 6.0 | created scratch variable $1 : double
jshell> Math.sqrt(25.0) $2 ==> 5.0 | created scratch variable $2 : double
The program must only print on the console the computed distance, and nothing else.