A Java statement is a minimal construct that can be executed. It describes an action and ends with a semicolon (;
). We have seen many statements already. For example, here are three statements:
float f = 23.42f;
String sf = String.valueOf(f);
System.out.println(sf);
The first line is a declaration statement combined with an assignment statement. The second line is also a declaration statement combined with an assignment statement and method invocation statement. The third line is just a method invocation statement.
Here is a list of Java statement types:
- An empty statement that consists of only one symbol,
;
(semicolon)
- A class or interface declaration statement (we will talk about this in Chapter 2, Java Object-Oriented Programming (OOP))
- A local variable declaration statement:
int x
;
- A synchronized statement: this is beyond the scope of this book
- An expression statement
- A control flow statement
An expression statement can be one of the following:
- A method invocation statement:
someMethod();
- An assignment statement:
n = 23.42f;
- An object creation statement:
new String("abc");
- A unary increment or decrement statement:
++x ; or --x; or x++; or x--;
We will talk more about expression statements in the Expression statements section.
A control flow statement can be one of the following:
- A selection statement:
if-else
or switch-case
- An iteration statement:
for
, or while
, or do-while
- An exception-handling statement:
throw
, try-catch
, or try-catch-finally
- A branching statement:
break
, continue
, or return
We will talk more about control statements in the Control flow statements section.
Expression statements
An expression statement consists of one or more expressions. An expression typically includes one or more operators. It can be evaluated, which means it can produce a result of one of the following types:
- A variable:
x = 1
, for example
- A value:
2*2
, for example
It returns nothing when the expression is an invocation of a method that returns void
. Such a method is said to produce only a side effect: void someMethod()
, for example.
Consider the following expression:
x = y++;
The preceding expression assigns a value to an x
variable and has a side effect of adding 1 to the value of the y
variable.
Another example would be a method that prints a line, like this:
System.out.println(x);
The println()
method returns nothing and has a side effect of printing something.
By its form, an expression can be one of the following:
- A primary expression: a literal, a new object creation, a field or method access (invocation).
- A unary operator expression:
x++
, for example.
- A binary operator expression:
x*y
, for example.
- A ternary operator expression:
x > y ? true : false
, for example.
- A lambda expression:
x -> x + 1
(see Chapter 14, Java Standard Streams).
- If an expression consists of other expressions, parentheses are often used to identify each of the expressions clearly. This way, it is easier to understand and to set the expressions’ precedence.
Control flow statements
When a Java program is executed, it is executed statement by statement. Some statements have to be executed conditionally, based on the result of an expression evaluation. Such statements are called control flow statements because, in computer science, a control flow (or flow of control) is the order in which individual statements are executed or evaluated.
A control flow statement can be one of the following:
- A selection statement:
if-else
or switch-case
- An iteration statement:
for
, while
, or do-while
- An exception-handling statement:
throw
, try-catch
, or try-catch-finally
- A branching statement:
break
, continue
, or return
Selection statements
Selection statements are based on an expression evaluation and have four variations, as outlined here:
if
(expression) {do something}
if
(expression) {do something} else
{do something else}
if
(expression) {do something} else if
{do something else} else
{do something else}
switch...case
statement
Here are some examples of if
statements:
if(x > y){
//do something
}
if(x > y){
//do something
} else {
//do something else
}
if(x > y){
//do something
} else if (x == y){
//do something else
} else {
//do something different
}
A switch...case
statement is a variation of an if...else
statement, as illustrated here:
switch(x){
case 5: //means: if(x = 5)
//do something
break;
case 7:
//do something else
break;
case 12:
//do something different
break;
default:
//do something completely different
//if x is not 5, 7, or 12
}
As you can see, the switch...case
statement forks the execution flow based on the value of the variable. The break
statement allows the switch...case
statement to be executed. Otherwise, all the following cases would be executed.
In Java 14, a new switch...case
statement has been introduced in a less verbose form, as illustrated here:
void switchStatement(int x){
switch (x) {
case 1, 3 -> System.out.print("1 or 3");
case 4 -> System.out.print("4");
case 5, 6 -> System.out.print("5 or 6");
default -> System.out.print("Not 1,3,4,5,6");
}
System.out.println(": " + x);
}
As you can see, it uses an arrow (->
) and does not use a break
statement.
Execute the main()
method of the com.packt.learnjava.ch01_start.ControlFlow
class—see the selection()
method that calls the switchStatement()
method with different parameters, as follows:
switchStatement(1); //prints: 1 or 3: 1
switchStatement(2); //prints: Not 1,3,4,5,6: 2
switchStatement(5); //prints: 5 or 6: 5
You can see the results from the comments.
If several lines of code have to be executed in each case, you can just put braces ({}
) around the block of code, as follows:
switch (x) {
case 1, 3 -> {
//do something
}
case 4 -> {
//do something else
}
case 5, 6 -> System.out.println("5 or 6");
default -> System.out.println("Not 1,3,4,5,6");
}
The Java 14 switch...case
statement can even return a value, thus becoming in effect a switch
expression. For example, here is a case when another variable has to be assigned based on the switch...case
statement result:
void switchExpression1(int i){
boolean b = switch(i) {
case 0, 1 -> false;
case 2 -> true;
default -> false;
};
System.out.println(b);
}
If we execute the switchExpression1()
method (see the selection()
method of the com.packt.learnjava.ch01_start.ControlFlow
class), the results are going to look like this:
switchExpression1(0); //prints: false
switchExpression1(1); //prints: false
switchExpression1(2); //prints: true
The following example of a switch
expression is based on a constant:
static final String ONE = "one", TWO = "two", THREE = "three",
FOUR = "four", FIVE = "five";
void switchExpression2(String number){
var res = switch(number) {
case ONE, TWO -> 1;
case THREE, FOUR, FIVE -> 2;
default -> 3;
};
System.out.println(res);
}
If we execute the switchExpression2()
method (see the selection()
method of the com.packt.learnjava.ch01_start.ControlFlow
class), the results are going to look like this:
switchExpression2(TWO); //prints: 1
switchExpression2(FOUR); //prints: 2
switchExpression2("blah"); //prints: 3
Here’s yet another example of a switch
expression, this time based on the enum
value:
enum Num { ONE, TWO, THREE, FOUR, FIVE }
void switchExpression3(Num number){
var res = switch(number) {
case ONE, TWO -> 1;
case THREE, FOUR, FIVE -> 2;
};
System.out.println(res);
}
If we execute the switchExpression3()
method (see the selection()
method of the com.packt.learnjava.ch01_start.ControlFlow
class), the results are going to look like this:
switchExpression3(Num.TWO); //prints: 1
switchExpression3(Num.FOUR); //prints: 2
//switchExpression3("blah"); //does not compile
In case a block of code has to be executed based on a particular input value, it is not possible to use a return
statement because it is reserved already for the returning value from a method. That is why, to return a value from a block, we have to use a yield
statement, as shown in the following example:
void switchExpression4(Num number){
var res = switch(number) {
case ONE, TWO -> 1;
case THREE, FOUR, FIVE -> {
String s = number.name();
yield s.length();
}
};
System.out.println(res);
}
If we execute the switchExpression4()
method (see the selection()
method of the com.packt.learnjava.ch01_start.ControlFlow
class), the results are going to look like this:
switchExpression4(Num.TWO); //prints: 1
switchExpression4(Num.THREE); //prints: 5
Iteration statements
An iteration statement can take one of the following three forms:
- A
while
statement
- A
do...while
statement
- A
for
statement, also called a loop
statement
A while
statement looks like this:
while (boolean expression){
//do something
}
Here is a specific example (execute the main()
method of the com.packt.learnjava.ch01_start.ControlFlow
class—see the iteration()
method):
int n = 0;
while(n < 5){
System.out.print(n + " "); //prints: 0 1 2 3 4
n++;
}
In some examples, instead of the println()
method, we use the print()
method, which does not feed another line (does not add a line feed control at the end of its output). The print()
method displays the output in one line.
A do...while
statement has a very similar form, as we can see here:
do {
//do something
} while (boolean expression)
It differs from a while
statement by always executing the block of statements at least once before evaluating the expression, as illustrated in the following code snippet:
int n = 0;
do {
System.out.print(n + " "); //prints: 0 1 2 3 4
n++;
} while(n < 5);
As you can see, it behaves the same way when the expression is true
at the first iteration. But if the expression evaluates to false
, the results are different, as we can see here:
int n = 6;
while(n < 5){
System.out.print(n + " "); //prints nothing
n++;
}
n = 6;
do {
System.out.print(n + " "); //prints: 6
n++;
} while(n < 5);
for
statement syntax looks like this:
for(init statements; boolean expression; update statements) {
//do what has to be done here
}
Here is how a for
statement works:
init
statements initialize a variable.
- A Boolean expression is evaluated using the current variable value: if
true
, the block of statements is executed; otherwise, the for
statement exits.
update
statements update the variable, and the Boolean expression is evaluated again with this new value: if true
, the block of statements is executed; otherwise, the for
statement exits.
- Unless exited, the final step is repeated.
As you can see here, if you aren’t careful, you can get into an infinite loop:
for (int x = 0; x > -1; x++){
System.out.print(x + " "); //prints: 0 1 2 3 4 5 6 ...
}
So, you have to make sure that the Boolean expression guarantees eventual exit from the loop, like this:
for (int x = 0; x < 3; x++){
System.out.print(x + " "); //prints: 0 1 2
}
The following example demonstrates multiple initialization and update
statements:
for (int x = 0, y = 0; x < 3 && y < 3; ++x, ++y){
System.out.println(x + " " + y);
}
And here is a variation of the preceding code for statements for demonstration purposes:
for (int x = getInitialValue(), i = x == -2 ? x + 2 : 0,
j = 0; i < 3 || j < 3 ; ++i, j = i) {
System.out.println(i + " " + j);
}
If the getInitialValue()
method is implemented like int getInitialValue(){ return -2; }
, then the preceding two for
statements produce exactly the same results.
To iterate over an array of values, you can use an array index, like so:
int[] arr = {24, 42, 0};
for (int i = 0; i < arr.length; i++){
System.out.print(arr[i] + " "); //prints: 24 42 0
}
Alternatively, you can use a more compact form of a for
statement that produces the same result, as follows:
int[] arr = {24, 42, 0};
for (int a: arr){
System.out.print(a + " "); //prints: 24 42 0
}
This last form is especially useful with a collection, as shown here:
List<String> list = List.of("24", "42", "0");
for (String s: list){
System.out.print(s + " "); //prints: 24 42 0
}
We will talk about collections in Chapter 6, Data Structures, Generics, and Popular Utilities.
Exception-handling statements
In Java, there are classes called exceptions that represent events that disrupt the normal execution flow. They typically have names that end with Exception
: NullPointerException
, ClassCastException
, ArrayIndexOutOfBoundsException
, to name but a few.
All the exception classes extend the java.lang.Exception
class, which, in turn, extends the java.lang.Throwable
class (we will explain what this means in Chapter 2, Java Object-Oriented Programming (OOP)). That’s why all exception objects have common behavior. They contain information about the cause of the exceptional condition and the location of its origination (line number of the source code).
Each exception object can be generated (thrown) either automatically by the JVM or by the application code, using the throw
keyword. If a block of code throws an exception, you can use a try-catch
or try-catch-finally
construct to capture the thrown exception object and redirect the execution flow to another branch of code. If the surrounding code does not catch the exception object, it propagates all the way out of the application into the JVM and forces it to exit (and abort the application execution). So, it is good practice to use try-catch
or try-catch-finally
in all the places where an exception can be raised and you do not want your application to abort execution.
Here is a typical example of exception handling:
try {
//x = someMethodReturningValue();
if(x > 10){
throw new RuntimeException("The x value is out
of range: " + x);
}
//normal processing flow of x here
} catch (RuntimeException ex) {
//do what has to be done to address the problem
}
In the preceding code snippet, normal processing flow will be not executed in the case of x > 10
. Instead, the do what has to be done
block will be executed. But, in the x <= 10
case, the normal processing flow block will be run and the do what has to be done
block will be ignored.
Sometimes, it is necessary to execute a block of code anyway, whether an exception was thrown/caught or not. Instead of repeating the same code block in two places, you can put it in a finally
block, as follows (execute the main()
method of the com.packt.learnjava.ch01_start.ControlFlow
class—see the exception()
method):
try {
//x = someMethodReturningValue();
if(x > 10){
throw new RuntimeException("The x value is out
of range: " + x);
}
//normal processing flow of x here
} catch (RuntimeException ex) {
System.out.println(ex.getMessage());
//prints: The x value is out of range: ...
//do what has to be done to address the problem
} finally {
//the code placed here is always executed
}
We will talk about exception handling in more detail in Chapter 4, Exception Handling.
Branching statements
Branching statements allow breaking of the current execution flow and continuation of execution from the first line after the current block or from a certain (labeled) point of the control flow.
A branching statement can be one of the following:
We have seen how break
was used in switch-case
statements. Here is another example (execute the main()
method of the com.packt.learnjava.ch01_start.ControlFlow
class—see the branching()
method):
String found = null;
List<String> list = List.of("24", "42", "31", "2", "1");
for (String s: list){
System.out.print(s + " "); //prints: 24 42 31
if(s.contains("3")){
found = s;
break;
}
}
System.out.println("Found " + found); //prints: Found 31
If we need to find the first list element that contains "3"
, we can stop executing as soon as the s.contains("3")
condition is evaluated to true
. The remaining list elements are ignored.
In a more complicated scenario, with nested for
statements, it is possible to set a label (with a : column
) that indicates which for
statement has to be exited, as follows:
String found = null;
List<List<String>> listOfLists = List.of(
List.of("24", "16", "1", "2", "1"),
List.of("43", "42", "31", "3", "3"),
List.of("24", "22", "31", "2", "1")
);
exit: for(List<String> l: listOfLists){
for (String s: l){
System.out.print(s + " "); //prints: 24 16 1 2 1 43
if(s.contains("3")){
found = s;
break exit;
}
}
}
System.out.println("Found " + found); //prints: Found 43
We have chosen a label name of exit
, but we could call it any other name too.
A continue
statement works similarly, as follows:
String found = null;
List<List<String>> listOfLists = List.of(
List.of("24", "16", "1", "2", "1"),
List.of("43", "42", "31", "3", "3"),
List.of("24", "22", "31", "2", "1")
);
String checked = "";
cont: for(List<String> l: listOfLists){
for (String s: l){
System.out.print(s + " ");
//prints: 24 16 1 2 1 43 24 22 31
if(s.contains("3")){
continue cont;
}
checked += s + " ";
}
}
System.out.println("Found " + found); //prints: Found 43
System.out.println("Checked " + checked);
//prints: Checked 24 16 1 2 1 24 22
It differs from break
by stating which of the for
statements need to continue and not exit.
A return
statement is used to return a result from a method, as follows:
String returnDemo(int i){
if(i < 10){
return "Not enough";
} else if (i == 10){
return "Exactly right";
} else {
return "More than enough";
}
}
As you can see, there can be several return
statements in a method, each returning a different value in different circumstances. If the method returns nothing (void
), a return
statement is not required, although it is frequently used for better readability, as follows:
void returnDemo(int i){
if(i < 10){
System.out.println("Not enough");
return;
} else if (i == 10){
System.out.println("Exactly right");
return;
} else {
System.out.println("More than enough");
return;
}
}
Execute the returnDemo()
method by running the main()
method of the com.packt.learnjava.ch01_start.ControlFlow
class (see the branching()
method). The results are going to look like this:
String r = returnDemo(3);
System.out.println(r); //prints: Not enough
r = returnDemo(10);
System.out.println(r); //prints: Exactly right
r = returnDemo(12);
System.out.println(r); //prints: More than enough
Statements are the building blocks of Java programming. They are like sentences in English—complete expressions of intent that can be acted upon. They can be compiled and executed. Programming is like expressing an action plan in statements.
With this, the explanation of the basics of Java is concluded. Congratulations on getting through it!