The if statement

Lesson content

  • What is the if statement – declaration and elements
  • Comparison operators
  • Alternative ways of writing if statements
  • Logical operators and their precedence, compound conditions
  • Unsatisfiable (contradiction) logical expression
  • Always true (tautology) logical expression
  • Nested if statements
  • Most common errors

What is the if statement – declaration and elements

Programmers often encounter situations where it is necessary to check a condition and, depending on whether it is met, do one thing or another. For example, it might be necessary to check whether an entered height falls within the range of 80 cm to 240 cm, and if it does not, display an error message.

This can be accomplished using a branching algorithmic structure, that is, the if control statement as implemented in Java. The declaration of an if statement is done as follows:

1 if ( ...condition... ) command_1;
2 else command_2;

The declaration begins with the reserved word if (lowercase). After that, within parentheses, you write the logical condition to be checked. After the condition comes the first branch (the “YES” branch), where you write the command to be executed if the condition is true (command_1). Then comes the second branch (the “NO” branch), which starts with the reserved word “else” and includes the command to be executed if the condition is not true (command_2).

It is important to note that the if statement does not need to have both branches. If not required, it can have only the “YES” or only the “NO” branch.

Comparison operators

The condition written in the parentheses of an if statement represents a logical expression that can be evaluated as true or false. In the simplest case, this expression checks whether one number is greater than, less than, or equal to another. This is done using comparison operators, as shown in the following table:

Operator Description Examples
> Greater than a > b, x > 2
< Less than a < b, x < 2
>= Greater or equal a >= b, x >= 135.5
<= Less or equal a <= b, x <= 13
== Equal (two equals signs) a == b, x == 45.22
!= Not equal a != b, x != 1

Comparison operators can only be used with primitive data types, as they don’t work when comparing objects like Strings, LocalDateTime or from any other class (Person, Car, etc.). If you try to compare two objects using “==”, you are comparing their memory addresses, not their content.

It’s also important to note that the operators “=” and “==” are not the same. The first is the assignment operator, while the second is the equality comparison operator. Beginners often confuse these two, so pay close attention.

Example 1

Create a class IntegerCheck that has:

  • A method checkSign that checks whether a passed integer is positive, negative, or zero, and prints the result in the format: “Number #### is ####.”
  • A method checkLessGreaterEqual that takes two integers A and B as parameters and checks whether A > B, A < B, or A == B, and prints the result accordingly.
 1     class IntegerCheck {
 2 
 3         void checkSign(int a){
 4             if (a == 0)
 5                 System.out.println("Number "+a+" is equal to zero");
 6 
 7             if (a > 0)
 8                 System.out.println("Number "+a+" greater than zero");
 9 
10             if (a < 0)
11                 System.out.println("Number "+a+" less than zero");
12         }
13 
14         void checkLessGreaterEqual (int a, int b){
15             if (a > b)
16                 System.out.println("Number "+a+" is greater than number "+b);
17 
18             if (a == b)
19                 System.out.println("Number "+a+" is equal to number "+b);
20 
21             if (a < b)
22                 System.out.println("number "+a+" is less than number "+b);
23         }
24     }

Create a TestIntegerCheck class with a main method that uses the IntegerCheck class to compare 23 and -17 and check the sign of -55.

 1 class TestIntegerCheck {
 2 
 3     public static void main(String[] args) {
 4         IntegerCheck ic = new IntegerCheck();
 5 
 6         ic.checkLessGreaterEqual(23, -17);
 7 
 8         ic.checkSign(-55);
 9     }
10 }

Each condition in the checkSign method is checked independently using three separate if statements, without else branches. That means all conditions are always checked, regardless of previous outcomes.

The condition checked in the first command is whether the number is equal to zero. If it is, the command in the extension will be executed, i.e., a message will be displayed on the screen stating that the number is equal to zero. If the condition is not met, i.e., if the number is not equal to zero, nothing happens because there is no “else” branch (no message will be displayed on the screen).
Then, the program moves on to the next IF statement, which checks if the number is greater than zero.
If it is, a message about it will be displayed. If the condition is not met, nothing happens because there is no “else” branch, and the program moves on to the third IF statement, which checks if the number is less than zero and displays a message about it only if the condition is met.

In the checkLessGreaterEqual method, the same approach is used: three independent if statements without else branches.

In the previous example, the formatting of the code is again shown, specifically “breaking” Java commands into multiple lines and indentation. In other words, an IF statement can be written across multiple lines to improve code readability:

1 if ( ...condition... )
2     command_1;

This is useful in situations where the condition or the command that follows it is quite long, so it cannot fit on a single line in the code editor (on the screen), or cannot be fully seen without scrolling the text or resizing the editor.

Also, it’s important to note that command_1 is indented to the right relative to the reserved word if (indentation). This makes it easier to see that this command is written within the IF statement.

As already mentioned, the IF statement can have a second branch (the “NO” branch), i.e., an “else” part (but it’s not mandatory). After the reserved word else, you write the command that is executed only if the condition is not met (command_2). The declaration of an IF statement with an “else” part looks like this:

1 if ( ...condition... ) command_1;
2 else command_2;

If the code formatting principles (breaking into multiple lines and indentation) are applied here, the statement can also be written like this:

1 if ( ...condition... )
2     command_1;
3 else
4     command_2;

Example 2

Create a class IntegerCheck2 that has:

  • A method notEqual that takes two numbers as parameters and returns TRUE if the numbers are not equal, and FALSE otherwise.
  • A method isEven that takes an integer as a parameter and checks whether it is even or odd. The method returns TRUE if the number is even, and FALSE if it is odd.
 1     class IntegerCheck2 {
 2 
 3         boolean isNotEqual(int a, int b){
 4             if (a != b)
 5                 return true;
 6             else
 7                 return false;
 8         }
 9 
10         boolean isEven(int a){
11             if ((a%2) == 0)
12                 return true;
13             else
14                 return false;
15         }
16     }

Create a class TestIntegerCheck2 which, in the main method, uses the IntegerCheck2 class to compare the numbers 12 and 13, and checks the parity of the number 33.

 1 class TestIntegerCheck2 {
 2 
 3     public static void main(String[] args) {
 4         IntegerCheck2 ic = new IntegerCheck2();
 5 
 6         boolean notEqual = ic.isNotEqual(12, 13);
 7         if (notEqual)
 8             System.out.println("The numbers are not equal");
 9         else
10             System.out.println("The numbers are equal");
11 
12         boolean even = ic.isEven(33);
13         if (even)
14             System.out.println("Number 33 is even");
15         else
16             System.out.println("Number 33 is odd");
17     }
18 }

In the isEven method, it shows how to check whether a number is even or odd. If the remainder when dividing the number by two is zero, the number is even; otherwise, it is odd.

It’s important to note that the IF statements in both methods also have an else branch, because in these cases, there are commands that need to be executed if the condition is not met.

The reason is that these methods always have to return a boolean value, so if the “YES” branch doesn’t execute and returns true, the “NO” branch (the else part) will definitely be executed and will return false. Without this else branch, there could be a situation where the method would not return anything if the condition is not met — neither true nor false — which would be syntactically incorrect.

Alternative ways of writing if statements

Each IF statement can usually be written in multiple ways (at least two), while still having the exact same functionality, but with the code looking different. This is typically achieved through two steps:

  • Writing the condition in a different way (reverse condition, “negation” of the original condition)
  • Then, swapping the “YES” and “NO” branches.

It is often necessary to do both things to maintain the same functionality of the IF statement. So, the original IF statement:

1 if ( ...condition... )
2     command_1;
3 else
4     command_2;

Could become the alternative IF statement (notice that command_1 and command_2 have swapped branches):

1 if ( ...reversed condition... )
2     command_2;
3 else
4     command_1;

Writing the condition in a different way, i.e., its “negation,” can, in the simplest case, be reduced to swapping the comparison operators:

  • If the condition uses the equal to (==) operator, it is replaced with the not equal to (!=) operator, for example:
1     (x == 2) becomes (x != 2)
2     (a == f) becomes (a != f)
  • If the condition uses the greater than (>) operator, it is replaced with the less than or equal to (<=) operator, for example:
1     (x > 2) becomes (x <= 2)
2     (a > f) becomes (a <= f)
  • If the condition uses the less than (<) operator, it is replaced with the greater than or equal to (>=) operator, for example:
1     (x < 2) becomes (x >= 2)
2     (a < f) becomes (a >= f)

The same applies in reverse, so:

  • If the condition uses the not equal to (!=) operator, it is replaced with the equal to (==) operator, for example:
1     (x != 2) becomes (x == 2)
2     (a != f) becomes (a == f)
  • If the condition uses the greater than or equal to (>=) operator, it is replaced with the less than (<) operator, for example:
1     (x >= 2) becomes (x < 2)
2     (a >= f) becomes (a < f)
  • If the condition uses the less than or equal to (<=) operator, it is replaced with the greater than (>) operator, for example:
1     (x <= 2) becomes (x > 2)
2     (a <= f) becomes (a > f)

Example 3

Create a class IntegerCheck2A that has the same methods as the IntegerCheck2 class, but with differently written IF statements — using reversed conditions and swapped branches.

 1 class IntegerCheck2A {
 2 
 3     boolean isNotEqual(int a, int b){
 4         //Instead of this if statement
 5         //if (a != b) return true;
 6         //else return false;
 7 
 8         //You can use this alternative if statement
 9         if (a == b) return false;
10         else return true;
11     }
12 
13     boolean isEven (int a){
14         //Instead of this if statement
15         //if ((a%2) == 0) return true;
16         //else return false;
17 
18         //This alternative if statement can be used
19         if ((a%2) != 0) return false;
20         else return true;
21     }
22 }

Create a class TestIntegerCheck2A which, in the main method, uses the IntegerCheck2A class to compare the numbers 12 and 13, and checks the parity of the number 33.

 1 class TestIntegerCheck2A {
 2 
 3     public static void main(String[] args) {
 4         IntegerCheck2A ic = new IntegerCheck2A();
 5 
 6         boolean notEqual = ic.isNotEqual(12, 13);
 7         if (notEqual)
 8             System.out.println("The numbers are not equal");
 9         else
10             System.out.println("The numbers are equal");
11 
12         boolean even = ic.isEven(33);
13         if (even)
14             System.out.println("Number 33 is even");
15         else
16             System.out.println("Number 33 is odd");
17     }
18 }

In the isNotEqual method, you can see that instead of checking the condition (a != b)— whether a is different from b - the reversed condition (its negation) (a == b) is checked — whether a is equal to b. The “not equal” (!=) operator is replaced with the “equal” (==) operator.

To ensure the IF statement still functions the same, it is necessary to swap the “YES” and “NO” branches, i.e., if (a == b) is true, the method now returns false because the numbers are not different; if not, it returns true because the numbers are indeed different.

Similarly, for the isEven method, it now checks whether the remainder of division is different from zero ((a % 2) != 0) (the “equal” operator is replaced with the “not equal” operator), and the branches are also reversed.

There is another possibility for writing an IF statement alternatively, but with a slightly different use of the return statement. As already mentioned, besides returning a value from a method, the return statement immediately terminates the execution of that method. This sometimes means that the “NO” (else) branch does not have to be explicitly mentioned, and the code can be written outside the IF statement, i.e., in the continuation after the “YES” branch (which contains the return statement). If the “YES” branch is executed, the method will terminate, and if it is not executed, the code will continue in the continuation.

Example 4

Create a class IntegerCheck2AA that has the same methods as the IntegerCheck2 class, but with differently written IF statements — without the else branch, using the return statement.

 1 class IntegerCheck2AA {
 2 
 3     boolean isNotEqual (int a, int b){
 4         //Instead of this
 5         //if (a != b) return true;
 6         //else return false;
 7 
 8         // It can be written like this
 9         // (without the else branch) because
10         // the return command terminates the method
11         if (a != b) return true;
12 
13         return false;
14     }
15 
16     boolean isEven(int a){
17         //Instead of this
18         //if ((a%2) == 0) return true;
19         //else return false;
20 
21         // It can be written like this
22         // (without the else branch) because
23         // the return command terminates the method
24         if ((a%2) == 0) return true;
25 
26         return false;
27     }
28 }

Create a class TestIntegerCheck2AA which, in the main method, uses the IntegerCheck2AA class to compare the numbers 12 and 13, and checks the parity of the number 33.

 1 class TestIntegerCheck2AA {
 2 
 3     public static void main(String[] args) {
 4         IntegerCheck2AA p = new IntegerCheck2AA();
 5 
 6         boolean notEqual = p.isNotEqual(12, 13);
 7         if (notEqual)
 8             System.out.println("The numbers are not equal");
 9         else
10             System.out.println("The numbers are equal");
11 
12         boolean even = p.isEven(33);
13         if (even)
14             System.out.println("Number 33 is even");
15         else
16             System.out.println("Number 33 is odd");
17     }
18 }

Here, it’s important to note that the IF statements do not have the “NO” branch (else part), yet the code behaves exactly the same. In the isNotEqual method, if the numbers are different, the method executes the return statement inside the “YES” branch, returns true, and terminates. In other words, the “return false;” statement written below will not be executed. Similarly, if the numbers are the same, the “YES” branch does not execute, and the program exits the IF statement (condition not met) and moves on to the next statement in the block, i.e., “return false;”.

It is also important to mention that the return statement can be used even when the method has no return value (void) in order to terminate the method. In this case, the return statement is written without specifying a value or variable name:

1 return;

In the context of the IF statement, this means that alternative writing without the “NO” branch can be achieved through the return statement, even though the method does not have a return value.

Finally, if an IF statement checks the value of a logical (boolean) variable, the condition can be shortened, resulting in an alternatively written condition. You only need to specify the variable name in the condition. For example, instead of:

1 boolean ind = ...;
2 
3 if (ind == true) 
4     command_1;
5 else
6     command_2;

The IF statement condition can be written more succinctly (but only because the variable ind is of boolean type):

1 boolean ind = ...;
2 
3 if (ind) 
4     command_1;
5 else
6     command_2;

The reason for this is that the expressions (ind == true) and (ind) are logically equivalent. In other words:

  • If the variable ind has the value true, the expression (ind == true) will evaluate as true because both sides of the expression are the same (true == true -> true).
  • If the variable ind has the value false, the expression (ind == true) will evaluate as false because both sides of the expression are different (false == true -> false).

The examples so far cover cases where only one command needs to be executed after the condition is checked. In the case where multiple commands need to be executed after the condition is checked, the commands must be enclosed in a block of commands using curly braces:

 1 if ( ...condition... ) {
 2     command_1_1;
 3     command_1_2;
 4     ...
 5     command_1_n;
 6 }
 7 else {
 8     command_2_1;
 9     command_2_2;
10     ...
11     command_2_m;
12 }

Example 5

Create a class DecimalCheck that has:

  • A method lessThanPi that takes a real number as a parameter and returns TRUE if the number is smaller than 3.141592 (Pi), and FALSE otherwise. In both cases, a message should be printed on the screen indicating whether the number is smaller than Pi or not.
 1     class DecimalCheck {
 2 
 3         boolean lessThanPi(double x){
 4             if (x < 3.141592) {
 5                 System.out.println("Number "+x+" is less than Pi");
 6                 return true;
 7             }
 8             else {
 9                 System.out.println("Number "+x+
10                     " is greater than or equal to Pi");
11                 return false;
12             }
13         }
14 
15     }

Create a class TestDecimalCheck that in the main method, using the DecimalCheck class, checks whether the number 3.333 is smaller than Pi or not.

1 class TestDecimalCheck {
2 
3     public static void main(String[] args) {
4         DecimalCheck dc = new DecimalCheck();
5 
6         dc.lessThanPi(3.333);
7     }
8 }

The lessThanPi method contains an IF statement that executes two commands if the condition is met and two others if it is not. These commands must be enclosed in a block of commands using curly braces. The block of commands for the “YES” branch starts immediately after the condition and ends before the reserved word else, while the block of commands for the “NO” branch starts after the reserved word else.

Here, code formatting is also applied to enhance readability, so these two blocks of commands are “indented” to the right relative to the reserved words if and else.

The previous example also highlights another concept. In addition to returning a value using the return statement, the execution of the method is also instantly terminated. In other words, every time the return command is executed, the method stops, and all commands written after (below) this command are not executed. Therefore, it’s important to make sure that the return statement is the last command in a block of commands, otherwise Java will report a syntax error.

Logical operators and their precedence, compound conditions

The condition being checked in an IF statement can also be complex, meaning it consists of several simpler conditions. Forming more complex conditions is done by using logical operators: AND (&&), OR (||), and NOT (!), which originate from the mathematical field of predicate logic - Boolean Algebra. The specification of these operators is given in the following table.

Operator Description Examples
! NOT (negation) !(a > b) — a must not be greater than b for the condition to be true.
&& AND (a > 2) && (a < 5) — a must be greater than 2 and less than 5 for the condition to be true.
㆐㆐ OR (x < 0) ㆐㆐ (x > 33.3) — x must be less than 0 or greater than 33.3 (any one or both) for the condition to be true.

It is important to note that forming complex conditions is done in a similar way as in natural language. Often, it is enough to simply state the condition, and it can be directly implemented in Java by connecting several simple conditions using logical operators.

Similar to arithmetic operators, each logical operator has a priority, with NOT (!) being the highest, followed by AND (&&), and OR (||) having the lowest priority, as indicated in the table above.

This practically means that adding extra parentheses around parts of a complex condition is common, as with arithmetic expressions. Sometimes, this is mandatory (due to operator priority), and other times it is just recommended (for better readability). Of course, the rule must still be followed that every opening parenthesis “(” must have a matching closing parenthesis “)”.

Example 6

If the check is to determine if a number is in the range from 0 to 100 (including both 0 and 100), the condition would be checking two simple conditions that must both hold true, namely:

  • Greater than or equal to zero: (x >= 0)
  • AND (both must be true at the same time)
  • Less than or equal to 100: (x <= 100)

In Java, this is written as:

1 x >= 0 && x <= 100

Optionally, the same expression with additional parentheses for better readability:

1 (x >= 0) && (x <= 100)

It is important to note that both simple conditions must be true simultaneously, meaning that the number must be both greater than or equal to zero and less than or equal to 100. Therefore, the logical AND (&&) operator is used.

Why can’t the logical OR (||) operator be used in this case? Or why is the following condition written incorrectly?

1 (x >= 0) || (x <= 100)

The reason is that this condition would check if at least one of these two simple conditions holds true (either one or the other or both), and it could happen that a number like -5 satisfies the condition since it’s less than 100 (x <= 100), but it’s not greater than or equal to zero (x >= 0), or even a number like 105 satisfies it since it’s greater than or equal to zero (x >= 0), but it’s not less than or equal to 100 (x <= 100). Effectively, any integer number would always satisfy this condition, resulting in a logically valid expression (tautology).

As in the previous example, a logical expression can be mistakenly written so that it always evaluates to true, regardless of the values of the variables involved in the expression. This is known as a tautology. Why is this a mistake, and why does it represent a problem when executing the program? If used within an IF statement, a tautology leads to the fact that the “YES” branch is always executed, and the “NO” branch (if it exists) will never be executed. This is like writing “true” instead of a condition in the IF statement (the “YES” branch will always execute):

1 if (true)
2     System.out.println("YES");
3 else
4     System.out.println("NO");

On the other hand, it is also possible to accidentally create a logically invalid expression (contradiction). This is a logical expression that always evaluates to false. If such a contradiction is used in an IF statement as a condition, only the “NO” branch will always execute (if it exists), and the “YES” branch will never execute. This is like writing “false” instead of a condition in the IF statement (the “NO” branch will always execute):

1 if (false)
2     System.out.println("YES");
3 else
4     System.out.println("NO");

Example 7

If you need to check if a number is outside the range of 0 to 100, then it’s still a check for two simple conditions, but this time at least one of them should be true (they don’t need to be true at the same time), i.e., the number should be:

  • Less than zero (x < 0)
  • OR (either of these two conditions must be true)
  • Greater than 100 (x > 100)

In Java, this is written as:

1 x < 0 || x > 100

Or, optionally, the same expression with additional parentheses for better readability:

1 (x < 0) || (x > 100)

If a number is outside a certain range, it usually means that it’s either smaller than the lower boundary (zero) or greater than the upper boundary (100). This means that it’s enough for at least one of these simple conditions to be true for the entire complex condition to be satisfied. Therefore, the logical OR (||) operator is used.

Why can’t the logical AND (&&) operator be used in this case? Or why is the following condition written incorrectly?

1 (x < 0) && (x > 100)

The reason is that this would check if both simple conditions hold true at the same time (both must be true), which results in a complex condition that can never be satisfied (an unsatisfiable condition). No number can be both less than zero (negative) and greater than 100 at the same time.

Example 8

Create a class DecimalCheck2 which has:

  • A method checkRange1 that checks if the parameter a (a real number) is in the range from 100 to 200, including those values. If true, the method returns TRUE, otherwise it returns FALSE.
  • A method checkRange2 that checks if the parameter a (a real number) is outside the range from 0 to 33.3 (i.e., if it’s less than zero or greater than 33.3). If it’s outside the range, the method returns TRUE, otherwise it returns FALSE. In both cases, an appropriate message should be printed on the screen.
 1     class DecimalCheck2 {
 2 
 3         boolean checkRange1(double a){
 4             if ((a >= 100) && (a <= 200))
 5                 return true;
 6             else
 7                 return false;
 8         }
 9 
10         boolean checkRange2(double a){
11             if ((a < 0) || (a > 33.3)) {
12                 System.out.println("The number " + a + 
13                  " is less than zero or greater than 33.3");
14                 return true;
15             }
16             else {
17                 System.out.println("The number " + a + 
18                     " is within the range 0 - 33.3");
19                 return false;
20             }
21         }
22     }

Create a class TestDecimalCheck2 which in the main method, using the DecimalCheck2 class, checks if the number 123.5 is within the range 100 to 200 and if the number 34 is outside the range 0 to 33.3.

 1 class TestDecimalCheck2 {
 2 
 3     public static void main(String[] args) {
 4         DecimalCheck2 dc = new DecimalCheck2();
 5 
 6         System.out.println( "Number "+ 123.5 + 
 7             " is inside range 100-200: "+ dc.checkRange1(123.5));
 8 
 9         boolean check2 = dc.checkRange2(34);
10         System.out.println(check2);
11     }
12 }

In the method checkRange1, you can see that the IF condition is complex, meaning that it has two simple conditions joined by the logical AND operator. The syntax of the IF statement requires that parentheses surround the entire condition, so the whole statement looks like this:

1 if ((a >= 100) && (a <= 200))
2     return true;
3 else
4     return false;

Again, this checks if the number is in the range 100-200, i.e., it checks if the number is both greater than or equal to 100 AND less than or equal to 200.

On the other hand, in the method checkRange2, it checks if the number is outside the range 0 to 33.3, i.e., if it’s less than zero (the first simple condition) OR greater than 33.3 (the second simple condition). It’s enough for one of these simple conditions to be true for the number to be outside the range.

1 if ((a < 0) || (a > 33.3)) {
2     System.out.println("The number " + a + " is less than zero or greater than 33.3");
3     return true;
4 }
5 else {
6     System.out.println("The number " + a + " is within the range 0 - 33.3");
7     return false;
8 }

Example 9

Create a class DecimalCheck2A which has all the same methods as the DecimalCheck2 class, but the IF statements inside them are written differently.

 1 class DecimalCheck2A {
 2 
 3     boolean checkRange1(double a){
 4         /* Instead of this if statement
 5         if ((a >= 100) && (a <= 200))
 6             return true;
 7         else
 8             return false;
 9         */
10 
11         //An alternative if statement can be used
12         if ((a < 100) || (a > 200))
13             return false;
14         else
15             return true;
16     }
17 
18     boolean checkRange2(double a){
19         /* Instead of this if statement
20         if ((a < 0) || (a > 33.3)) {
21             System.out.println("Number "+a+
22                 " is less than 0 or greater than 33.3");
23             return true;
24         }
25         else {
26             System.out.println("Number "+a+
27                 " is within range 0 - 33.3");
28             return false;
29         }
30         */
31 
32         //An alternative if statement can be used
33         if ((a >= 0) && (a <= 33.3)) {
34             System.out.println("Number "+a+" is within range 0 - 33.3");
35             return false;
36         }
37         else {
38             System.out.println("Number "+a+
39                 " is less than 0 or greater than 33.3");
40             return true;
41         }
42     }
43 }

Create a class TestDecimalCheck2A which in the main method, using the DecimalCheck2A class, checks if the number 123.5 is within the range 100 to 200 and if the number 34 is outside the range 0 to 33.3.

 1 class TestDecimalCheck2A {
 2 
 3     public static void main(String[] args) {
 4         DecimalCheck2A dc = new DecimalCheck2A();
 5 
 6         System.out.println( "Number "+ 123.5 +
 7             " is in range 100-200: "+ dc.checkRange1(123.5));
 8 
 9         boolean pr2 = dc.checkRange2(34);
10         System.out.println(pr2);
11     }
12 }

In the method checkRange1, you can see that instead of checking the condition ((a >= 100) && (a <= 200)), which checks if the number is in the range 100-200, it checks the negated condition, i.e., ((a < 100) || (a > 200)), which checks if the number is outside the range. The simple parts of the condition are negated (a >= 100 becomes a < 100, a <= 200 becomes a > 200), and then the logical AND (&&) operator is replaced with the logical OR (||) operator. To make sure the IF statement still works as expected, the “YES” and “NO” branches must be swapped, so the else branch returns true, and if the (new) condition is satisfied, it returns false.

In the method checkRange2, you can see that instead of checking the condition ((a < 0) || (a > 33.3)), which checks if the number is outside the range 0-33, it checks the negated condition, i.e., ((a >= 0) && (a <= 33.3)), which checks if the number is inside the range. The simple parts of the condition are negated (a < 0 becomes a >= 0, a > 33.3 becomes a <= 33.3), and then the logical OR (||) operator is replaced with the logical AND (&&) operator. As before, to make the IF statement work as expected, the “YES” and “NO” branches are swapped.

As mentioned earlier, similar to arithmetic operators, logical operators also have their priority, with NOT (!) being the highest, followed by AND (&&), and OR (||) having the lowest priority. In some more complex logical expressions that use multiple different logical operators, omitting additional parentheses can lead to the formation of an incorrect expression. In such cases, the expression is evaluated according to the priority of the operators, which may not necessarily align with the intended logic. This is similar to when parentheses are omitted in arithmetic expressions, causing multiplication and division to be performed before addition and subtraction when it was meant to be the other way around.

Nested if statements

In the previous examples, some methods contained multiple sequentially connected if statements. These statements, as written, are considered independent (separate) commands. However, in some situations, after checking one condition, additional conditions need to be checked, but only if the first condition is satisfied. The solution to such problems involves writing what are called nested statements, specifically nested if statements:

1 if ( ...condition 1... )
2     if ( ...condition 2... )  command_1_1;
3     else  command_2_1;
4 else 
5     if ( ...condition 3... )  command_1_2;
6     else  command_2_2;

From the declaration, it can be seen that instead of the usual commands that follow the condition, there are new if statements. This way, “condition 2” will be checked only if “condition 1” is satisfied. If both “condition 1” and “condition 2” are true, then command_1_1 will be executed. If “condition 1” is true and “condition 2” is false, command_2_1 will be executed. Similarly, if “condition 1” is false, then “condition 3” will be checked. If “condition 3” is true, command_1_2 will be executed, otherwise, command_2_2 will be executed.

If statements can also be nested and placed within a block of commands together with other statements, so the following situation is common:

 1 if ( ...condition... ) {
 2   command_1;
 3   command_2;
 4   if ( ...condition 2... )
 5       command_1_1;
 6   else   
 7       command_2_1;
 8   ...
 9   command_n;
10 }
11 else  {
12   if ( ...condition 3... )
13       command_1_2;
14   else
15       command_2_2;
16   command_3_1;
17   command_3_2;
18   ...
19   command_3_m;
20 }

The if statement is often used when it is necessary to check whether some input value falls within predefined boundaries for an attribute or variable (e.g., whether height is within the range of 120-240 cm). This process of checking input values is commonly called logical input validation.

Example 10

Create a class HeightAgeGroups with the following methods:

  • Method checkHeight takes the height of a person in centimeters (a real number) as a parameter and prints whether the person is classified as short (less than or equal to 158 cm), medium height (greater than 158 cm and less than or equal to 179 cm), or tall (greater than 179 cm). If the entered height is outside the range of 120-240 cm, an error message should be displayed on the screen.
  • Method checkAge takes the person’s age in years as a parameter. The method first checks if the entered age is within the range of 0 - 120 years. If not, an error message is displayed on the screen. If the age is valid, the method checks whether the person is young (0-30 years), middle-aged (31-55 years), or old (56 years and older), and displays the appropriate message on the screen.
 1     class HeightAgeGroups {
 2 
 3         void checkHeight(double height){
 4             if ((height < 120) || (height > 240))
 5                 System.out.println("Height is out of range");
 6             else{
 7                 if (height <= 158)
 8                     System.out.println("Short person");
 9                 if ( (height > 158) && (height <= 179))
10                     System.out.println("Medium height person");
11                 if (height > 179)
12                     System.out.println("Tall person");
13             }
14         }
15 
16         void checkAge(int age){
17             if ((age < 0) || (age > 120))
18                 System.out.println("Age is out of range");
19             else{
20                 if (age <= 30)
21                     System.out.println("Young person");
22                 if ( (age > 30) && (age <= 55))
23                     System.out.println("Middle-aged person");
24                 if (age > 55)
25                     System.out.println("Old person");
26             }
27         }
28 
29     }

Create a class TestHeightAgeGroups which creates an object of the HeightAgeGroups class and calls its methods to check the age groups for people aged 5, 35, and 56, as well as to check the heights of people at 145 cm, 185 cm, and 175 cm.

 1 class TestHeightAgeGroups {
 2 
 3     public static void main(String[] args) {
 4         HeightAgeGroups hg = new HeightAgeGroups();
 5 
 6         hg.checkAge(5);
 7         hg.checkAge(35);
 8         hg.checkAge(56);
 9 
10         hg.checkHeight(145);
11         hg.checkHeight(185);
12         hg.checkHeight(175);
13     }
14 
15 }

In the checkHeight method, there is an if statement that checks the height range (120-240 cm). If the height is outside this range, the branch that prints an error message is executed. If the height is within the range, three nested if statements are executed in the “else” branch to check the ranges for short, medium, and tall people.

Similarly, in the checkAge method, there is one “outer” if statement to check the age range (0-120), followed by three nested if statements to check the individual age categories (young, middle-aged, old).

When multiple related checks are performed, as in the previous examples, and there are several separate if statements, multiple levels of nested statements can often be used. This nested structure results in less readable code, but it makes the code execute faster because not all if statements are evaluated (conditions are checked only when necessary). Additionally, it usually reduces the number of if statements. This can be seen in the following example.

Example 11

Create a class IntegerCheckNE that contains the same methods as the IntegerCheck class from Example 1, but with the if statements nested within each method.

 1 class IntegerCheckNE {
 2 
 3     void checkSign(int a){
 4         /* Instead of three separate if statements
 5         if (a == 0)
 6             System.out.println("Number "+a+" is equal to zero");
 7 
 8         if (a > 0)
 9             System.out.println("Number "+a+" greater than zero");
10 
11         if (a < 0)
12             System.out.println("Number "+a+" less than zero");
13             */
14 
15         //Two nested if statements can be used
16         if (a == 0)
17             System.out.println("Number "+a+" is equal to zero");
18         else if (a > 0)
19                 System.out.println("Number "+a+" is greater than zero");
20             else
21                 System.out.println("Number "+a+" is less than zero");
22     }
23 
24     void checkLessGreaterEqual(int a, int b){
25         /* Instead of three separate if statements
26         if (a > b)
27             System.out.println("Number "+a+" is greater than number "+b);
28 
29         if (a == b)
30             System.out.println("Number "+a+" is equal to number "+b);
31 
32         if (a < b)
33             System.out.println("number "+a+" is less than number "+b);
34         */
35 
36         //Two nested if statements can be used
37         if (a > b)
38             System.out.println("Number "+a+" is greater than number "+b);
39         else if (a == b)
40                 System.out.println("Number "+a+" is equal to number "+b);
41             else
42                 System.out.println("number "+a+" is less than number "+b);
43     }
44 }

Create a class TestIntegerCheckNE which, in the main method, uses the IntegerCheckNE class to compare the numbers 23 and -17 and check the sign of the number -55.

 1 class TestIntegerCheckNE {
 2 
 3     public static void main(String[] args) {
 4         IntegerCheckNE ic = new IntegerCheckNE();
 5 
 6         ic.checkLessGreaterEqual(23, -17);
 7 
 8         ic.checkSign(-55);
 9     }
10 }

Before nesting, the checkSign method had three independent if statements that checked whether a number was equal to zero, greater than zero, or less than zero. Since all three conditions were written separately, it meant that all three conditions would be checked every time, which is often unnecessary since any number can only fall into one of these three categories: zero, positive, or negative.

In the non-nested version, if the number 5 is entered, the first if statement checks the number is equal to zero. Since it isn’t, the first if statement is skipped (there is no “else” branch), and the second if statement checks whether the number is greater than zero. Since it is, a message is displayed. However, the third if statement would still check if the number is negative, even though it was already established that the number is greater than zero. If the number zero were entered, the conditions for all three if statements would be checked, even though the first check would already confirm that the number is zero and the other checks would be unnecessary.

After nesting, there are actually just two dependent if statements doing the same job, but without unnecessary checks. When a solution is found, the checks stop. Specifically, in the first (“outer”) if statement, the program checks if the number is equal to zero. If it is, a message is displayed. If it is not, the first “else” branch is executed, where a second (nested, “inner”) if statement checks whether the number is greater than zero. If it is, a message is displayed. If it is not, the “else” branch of the second if statement is executed, and a message is displayed saying that the number is less than zero.

It is important to note that the second if statement is executed only if the “else” branch of the first if statement is executed, meaning it is only evaluated if the number is not equal to zero. In other words, if zero is entered as the method’s parameter, only the first if statement will execute, and the second will not.

Additionally, why is there no check for whether the number is less than zero (a < 0) in the nested version? Why does the code not look like this:

1 if (a == 0)
2   System.out.println("Number "+a+" is equal to zero");
3 else if (a > 0)
4       System.out.println("Number "+a+" is greater than zero");
5 else if (a < 0)
6       System.out.println("Number "+a+" is less than zero");

It can be written that way, but it is not necessary. The reason is that the if statements are nested, so if the number is not zero, the “else” branch of the first if statement is executed, where the second if statement checks whether the number is greater than zero. If it is not greater than zero, the second else branch is executed, and it is implicitly understood that the number must be less than zero, so no additional check for a < 0 is needed.

Similar logic can be seen in the checkLessGreaterEqual method.

In summary, it can be said that if statements can be written in multiple ways by reversing conditions and swapping branches, but groups of multiple if statements are often more efficiently written by nesting these statements.

Example 12

Create a class HeightAgeGroupsA that has the same methods as the HeightAgeGroups class from Example 9, but with the if statements in those methods written alternatively by nesting (where possible) and by reversing conditions and branches (where nesting is not possible).

 1 class HeightAgeGroupsA {
 2 
 3     void checkHeight(double height){
 4         /* Instead of these if statements
 5         if ((height < 120) || (height > 240))
 6             System.out.println("Height is out of range");
 7         else{
 8             if (height <= 158)
 9                 System.out.println("Short person");
10             if ( (height > 158) && (height <= 179))
11                 System.out.println("Medium height person");
12             if (height > 179)
13                 System.out.println("Tall person");
14         }
15         */
16 
17         // These can be used
18         if ((height >= 120) && (height <= 240))
19             if (height <= 158)
20                 System.out.println("Short person");
21             else if ( (height > 158) && (height <= 179))
22                     System.out.println("Medium height person");
23                 else
24                     System.out.println("Tall person");
25         else
26             System.out.println("Height is out of range");
27     }
28 
29     void checkAge(int age) {
30         /* Instead of these if statements
31         if ((age < 0) || (age > 120))
32             System.out.println("Age is out of range");
33         else{
34             if (age <= 30)
35                 System.out.println("Young person");
36             if ( (age > 30) && (age <= 55))
37                 System.out.println("Middle-aged person");
38             if (age > 55)
39                 System.out.println("Old person");
40         }*/
41 
42         // These can be used
43         if ((age >= 0) && (age <= 120))
44             if (age <= 30)
45                 System.out.println("Young person");
46             else if ((age > 30) && (age <= 55))
47                 System.out.println("Middle-aged person");
48             else
49                 System.out.println("Old person");
50         else
51             System.out.println("Age is out of range");
52     }
53 
54 }

Create a class TestHeightAgeGroupsA that creates an object of the HeightAgeGroupsA class and calls its methods to check the age groups of people aged 5, 35, and 56 years, as well as the heights of people at 145 cm, 185 cm, and 175 cm.

 1 class TestHeightAgeGroupsA {
 2 
 3     public static void main(String[] args) {
 4         HeightAgeGroupsA hg = new HeightAgeGroupsA();
 5 
 6         hg.checkAge(5);
 7         hg.checkAge(35);
 8         hg.checkAge(56);
 9 
10         hg.checkHeight(145);
11         hg.checkHeight(185);
12         hg.checkHeight(175);
13     }
14 
15 }

In the checkHeight method, there is now one if statement that checks if the height is within the range of 120-240 cm (with reversed condition and branches), and then two nested if statements within the “YES” branch of that if statement to check height categories. It is important to note that the “else” branch, which is written last, actually belongs to the first if statement — within it, an error message is printed if the height is out of bounds.

Similar changes can be seen in the checkAge method.

Most common errors

Some of the most common syntax errors when writing if statements are:

  • Writing the assignment operator (=) instead of the equality comparison operator (==), or vice versa:
1     if (a = 0)
2       System.out.println("The number " + a + " is zero");

Instead, it should be:

1     if (a == 0)
2       System.out.println("The number " + a + " is zero");
  • Forgetting to place parentheses around the logical condition:
1     if a == 0
2       System.out.println("The number " + a + " is zero");

Instead, it should be:

1     if (a == 0)
2       System.out.println("The number " + a + " is zero");
  • Forgetting to close the parentheses of the logical condition or part of it (missing a closing parenthesis at the end):
1     if ((a < 0) || (a > 33.3)) 
2       return true;

Instead, it should be:

1     if ((a < 0) || (a > 33.3)) 
2       return true;
  • Forgetting to open parentheses around the logical condition or part of it (missing an opening parenthesis in the middle after the OR operator):
1     if ((a < 0) || a > 33.3)) 
2       return true;

Instead, it should be:

1     if ((a < 0) || (a > 33.3)) 
2       return true;
  • Forgetting to create a block of commands in the “YES” branch if there are multiple commands, and there is an “NO” branch (else), for example:
1     if ((a < 0) || (a > 33.3)) 
2       System.out.println("The number " + a + " is less than zero or greater than 33.3");
3       System.out.println("End");
4     else 
5       System.out.println("The number " + a + " is in the range 0 - 33.3");
6       System.out.println("End");

Instead, it should be:

1     if ((a < 0) || (a > 33.3)) {
2       System.out.println("The number " + a + " is less than zero or greater than 33.3");
3       System.out.println("End");
4     }
5     else {
6       System.out.println("The number " + a + " is in the range 0 - 33.3");
7       System.out.println("End");
8     }
  • Placing a return statement in one of the IF branches, but not as the last statement in that branch, for example:
1     if ((a < 0) || (a > 33.3)) {
2       return true;
3       System.out.println("The number " + a + " is less than zero or greater than 33.3");
4     }
5     else {
6       return false;
7       System.out.println("The number " + a + " is in the range 0 - 33.3");
8     }

Instead, it should be:

1     if ((a < 0) || (a > 33.3)) {
2       System.out.println("The number " + a + " is less than zero or greater than 33.3");
3       return true;
4     }
5     else {
6       System.out.println("The number " + a + " is in the range 0 - 33.3");
7       return false;
8     }
  • Forgetting to place a return statement in each branch of the IF statement when a method is supposed to return a value or, at least, a return outside the IF statement that returns a default value in every case, for example:
1     boolean returnValue(int a) {
2         if ((a < 0) || (a > 33.3)) 
3           return true;
4     }

Instead, it should be:

1     boolean returnValue(int a) {
2         if ((a < 0) || (a > 33.3)) 
3           return true;
4         else
5           return false;
6     }

Or like this with a default return statement outside the IF statement:

1     boolean returnValue(int a) {
2         if ((a < 0) || (a > 33.3))
3           return true;
4         
5         return false;
6     }

Other non-syntax errors, which can still affect the behavior of the program, include:

  • Writing an always-satisfied logical expression (a tautology) as the condition for the IF statement, for example:
1     if (x > 1 || x < 100)

Instead (correctly):

1     if (x > 1 && x < 100)
  • Writing an always-unsatisfied logical expression (a contradiction) as the condition for the IF statement, for example:
1     if (x < 1 && x > 100)

Instead (correctly):

1     if (x < 1 || x > 100)
  • Forgetting to create a block of commands in the “YES” branch if there are multiple commands, and there is no “NO” branch (else). This will cause only the first command to be executed if the condition is satisfied, and all subsequent commands will “fall out” of the IF statement, i.e., they will always be executed, for example:
1     if ((a < 0) || (a > 33.3)) 
2       System.out.println("The number " + a);
3       System.out.println(" is less than zero or greater than 33.3");

This will cause the message “ is less than zero or greater than 33.3“ to always be printed. The correct code should be:

1     if ((a < 0) || (a > 33.3)) {
2       System.out.println("The number " + a);
3       System.out.println(" is less than zero or greater than 33.3");
4     }
  • Forgetting to create a block of commands in the “NO” branch if there are multiple commands. This will cause only the first command to be executed if the condition is not satisfied, and all subsequent commands will “fall out” of the IF statement, i.e., they will always be executed, for example:
1     if ((a < 0) || (a > 33.3)) {
2       System.out.println("The number " + a);
3       System.out.println(" is less than zero or greater than 33.3");
4     }
5     else
6       System.out.println("The number " + a);
7       System.out.println(" is in the range 0 - 33.3");

This will always print the message “ is in the range 0 - 33.3“. The correct code should be:

1     if ((a < 0) || (a > 33.3)) {
2       System.out.println("The number " + a);
3       System.out.println(" is less than zero or greater than 33.3");
4     }
5     else {
6       System.out.println("The number " + a);
7       System.out.println(" is in the range 0 - 33.3");
8     }
  • Forgetting to place parentheses around parts of a complex logical condition in cases where a different condition will result due to the precedence of logical operators. For example, the following IF statement returns true if both b is zero and a is outside the range of 0 to 33.3:
1     if (b == 0 && (a < 0 || a > 33.3)) 
2       return true;

If written without inner parentheses, a different condition is produced, which is not what is intended:

1     if (b == 0 && a < 0 || a > 33.3)
2         return true;

Or like this:

1     if ( (b == 0 && a < 0) || a > 33.3) 
2       return true;
  • Writing a semicolon (;) immediately after the IF condition. This will cause the statement written in the “YES” branch to fall outside the IF statement, meaning it will execute every time, for example:
1     if ((a < 0) || (a > 33.3));
2       System.out.println("The number " + a + " is outside the range 0-33.3");

The correct code would be:

1     if ((a < 0) || (a > 33.3))
2       System.out.println("The number " + a + " is outside the range 0-33.3");