It may seem that there is nothing easier than 2+2 even in Java, right? Nope! Even simple Operators and Expressions can give you a serious headache once you start working with them. This is an ongoing list of issues, secret features, niche cases etc. about Operators and Expressions that I’ve been collecting since I started working with Java. The list is definitely gonna get even bigger with time but for now, that’s all I’ve got.
If you find that you were not 100% aware of any of the following, it’s best to get some digging and google the sh*t out of it to make sure you really GET what it’s about. Happy reading!
Short-circuit evaluation of logical operators
Java is rather lazy and uses the so called short-circuit evaluation with logical operators: When using logical operators && and ||, Java performs short-circuit evaluation, meaning it only evaluates the second operand if necessary. For example, when using || and finding the first argument to be true, the JVM will not even look at the second argument. This can be used to your advantage in cases where evaluating the second operand would result in a runtime error, such as a null pointer exception (although please do not ever leave an opening for NPE, don’t be that kind of dev!).
Sometimes, when you have 2 expressions, one that requires a lot of memory use and another short one, it’s a good idea to use the short one first. In many cases the JVM will not need to read the second one and will save you some memory.
Dividing by zero and AN Arithmetic Exception
Dividing by zero is undefined and will result in an ArithmeticException. One way to avoid this is to check the denominator before performing the division, or use the Math.floorDiv or Math.floorMod methods that throw an ArithmeticException if the divisor is zero.
You can use a try-catch block to catch the ArithmeticException and handle it gracefully. For example, you could print an error message and then continue with the program execution.
Other than that, you can check for a zero divisor BEFORE performing the division operation. This can be done using an if statement or a ternary operator, for example. If the divisor is zero, you can handle the error in a similar way to the try-catch block approach.
Operator Precedence
Be careful when using the increment (++) and decrement (–) operators in complex expressions. ++ and — operators have a higher precedence than other operators, which can lead to unexpected behaviour if you’re not careful. It’s often better to use separate assignment statements to avoid confusion and make your code more readable.
Java has a default order in which the operators are evaluated in an expression. The operator with higher precedence is evaluated first. Here is the order of precedence for the operators in Java, from highest to lowest (also, try to remember the names!) :
- Postfix operators (++, –)
- Unary operators (+, -, !, ~)
- Multiplicative operators (*, /, %)
- Additive operators (+, -)
- Shift operators (<<, >>, >>>)
- Relational operators (<, >, <=, >=, instanceof)
- Equality operators (==, !=)
- Bitwise AND operator (&)
- Bitwise exclusive OR operator (^)
- Bitwise OR operator (|)
- Logical AND operator (&&)
- Logical OR operator (||)
- Conditional operator (?:)
- Assignment operators (=, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=, >>>=)
It’s important to keep the order of precedence in mind when writing complex expressions, to ensure that they are evaluated correctly. Parentheses can override the default order of precedence and force certain parts of an expression to be evaluated first.
++ and — synchronisation
The ++ and -- operators are not synchronised, which means that if multiple threads access and modify the same variable using these operators, there is a risk of race conditions and other concurrency issues.
For example, if two threads attempt to increment the same integer variable at the same time using the ++ operator, there is a possibility that they will both read the same value of the variable before modifying it, resulting in one of the increments being lost. This can lead to unexpected behaviour and bugs in your code.
To avoid these issues, it’s important to synchronise access to shared variables using techniques such as locks, synchronised blocks, or atomic variables. By ensuring that only one thread can access and modify a variable at a time, you can prevent race conditions and ensure that your code behaves predictably in a multithreaded environment.
The difference between x++ and ++x
The difference between x++ and ++x lies in the order of operations.
When you use x++, the current value of x is returned first, and then x is incremented by 1. This is known as the post-increment operator.
int x = 5;
int y = x++;
System.out.println(x); // Outputs 6
System.out.println(y); // Outputs 5
In this example, y gets the original value of x (5), and then x is incremented to 6.
On the other hand, when you use ++x, x is incremented by 1 first, and then the new value of x is returned. This is known as the pre-increment operator.
int x = 5;
int y = ++x;
System.out.println(x); // Outputs 6
System.out.println(y); // Outputs 6
In this example, x is incremented to 6 before the value is assigned to y.
In general, the choice between x++ and ++x depends on the specific use case. If you need to use the current value of x and then increment it, use x++. If you need to increment x first and then use the new value, use ++x.
NOTE: Honestly, I do not like using either the pre or post increment operator when assigning values. It's so misleading and difficult to read that IMO it's not worth saving that one additional line of code.
Modulo operator and negative numbers
There is an odd behaviour (a negative result) of the modulus operator (%) when dealing with negative numbers.
In normal cases (with positive numbers), the result of a modulus operation is the remainder of dividing the left-hand operand by the right-hand operand. For example, 7 % 3 would result in 1, because 7 divided by 3 leaves a remainder of 1.
However, when counting the modulo of a negative number, the result will also be negative. For example, -7 % 3 would result in -1, because -7 divided by 3 leaves a remainder of -1. This is because the sign of the result is determined by the sign of the left-hand operand, rather than the sign of the result of the division operation.
Deep Nesting of Ternary Operators
Deep nesting of ternary operators creates a weird-looking structure of a multilevel ternary operator. It allows you to evaluate multiple conditions in a single expression but can make code less readable.
Each level of the operator is evaluated independently, it can be hard to track down errors if something goes wrong.
It’s generally recommended to use simple, clear conditional statements or switch statements instead of multilevel ternary operators whenever possible. If you do need to use a multilevel ternary operator, make sure to keep it as simple and clear as possible, and be sure to thoroughly test your code to ensure that it works as expected.
Alright, we’ve just scratched the surface of Operators and Expressions in Java! We talked about the basic arithmetic and comparison operators, as well as some more advanced topics like short-circuit evaluation and deep nesting ternary operators. We also covered some common mistakes to avoid and best practices to follow when using these operators in your code.
The truth is, even though operators and expressions might seem simple, there’s often a lot more happening behind the scenes. It’s important to take the time to understand their behavior in different situations and be aware of any potential issues that might come up.
By doing so, you’ll be able to write more efficient and reliable code that will help you and your team achieve your coding goals. So don’t be afraid to dive deeper and explore the world of Operators and Expressions in Java!

/*
Until next time,
eMs
*/
