Module 2, Part 1: a First Taste of Java#

This Module is split in two parts. In this first part, you will get a first taste of the Java programming language. We first discuss what is Java some experiments with the Java shell (JShell), and then write and execute our first Java program.

In the second part of this Module we will explore some more Java programming constructs allowing us to write some basic programs.

What is Java?#

Java is a high-level programming language, originally launched in 1995 by Sun Microsystems (which was later acquired by Oracle). Since then, the language and its tools have been expanded and improved significantly.

The term “high-level” means that the language offers programming concepts and tools that are quite detached from the “low-level” details of the underlying computer. This is very different from assembly, where each instruction has a direct correspondence with the computer’s machine instruction; instead, when we write and execute a Java program, the Java code is automatically translated into machine instructions, which are then executed. (This is not 100% accurate, but we will discuss the precise details of how Java works later in the course.)

The main benefits of Java (and most high-level programming languages in general) when compared to assembly are:

  • The code is more readable by humans, since its notation is based on Eglish and mathematics;

  • Programmers can plan their programs by thinking about high-level tasks (e.g., “add \(x\) and \(y\)”) without worrying about low-level details (e.g., “which exact registers are being used to hold \(x\), \(y\), and the result of the addition?)”;

  • The language provides different types of data to avoid confusion and mistakes. We have seen that, under the hood, a computer only deals with numerical values that may have different interpretations; correspondingly, we have seen that a BlinkenBits assembly program only deals with numerical values, and the programmer must keep track of what those values are for — e.g., if the value 10 is in a register, that value might be either a number to be used for mathematical computations, or a memory address, or the symbol a to be shown on the BlinkenBits display. Java programs assign a type to each value, to distinguish how the value can be used: e.g., Java distinguishes a value of type int (integer, usable for mathematical computations) from a value of type char (which represent a symbol like the letter a). This makes programs more readable and easier to maintain.

  • Since the code is not tied to specific machine instructions, the code is also more portable across different computer architectures. For instance, a Java program can run in the same way on an Intel or AMD computer with an x86 processor, and on an Apple computer with an M3 processor, even if their machine instructions are incompatible with each other.

Experimenting with the Java Shell (JShell)#

The Java Shell (JShell) is an interactive tool for learning the Java programming language and prototyping Java code.

Important

The following instructions will only work if you have already installed the software required for the course.

First, you need to open a terminal (a.k.a. console) with the command-line interface.

Search for Git Bash in the start menu, and press (return key).

Alternatively, please follow these instructions (look for the section “Open Git Bash directly in the folder”).

When the terminal starts, write:

jshell -v

and press (return key).

Important

The option -v above stands for “verbose” and makes the Java shell display more informative messages while we use it. This option is needed to see information about types, that we will discuss shortly.

You should now see the following message printed on the terminal (although the reported version may be slightly different):

|  Welcome to JShell -- Version 21.0.4
|  For an introduction type: /help intro

jshell>

Here, jshell> is the Java shell prompt: we can write a snippet of Java code there, which will be executed immediately (unless it contains errors); then we will see the result of the execution, and we will get a new prompt to keep using the shell.

Note

In the JShell examples below, some lines are highlighted, which means: if you write what is on the right of the jshell> prompt on the highlighted line and and press (return), then the Java shell will produce the output that follows.

Some Simple Integer Expressions#

In Java, every value and expression has a type. For example, let us try to perform some calculations with integer values:

jshell> 2 + 3
$1 ==> 5
|  created scratch variable $1 : int

The message above is telling us that the expression 2 + 3 has been executed and it has produced the result 5 — which in turn has been used to create a “scratch variable” called $1; moreover, the variable $1 has a type, which is int (shorthand for “integer”). In other words, the integer value 5 is now stored in a memory location that we can access later (if we need) through the variable name $1.

Note

You can imagine that, in order to execute the example above, the Java shell has translated the expression 2 + 3 into a sequence of machine instructions that perform the desired computation, producing the result 5. If those machine instructions were written in BlinkenBits assembly, they might look as follows:

00: r0 <- 2          // Put the value 2 in register r0
01: r1 <- 3          // Put the value 3 in register r1
02: r2 <- r0 + r1    // Add the values in r0 and r1, put the result in r2
03: r3 <- 10         // Put the value 10 (a memory address) in register r3
04: memory@r3 <- r2  // Store the result of the addition in memory

When writing Java code, we do not need to worry about which exact registers are used for the computation, or which exact memory locations are used to store results: the translation from Java to machine instructions takes care of these aspects.

Continuing the example above, we can now try:

jshell> $1 * 2
$2 ==> 10
|  created scratch variable $2 : int

I.e., we have given the expression $1 * 2 to the Java shell, which proceeds as follows:

  • first, it looks up the value of the variable $1, which is 5;

  • conceptually, Java rewrites the expression $1 * 2 by substituting the occurrence of variable $1 with its value 5 — and this substitution gives the expression 5 * 2;

  • then, it computes the result of the expression 5 * 2, that produces the result 10;

  • finally, it stores the result 10 in a “scratch variable” called $2 of type int.

The “scratch” variable names $1, $2, etc. are automatically selected by the Java shell to let us reuse the results of previous expressions — but we can create variables with (almost) any name we like. Recalling the opening example about computing an area, let us now use the Java shell to compute the area of a rectangle having width 3 and height 7. We can start by declaring some variables with descriptive names:

jshell> var width = 3
width ==> 3
|  created variable width : int
jshell> var height = 7
height ==> 7
|  created variable height : int
jshell> var area = width * height
area ==> 21
|  created variable area : int

In the first highlighted line above, var width = 2 declares a variable called width and having the value 3; in the two lines that follow, JShell tells us that width has value 3 and type int. Then, we declare the variables height and area in a similar way. Notice that we declare the variable area with the result of the expression width * height i.e. by referring to the variables that we have introduced earlier. The value of area is computed by substituting the variables width and height with their respective values: therefore, the expression width * height becomes 3 * 7, which produces the value 21.

Important

If you try to write an expression that references an undefined variable, you will get an error. For example, if you misspell area as aera, you will get:

jshell> aera + 42
|  Error:
|  cannot find symbol
|    symbol:   variable aera
|  aera + 42
|  ^--^

Tip

To see which variables have been declared so far, their type, and their values, you can use the JShell command /vars. If you try it now, you should see an output like:

jshell> /vars
|    int $1 = 5
|    int $2 = 10
|    int width = 3
|    int height = 7
|    int area = 21

This tells us e.g. that area is a variable of type int, and has value 21.

Note that in Java expressions can be nested as one may expect from mathematics. In this case, after a sub-expression is computed, its result is used to compute the surrounding expression. Expression nesting is a common feature of most programming languages (except assembly). For example, consider:

jshell> var twiceArea = (width * height) * 2
twiceArea ==> 42
|  created variable twiceArea : int

When executing the code above, Java proceeds as follows:

  • First, Java evaluates the innermost sub-expression, i.e., witdth * height:

    • Java substitutes he variables width and height with their values, which are respectively 3 and 7. Hence, the sub-expression witdth * height becomes 3 * 7;

    • Java now computes the result of the sub-expression 3 * 7, which is 21.

  • Then, Java uses the result of the sub-expression witdth * height (i.e., 21) to compute the result of the initial expression (width * height) * 2. Therefore, Java computes 21 * 2, which gives the result 42.

The result 42 is finally used to initialise the variable twiceArea.

(We will discuss nested expressions again later, in Remark 7.)

Some Expressions with Numbers Having Fractional Parts#

Continuing the example above, let us now try to compute half of the area of the rectangle:

jshell> area / 2
$6 ==> 10
|  created scratch variable $6 : int

Since the value of area is 21, the result of area / 2 should have been 10.5 — but it has been truncated to 10!

This happens because the arithmetic expression area / 2 only involves values and variables of type int — and consequently, Java computes a result of type int, without the fractional part .5. Correspondingly, the scratch variable $6 gets type int, because that is the type of the result of the expression area / 2.

Let us try instead to divide the area by 2.0 (i.e. we explicitly write the radix point and the fractional part, which in this case is 0):

jshell> var halfArea = area / 2.0
halfArea ==> 10.5
|  created variable halfArea : double

Observe that the result of area / 2.0 is now 10.5, which becomes the value of the variable halfArea; moreover, halfArea has type double, which denotes numbers which may include a fractional part.

The reason is that, in the expression area / 2.0, the value 2.0 has type double — and when a numerical operation contains at least one operand of type double, then the result of the whole operation has also type double, and is computed with the fractional part if needed. Consequently, the result of area / 2.0 has type double and is the value 10.5. Correspondingly, the variable halfArea gets type double, because that is the type of the result of the expression area / 2.0.

Remark 2 (Type promotion)

More technically, what happened to the expression area / 2.0 is an example of automatic type promotion, and the principle is: if a numerical operation involves operands having different types, then the operand with the “less precise” type is promoted (i.e. converted) to the “most precise” type before computing the result. In the example above, the type of the operand 2.0 (which is double) is more precise than the type of area (which is int). Therefore, the value of area (21, of type int) is automatically promoted (to 21.0, of type double) before computing the operation (21.0 / 2.0).

Beyond Numbers: Booleans and Strings#

Java supports values and expressions with a variety of types (and later in the course we will see that we can also define new types).

For instance, Java supports values of type boolean, which can be only true or false. It also provides the operators < (“less than”), > (“greater than”) or == (“equal”) to compare two numbers — and the result of the comparison is a boolean value.

jshell> width < height
$9 ==> true
|  created scratch variable $9 : boolean
jshell> width > height
$10 ==> false
|  created scratch variable $10 : boolean
jshell> (width + 4) == height
$11 ==> true
|  created scratch variable $11 : boolean

Notice that, since true and false are values, we can store them into variables: in fact, the booleans computed in the examples above are stored in (scratch) variables — and of course, we can create our own variables of type boolean:

jshell> var isWidthGreaterThanHeight = width > height
isWidthGreaterThanHeight ==> true
|  created variable isWidthGreaterThanHeight : boolean

Note

The idea that true and false are values that are produced by a computation can also be found in the BlinkenBits comparison instructions: for instance, r1 <- r2 < r3 produces the value 1 (and writes it in r1) when the value in r2 is smaller than the value in r3; otherwise, the comparison produces the value 0 (and writes it in r1). The difference is that Java provides the dedicated keywords true and false to represent boolean values, instead of letting us deal with the numerical values 1 and 0.

Important

When writing Java code, we must be mindful about the types expected by operators and other language constructs. For example, the multiplication operator * and the comparison < only accept operands that are numbers (e.g. of type int or double). Therefore, if we try to use * or < with some operand of type boolean, we get an error. You can see the errors by trying the following examples:

jshell> width * true
|  Error:
|  bad operand types for binary operator '*'
|    first type:  int
|    second type: boolean
|  width * true
|  ^----------^
jshell> isWidthGreaterThanHeight > 42.0
|  Error:
|  bad operand types for binary operator '>'
|    first type:  boolean
|    second type: double
|  isWidthGreaterThanHeight > 42.0
|  ^-----------------------------^
jshell> width < (4 == height)
|  Error:
|  bad operand types for binary operator '<'
|    first type:  int
|    second type: boolean
|  width < (4 == height)
|  ^-------------------^

This type distinction does not exist in BlinkenBits, where every value is a numerical value — so the value 0 is used both as the result of a (false) numerical comparison, and as an integer that can be operand for additions or subtractions. This lack of type distinctions between values is often the source of confusion and mistakes in assembly programs.

Java also supports values of type String, that are sequences of characters. To create a value of type String, we write the desired sequence of characters enclosed between double quotes ". For example:

jshell> var hello = "Hello, I am a String!"
hello ==> "Hello, I am a String!"
|  created variable hello : String

The operator + also works on strings: it can take two strings as operands, and produces a new string obtained by concatenating the first and second operand.

jshell> var hello2 = hello + " And here is more text"
hello2 ==> "Hello, I am a string! And here is more text"
|  created variable hello2 : String

The operator + can also concatenate strings to ints, doubles, or booleans, and produce a new string as a result. For example:

jshell> var message = "The area of the rectangle is " + area
message ==> "The area of the rectangle is 21"
|  created variable message : String

Java’s Primitive Data Types and Numerical Operators#

Table 5 and Table 6 below provide a brief summary of all the primitive data types and numerical operators in Java (some of which we have already seen above). Table 5 also includes examples on how to write literal values of each primitive data type (try writing them on the Java shell!).

These tables are just for reference, and you do not need to know their content by heart. We will use these primitive types and numerical operators throughout the course.

Table 5 Primitive data types in Java#

Type

Size (bits)

Example

Range of allowed values

byte

8

(byte)123

-128 to 127

short

16

(short)12345

-32,768 to 32,767

int

32

-1234567890

-2,147,483,648 to 2,147,483,647

long

64

1234567890123456789L

-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

float

32

3.141592f

+/-3.4E+38F (6-7 significant digits)

double

64

3.141592653589793

+/-1.8E+308 (15 significant digits)

char

16

'A'

All Unicode characters

boolean

8

true

Either true or false

Important

As shown in Table 5, all primitive Java types have limits — for example, a value of type int cannot be smaller than -2147483648, nor larger than 2147483647. This is conceptually similar to the limits in BlinkenBits, where a value held in a register or in a memory location must be between 0 and 4095.

When a computation exceeds the maximum value allowed by its result type, it will overflow and “wrap around” to the minimum allowed value. Vice versa, when a computation yields a result that is smaller than the minimum value allowed by its result type, it will underflow and “wrap around” to the maximum allowed value. For instance, you can try on the Java shell:

jshell> var overflow = 2147483647 + 1
overflow ==> -2147483648
|  created variable overflow : int

jshell> var underflow = -2147483648 - 1
underflow ==> 2147483647
|  created variable underflow : int

In this example, adding 1 to the maximum integer value yields the minimum integer value; vice versa, subtracting 1 from the minimum integer value yields the maximum integer value.

You can observe overflows and underflows in BlinkenBits, too. For example, you can:

  • Overflow the value contained in a register:

    00: r0 <- 05        // Memory addr. containing the max allowed value (see below)
    01: r0 <- memory@r0 // Load maximum allowed value 4095 into r0
    02: r1 <- 1         // Put value 1 into r1
    03: r0 <- r0 + r1   // The result of this addition will be 0 (overflow)
    04: halt
    05: data 4095
    
  • Underflow the value contained in a register:

    00: r0 <- 0         // Put the minimum allowed value 0 into r0
    01: r1 <- 1         // Put value 1 into r1
    02: r0 <- r0 - r1   // The result of this subtraction will be 4095 (underflow)
    03: halt
    

Important

If you try to write a literal numerical value like 1234567890123456789, Java will interpret it as a literal value of type int — and you will get an error, because that value is too big to fit in 32 bits (the size of int values, as shown in Table 5). Try it on the Java shell!

To represent such a big number, Java provides the long type — and to write a literal value of long type, you need to add an L at the end of the number, as shown in the example in Table 5.

Still, the long type also has its limits — so if you add a 0 at the end of the number above and write 12345678901234567890L, you will get an error, because that number does not fit in 64 bits (the size of long values). (Try it on the Java shell!)

Note

Table 5 does not include the type String. This is not an oversight: we will address the topic in Module 2, Part 2: Console I/O, Conditionals, Strings.

Table 6 Numerical operators in Java#

Operator

Example

Description

+

3.14 + 42

Addition

-

3.14 - 42

Subtraction

/

10.0 / 4

Division

*

3.14 * 2

Multiplication

%

13 % 5

Modulo (a.k.a. remainder of the division)

Our First Java Program#

We have used the Java shell to experiment with the Java language — but to write an actual Java program, we need to write Java statements in a text file with extension .java. Let us now create a simple Java program by assembling some of the statements we tried on the Java shell.

 1// Our first Java program!
 2
 3class Area {
 4    public static void main(String[] args) {
 5        var width = 3;
 6        var height = 7;
 7        var area = width * height;
 8        var message = "The area of the rectangle is " + area;
 9
10        System.out.println(message);
11    }
12}

Let us examine the program above in more detail:

  • Line 1 is just a comment: when we write // in Java, everything that follows the slashes until the end of the line is ignored by the language. This is handy e.g. for leaving notes and documentation for the humans who read the program.

  • Lines 5–8 are the same Java statements that we have tried on the Java shell — except that each statement is followed by a semicolon (the semicolon is mandatory, and is used to indicate the end of a statement).

  • On line 10, there is an additional statement: it invokes System.out.println passing the variable message as argument. System.out.println is a facility included in the Java environment: when it is invoked, it displays the value of its argument on the terminal where the program is running. (You can try that on the Java shell, if you wish. We will discuss this topic in more detail later in the course.)

  • All these statements are contained inside the curly brackets {} (lines 4–11) of a method called main: when a Java program is launched, it runs by executing the statements inside main, one by one. This is conceptually similar to BlinkenBits that executes the instructions in memory, one by one. (We will talk more about Java methods later in the course.)

  • The method main, in turn, belongs a class called Area (whose contents are inside the curly brackets {} on lines 3–12). We can choose the class name however we like. (We will talk more about classes later in the course.)

Important

In the Java program above, you can notice that the code is indented:

  • the code belonging to main (lines 5-10) is indented to the right w.r.t. the lines that open and close the definition of main (lines 4 and 11);

  • similarly, the code belonging to Area is indented to the right w.r.t. the lines that open and close the definition of Area (lines 3 and 12).

This indentation is not required by Java, but makes the code much easier to read for us programmers. This will become very important as our programs will become more complex: indentation helps in grasping the code structure at a glance, and navigating the program while working on it.

To write and run this program, you can proceed as follows:

  1. Open a new terminal — or exit from the Java shell by typing /exit.

  2. At the terminal prompt, launch Visual Studio Code by executing:

    code Area.java
    
  3. You should now see the editing window of VS Code: copy&paste the text of the Java program above, and save the file (by pressing Ctrl+S or +S).

    Note

    If VS Code suggests you to install some “Java extensions,” please ignore it.

  4. With the steps above, you have created a new text file called Area.java containing the simple Java program above. To run the program, go back to the terminal from which you launched VS Code, and execute:

    java Area.java
    

If everything goes well, you should see the following output on the terminal:

The area of the rectangle is 21

and then you should see the terminal prompt again: this means that the Java program has completed its execution.

Behind the scenes, the java command has translated the program written in the file Area.java into machine instructions, and those instructions have been executed by the CPU of your computer. (This is not 100% accurate and there is more going on behind the scenes, but we will discuss the precise details of how java works later in the course.)

Concluding Remarks#

You should now have an understanding of the following topics:

  • how to experiment with basic Java statements on the Java shell (JShell);

  • in Java, every value and expression has a type (we have experimented with int, double, boolean and String);

  • how to save a simple Java program in a file, and how to run it.

You should be able to use the Java shell for further experiments, and use the simple Java program above as a blueprint and starting point for writing other simple programs.

Important

If you read other materials about Java programming (including the reference book), you will notice that there is another popular style for declaring variables: instead of writing var x = ..., we can manually specify the type of the variable, by writing the type itself instead of var. Therefore, the simple Java program above can be also written as follows:

 1class Area {
 2    public static void main(String[] args) {
 3        int width = 3;
 4        int height = 7;
 5        int area = width * height;
 6        String message = "The area of the rectangle is " + area;
 7
 8        System.out.println(message);
 9    }
10}

Feel free to adopt the style you like better, or mix the two styles (e.g. declare some variables with var x = ... and others with String y = ...). No matter which style you adopt, keep in mind that all variables and values have a type, as seen during our experiments with the Java shell.

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 1.4 - “The Java Programming Language”

  • Section 2.3 - “Primitive Data Types”

  • Section 2.4 - “Expressions”

The following appendix of the reference book may be also useful:

  • Appendix C - “The Unicode Character Set”

The Java documentation includes a Java Shell User’s Guide (Note: it mentions Java features that we will address later in the course).

Exercises#

Note

These exercises are not part of the course assessment. They are provided to help you self-test your understanding and practice with Java.

Exercise 19 (What is the type?)

Read the following Java expressions, and figure out what is the type of each one. To verify whether your answers are correct, you can execute each expression on the Java shell.

Important

Don’t forget to use the option -v when you launch jshell -v, otherwise the Java shell will not display the type information.

  • 58

  • true

  • -23

  • "afd "

  • 42.0

  • "42.0"

  • "true"

  • 1.5 * 60.0

  • 1.5 * 60

  • 24 * 60

  • 1.1 + 60 - 1

  • 150.0 / 60

  • 150 / 60

  • 2 + 2

  • "2" + 3

  • 2 + "3"

  • "Apollo " + (5 * 3 + 2) +" was the last crewed mission to the Moon"

Exercise 20 (Causing errors)

Edit the simple Java program above by applying one or more of the changes below: each change introduces an error in the program. After each change, save the modified file Area.java, and try to run it, by executing java Area.java on the terminal: observe how each change causes a different error.

  • Remove one of the semicolons

  • Remove one of the curly brackets

  • On line 4, change main into maiiin

  • On line 3, change the name of the variable width into w

  • On line 3, change the initialisation value from 3 (which has type int) into "3" (which has type String)

  • On line 8, remove the first or last double-quote "

  • On line 10, change println into bogus

Lab and Weekly Assessments#

During the lab you can try the exercises above or begin your work on the weekly assessments below.

Important

01 - Hello, World!#

Edit the file Hello.java provided in the handout to make it display the sentence Hello, World! on the terminal. Submit the modified file Hello.java on DTU Autolab.

Hint

  • In the file Hello.java (provided in the handout) there is a comment where you should write your Java code.

  • You should use System.out.println(...), as in the simple Java program above.

  • Make sure that the program still runs without errors after your changes, by executing in a terminal (in the handout directory):

    java Hello.java
    
  • Before submitting your solution on DTU Autolab, you can grade it on your own computer, by executing in a terminal (in the handout directory):

    ./grade
    

    When the resulting messages say “For details, see: file:/…”, then you can open the URL beginning with file:/... in a browser to see more details.

02 - Taxes#

The file Taxes.java provided in the handout computes and displays the labour market contribution and the special pension savings of a person with an income of DKK 120000.

Edit the file Taxes.java to make it also compute and display the basic tax, as described below. Then, submit the modified file Taxes.java on DTU Autolab.

The basic tax is computed with the following formula:

\[ (\mathit{income} - (\mathit{contrib} + \mathit{pension} + \mathit{allowance})) \times 6\% \]

where:

  • \(\mathit{contrib}\) is the labour market contribution (already computed in Taxes.java);

  • \(\mathit{pension}\) is the special pension savings (already computed in Taxes.java);

  • \(\mathit{allowance}\) is a personal allowance that (for this exercise) must be equal to DKK 33400.

Hint

  • See the hints provided for the assessment 01 - Hello, World!

  • To see what the modified Taxes.java is expected to output on the terminal, you can execute, in the handout directory:

    ./grade
    

    You will see a message that says “For details, see: file:/…”: you can open the URL beginning with file:/... in a browser to see more details.