Introduction
Conditional statements and loops are a very important tool in programming. There aren't many things we could do with code that can only execute line-by-line.
That's what "flow control" means - guiding the execution of our program, instead of letting it execute line-by-line regardless of any internal or external factors. Every programming language supports some form of flow control, if not explicitly via if
s and for
s or similar statements - then it implicitly gives us the tools to create such constructs, i.e. low-level programming languages usually achieve that affect with a lot of go-to
commands.
Loops were a concept used long before computer programming was even a thing, but the first person to use a software loop was Ada Lovelace, commonly known by her maiden name - Byron, while calculating Bernoulli numbers, back in the 19th century.
go-to
commands are to be avoided in most situations, whenever we're using a programming language that gives us the tools to avoid it, like Java and all remotely similar languages do (there's a slight exception to this rule when breaking nested loops).
In Java, there are several ways to control the flow of the code:
- if and if-else statements
- switch statements
while
anddo-while
statementsfor
andenhanced for
statementsbreak
andcontinue
statements
The if Statement
The if
statement is probably the most common form of flow control, regardless of the language. It's very simple and intuitive:
if(true) {
//execute this code block if the argument/expression is true
//some more code
}
Since in a lot of cases, only a single line needs to be executed, you can skip using the curly brackets and a block of code and simply indent the next line, though this only works for a single line:
if(true)
System.out.println("Inside the if block");
System.out.println("I have nothing to do with the if block!");
System.out.println("Neither do I!");
Typically, we don't input boolean
values like in these examples, but rather, we use an expression:
if(username != null)
System.out.println(username);
if(argument >= 10)
System.out.println("The input is higher or equal than 10");
If the expression is false
, the code belonging to the if
statement is simply skipped:
if(1 > 5)
System.out.println("Hello");
System.out.println(" World!");
And the output is:
World!
The if-else Statement
We rarely wish to simply skip a piece of code if the expression is evaluated to be false
. Typically, we want to do something else
in that case:
if(expression) {
//code that executes only if the expression is true
}
else {// optional
//code that executes only if the expression is false
}
Of course, you can use the else
statement in conjunction with the shortened if
statement:
if(expression)
//code that executes only if the expression is true
else
//code that executes only if the expression is false
However this is unrecommended to if you're not dealing with simple statements, especially with nested if
s since it's hard to figure out "which if
does a particular else
belong to". Using brackets will take ambiguity out of the problem if you find this approach unreadable.
The else
statement statement is optional, so there's no need to write an empty else {}
block if we don't want anything to happen if is the expression
is false.
The else
statement is intrinsically tied to the if
statement and can't exist without it. It must appear right after the if
statement otherwise an "else without if" compiler error will appear.
In most cases, we want to compare something for equality - whether one variable has the same value as another or whether it's smaller/larger than another:
String s = "smol";
if (s.length() > 8)
System.out.println("s has too many characters!");
else
System.out.println("Ok, so basically, s is very " + s);
The >
operator here has the usual "greater than" meaning, and Java supports the usual group of relational operators. These include <
, >
, <=
, =>
, ==
checks for equality and !=
checks for inequality.
Note: Make sure to use ==
in if
statements instead of =
, or else you may assign a value instead of compare it.
2 < 5 //true
2 < 2 //false
2 <= 2 //true
2 == 3 //false
2 != 3 //true
Note: Most of the time, we don't compare String
variables using ==
but with the .equals()
method. ==
checks for object reference equality. For primitive types (int
, char
, etc.) it's the same as checking whether they have the same value, but with Strings it most commonly isn't.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
if(string1.equals(string2))
//code to be executed if the strings are equal by value
Nested if Statements
We can have more than one if
statement connected, either by nesting (putting one if
statement inside another) or adding an else if
at the end of our previous if
:
int ourNumber = 10;
if(ourNumber > 8){
if(ourNumber % 2 == 0){
System.out.println("The number is larger than 8 and even");
}
}
else if (ourNumber == 1) {
System.out.println("1 is less than 8, so the previous if block isn't executed");
}
else {
System.out.println("ourNumber is less than 8 and different than 1");
}
else
blocks always "belong" to the if
of the same level, so the else if (ourNumber == 1)
in our code belongs to the if(ourNumber > 8)
and not the nested if
that checks whether the number is even.
You can have as many else if
blocks as you'd like, but only one else
block, which comes last. Each if
, else if
and else
block that evaluates to true
will execute:
Multiple Expressions in a Single if Statement
Another very useful thing is the ability to check multiple conditions in a single if
:
int ourNumber = 10;
if(ourNumber > 5 && (ourNumber % 2 == 0)){
System.out.println("The number is larger than 8 and even");
}
The &&
operator is one of the logical operators Java supports. boolExpr
is used as shorthand for boolean expression
. Reminder: a boolean
expression is an expression that can be evaluated as true
or false
, i.e. it can also be an expression containing a logical operator, as we can see here:
!boolExpr
-!
is negation, it evaluates atrue
expression tofalse
and vice versa.boolExpr1 & boolExpr2
- the AND operator evaluates totrue
if both expressions aretrue
.boolExpr1 | boolExpr2
- the OR operator evaluates totrue
if at least one of the expressions istrue
.boolExpr1 && boolExpr2
- the short-circuit AND operator evaluates totrue
only ifboolExpr1
andboolExpr2
are bothtrue
. It's called a short-circuit operator because if the first expression isfalse
, it won't even evaluate the second expression as the basic condition that both need to betrue
can never occur. With simple expressions, this doesn't affect the code in a meaningful way, but in production code, both expressions can be costly operations and this approach can significantly optimize code.boolExpr1 || boolExpr2
- the short-circuit OR operator evaluates totrue
if at least one ofboolExpr1
andboolExpr2
istrue
. A short-circuit version of the|
operator.boolExpr1 ^ boolExpr2
- the XOR operator evaluates totrue
only ifboolExpr1
andboolExpr2
are evaluate as differently, i.e. if only one of them istrue
and the other one isfalse
.
true && false //false
(2 < 5) && (2 != 3) //true
false || true //true
true && ((false || false) ^ true) //true
(true && false) || false // false
It's highly recommended to use parentheses whenever it makes sense to use them. The last example works the same without parentheses but relying on operator precedence is extremely risky and leads to unreadable code.
Ternary Constructs
The only ternary operator (an operator that takes three operands) in Java is the ?:
operator. It works like a very compact if-else
statement.
int max = 0;
if (a > b)
max = a;
else max = b;
//can be written as
int max = (a > b) ? a : b;
Here's how a ternary construct generally looks like:
(expression) ? returnValueIfTrue : returnValueIfFalse
Ternary constructs can work without parentheses as well, though it's generally more readable to use parentheses:
int max = a > b ? a : b;
You can also use ternary constructs to modify variables:
System.out.println("10% discount for orders above $50!");
double orderPrice = 55;
double finalPrice = orderPrice > 50 ? orderPrice*09 : orderPrice;
System.out.println("Your final price is $" + finalPrice);
And the output is:
Your final price is $49.5
Using the construct, we can also call methods:
public void someMethod() {
String userRole = "Admin";
String result = userRole.equals("Admin") ? greetAdmin() : greetUser();
System.out.println(result);
}
static String greetAdmin() {
return "Hello Admin!";
}
static String greetUser() {
return "Hello User!";
}
Logically, this would result in:
Hello Admin!
Nested Ternary Constructs
System.out.println("10% discount for all orders above $50 and additional $5 off if your order exceedes 2 items!");
double orderPrice = 140;
int quantity = 5;
double finalPrice = (orderPrice > 50) ? (quantity > 2) ? (orderPrice*0.9)-5 : orderPrice*0.9 : orderPrice;
System.out.println("Your final price is: $" + finalPrice);
Since both conditions are true
, the output is:
10% discount for all orders above $50 and additional $5 off if your order exceedes 2 items!
Your final price is: $121.0
However, this approach is horribly unreadable. If possible, avoid nesting ternary constructs, though if you must, break it down into simpler blocks. For an example, this example can be made a bit more readable:
System.out.println("10% discount for all orders above $50 and additional $5 off if your order exceedes 2 items!");
double orderPrice = 140;
int quantity = 5;
double finalPrice = (orderPrice > 50) ?
(quantity > 2) ?
(orderPrice*0.9)-5 : orderPrice*0.9
: orderPrice;
System.out.println("Your final price is: $" + finalPrice);
Conclusion
Flow control in code is essential for absolutely every application. Statements such as if
and if-else
are fundamental building blocks and every aspiring developer should be completely in control/aware of how they work.