// Java Fundamentals · Lesson 03
Operators and Expressions
How Java turns 2 + 3 * 4 into one value, and why x++ + ++x is 12.
BY THE END OF THIS LESSON YOU CAN
- Predict what
/and%give, even with negative numbers - Use
++,--and compound assignment like+=without surprises - Compare values, and know why Strings need
.equals - Combine conditions with
&&,||and!, and use short-circuiting - Read bitwise operators and predict the order Java evaluates an expression in
MEMORY
type · name · valueOnly the final value is stored. The steps in between are temporary results that Java throws away.
Part 1 of 7
Expressions and arithmetic
What an expression is, the five arithmetic operators, the remainder, and the rules Java uses to pick the type of a result.
// 1.1
Anatomy of an expression
An expression is a piece of code that Java can work out to a single value. It is built from operands (the values) and operators (the symbols that do something with them). Click any part of the line.
int total = price * count + 5;
Every expression has a value and a type
With int price = 12; and int count = 3;, Java builds the value from small expressions into bigger ones:
price12intprice * count36int55intprice * count + 541int
How many operands?
- UnaryOne operand:
-x,!isOpen,count++ - BinaryTwo operands, one on each side:
a + b,age >= 18,x = 5 - TernaryThree operands. Java has exactly one:
?:, which you will meet in lesson 4.
// 1.2
The five arithmetic operators
You know four of them from school. The fifth, %, gets its own lab next. Remember from lesson 2: the types of the operands decide what / does.
| Operator | Name | Example | Result |
|---|---|---|---|
+ | Addition | 7 + 2 | 9 |
- | Subtraction | 7 - 2 | 5 |
* | Multiplication | 7 * 2 | 14 |
/ | Division | 7 / 2 | 3, not 3.5: both operands are ints, so the decimal part is dropped |
% | Remainder | 7 % 2 | 1: 7 = 2 × 3 + 1 |
int / int gives an int
Java cuts off the decimal part, toward zero. It never rounds.
7 / 2317 / 53-7 / 2-3-17 / 5-3
One double makes it a double
If either operand is a double, you get real division.
7.0 / 23.57 / 2.03.5(double) 7 / 23.5(double) (7 / 2)3.0
The average trap
Storing the result in a double is too late: sum / count was already an int division, so the .5 was gone before Java converted 3 to 3.0. Cast one operand first.
int sum = 7; int count = 2; double avg = sum / count; System.out.println(avg); System.out.println((double) sum / count);
$ java Main 3.0 3.5
- and + also work with one operand: -x flips the sign. And dividing an int by zero crashes the program, while a double gives Infinity or NaN (lesson 2).
// 1.3
The remainder operator %
a % b is what is left over when you divide a by b in whole numbers. Change the numbers and watch the leftovers.
TRY THESE
THE PICTURE
- The sign follows the left operand
-7 % 3is-1and7 % -3is1. Java first divides toward zero (-7 / 3is-2), and the remainder is what is left: −7 − (−2 × 3) = −1. % 0crashes, like/ 0With ints,7 % 0throwsArithmeticException: / by zero. With doubles,10 % 0.0quietly givesNaN.- Want it never negative?
Math.floorModMath.floorMod(-7, 3)is2. Its sign followsb, so with a positivebthe result is always between 0 and b − 1. - It works on doubles too
5.5 % 2is1.5: 2 fits twice into 5.5, and 1.5 is left.
// 1.4
% at work
Backend code uses the remainder all the time: even and odd checks, digits of a number, time, and anything that wraps around.
Even or odd?
A number is even when n % 2 == 0. But -7 % 2 is -1, so testing n % 2 == 1 misses negative odd numbers. Test for odd with != 0.
int n = -7; System.out.println(n % 2 == 0); // even? System.out.println(n % 2 == 1); // odd? wrong System.out.println(n % 2 != 0); // odd? right
$ java Main false false true
Digits of a number
% 10 gives the last digit, / 10 removes it. Together they take any number apart.
int n = 472; System.out.println(n % 10); // last digit System.out.println(n / 10 % 10); // middle digit System.out.println(n / 100); // first digit
$ java Main
2
7
4Minutes into hours
/ says how many whole hours fit, % says how many minutes are left over. The same trick turns qəpik into manat and qəpik.
int minutes = 125; int h = minutes / 60; int m = minutes % 60; System.out.println(h + " h " + m + " min");
$ java Main
2 h 5 minWrap around the clock
It is 22:00 in Baku. Five hours later it is 3:00, not 27:00. % 24 wraps the hour back to the start of the day.
int hour = 22; System.out.println((hour + 5) % 24);
$ java Main
3// 1.5
Numeric promotion: what type is the result?
Before Java does arithmetic, it converts both operands to one common type. This is called numeric promotion, and two rules decide the type.
Rule 1: small types become int
byte, short and char are always turned into int before arithmetic. Even byte + byte gives an int.
Rule 2: the wider type wins
If one operand is wider, the other is widened to match:
Try it: pick two operand types
Left operand
Right operand
Overflow happens before widening
Milliseconds in a year. All five numbers are ints, so Java multiplies in int and overflows long before the result reaches the long variable. Make the first number a long and every step is done in long.
long wrong = 24 * 60 * 60 * 1000 * 365; long right = 24L * 60 * 60 * 1000 * 365; System.out.println(wrong); System.out.println(right);
$ java Main 1471228928 31536000000
byte a = 10, b = 20; byte c = a + b; System.out.println(c);
$ javac Main.java Main.java:4: error: incompatible types: possible lossy conversion from int to byte byte c = a + b; ^ 1 error
byte a = 10, b = 20;
int sum = a + b;
System.out.println(sum);
System.out.println('A' + 2);
System.out.println((char) ('A' + 2));
long big = 3_000_000_000L;
System.out.println(big + 1);
$ java Main
30
67
C
3000000001
// 1.6
+ with Strings
+ has two jobs. Between two numbers it adds. If either side is a String, it joins text, which is called concatenation. Java works from left to right, so where the String sits matters.
1 + 23Two ints: addition3 + "3""33"A String: the 3 becomes text and is joined"33" + 4"334"Still a String, so 4 is joined too
- Left to rightEach
+looks only at its two operands: the result so far and the next value. - Once a String, always a StringAfter the first String, every following
+joins text. Use brackets to add numbers first:"Sum: " + (5 + 5). - A char is a number
'a' + 'b'adds the character codes: 97 + 98. Only a String in the expression turns it into text.
Predict the result
0 of 15 answeredJShell shows Strings in quotes, so "33" is text and 33 is an int.
// 1.7
Useful Math methods
Java has no operator for powers or square roots. The built-in Math class has them, plus a few you will use every week.
| Method | Example | Result | Good to know |
|---|---|---|---|
Math.abs | Math.abs(-7) | 7 | Distance from zero |
Math.max · min | Math.max(3, 9)Math.min(-2, 5) | 9-2 | The bigger or smaller of two values |
Math.pow | Math.pow(2, 10) | 1024.0 | Always returns a double |
Math.sqrt | Math.sqrt(16) | 4.0 | Square root, also a double |
Math.round | Math.round(2.5)Math.round(-2.5) | 3-2 | A half always goes up, toward positive infinity. For a double it returns a long |
Math.ceil · floor | Math.ceil(4.1)Math.floor(-4.1) | 5.0-5.0 | Round up or down. The result is a double |
Math.floorDiv · floorMod | Math.floorDiv(-7, 3)Math.floorMod(-7, 3) | -32 | Division that rounds down, and its matching remainder |
Math.addExact | Math.addExact(2147483647, 1) | ArithmeticException | Crashes with “integer overflow” instead of silently wrapping around |
^ | 2 ^ 3 | 1 | Trap: ^ is not power. It is XOR, a bit operator (Part 5). Use Math.pow(2, 3), which gives 8.0 |
// 1.8
Checkpoint: expressions and arithmetic
Eight questions on Part 1. Answer each one, read the explanation, and see your score at the end.
Part 2 of 7
Assignment, increment and decrement
Operators that change a variable: =, the shortcuts like +=, and ++ and --. Here you finally solve x++ + ++x.
// 2.1
Assignment and compound operators
An assignment is an expression too: it stores a value and also has that value. And because adding to a variable is so common, Java has shortcuts for it.
Assignment runs right to left
In a = b = 5, Java first runs b = 5. That assignment has the value 5, which is then assigned to a. Both end up as 5.
int a, b; a = b = 5; System.out.println(a + " " + b);
$ java Main
5 5Try it: start with int x = 10;
The compound operators
Each one reads the variable, does the operation and stores the result back.
| Shortcut | Means | If x is 10 |
|---|---|---|
x += 3 | x = x + 3 | 13 |
x -= 3 | x = x - 3 | 7 |
x *= 3 | x = x * 3 | 30 |
x /= 3 | x = x / 3 | 3 |
x %= 3 | x = x % 3 | 1 |
There are also compound versions of the bit operators from Part 5: &= |= ^= <<= >>= >>>=.
The right side is worked out first, as a whole. x *= 2 + 1 means x = x * (2 + 1), not x * 2 + 1.
// 2.3
++ and --, step by step
x++ and ++x both add 1 to x. The difference is the value the expression gives: postfix x++ uses the old value, then changes. Prefix ++x changes first, then uses the new value. Step through it.
(output appears here)
Memory
Value of the expression
// 2.4
Solving x++ + ++x
Lesson 2 ended with this puzzle. Java guarantees that the operands of + are evaluated from left to right, so the answer is always the same: 12. Step through it.
x+++++xThe classic trap: x = x++;
x++ gives the old value 5 and makes x 6. Then the assignment runs last and stores that 5 back into x. The increment is lost.
int x = 5; x = x++; System.out.println(x);
$ java Main
5The advice: one change per line
Use ++ and -- as a statement of their own, like count++;. On its own line, prefix and postfix do exactly the same thing. Puzzles like x++ + ++x are good for exams and bad for real code: nobody on your team should need a debugger to read one line.
// 2.5
Game: predict x and y
Each snippet runs inside main. When it finishes, what are x and y? Work it out on paper before you click.
Predict x and y
Your toolkit
x++: use, then changeThe expression gives the old value.++x: change, then useThe expression gives the new value.- Left to rightOperands are evaluated from left to right, and each one sees the changes made before it.
- Assignment is last
=and+=store their value at the very end, after the right side, with a hidden cast for+=.
// 2.6
Checkpoint: assignment, ++ and --
Eight questions on Part 2. Trace each one in your head, step by step.
Part 3 of 7
Comparing values
Six operators that answer yes-or-no questions, the difference between = and ==, and why Strings need .equals.
// 3.1
Relational and equality operators
A comparison always gives a boolean: true or false. You can print it, store it in a variable, and soon (lesson 4) use it in an if.
| Operator | Means | With int age = 20; |
|---|---|---|
== | is equal to | age == 18 → false |
!= | is not equal to | age != 18 → true |
< | is less than | age < 18 → false |
> | is greater than | age > 18 → true |
<= | is less than or equal to | age <= 20 → true |
>= | is greater than or equal to | age >= 21 → false |
int age = 20; boolean isAdult = age >= 18; System.out.println(isAdult); System.out.println(age == 18); System.out.println(age != 18);
$ java Main
true
false
true
= stores, == compares
Typing one = instead of two is a classic typo. Java protects you: x = 5 has the int value 5, and an int can’t be used as a boolean.
int x = 3; boolean ok = (x = 5);
Main.java:4: error: incompatible types: int cannot be converted to boolean
boolean ok = (x = 5);
^
1 errorWith booleans, the typo compiles
done = true is an assignment: it changes done, and its value is true. You meant done == true, or simply done.
boolean done = false; System.out.println(done = true); System.out.println(done);
$ java Main
true
trueComparisons don’t chain
Java works out 1 < x first and gets true. Then true < 10 makes no sense: a boolean is not a number. Write x > 1 && x < 10 instead (Part 4).
int x = 5; System.out.println(1 < x < 10);
$ javac Main.java Main.java:4: error: bad operand types for binary operator '<' System.out.println(1 < x < 10); ^ first type: boolean second type: int 1 error
// 3.2
Mixed types and doubles
Before comparing two numbers, Java promotes them to a common type, exactly like in arithmetic (1.5). And doubles bring back the rounding errors from lesson 2.
Promoted first, then compared
7 == 7.0: the int 7 becomes 7.0, so it is true.'a' == 97: a char is its code, and'a'is 97.10 / 4 == 2.5: the division happens first and gives the int 2. Only then is 2 compared with 2.5.
System.out.println(7 == 7.0);
System.out.println('a' == 97);
System.out.println(10 / 4 == 2.5);
System.out.println(10 / 4.0 == 2.5);$ java Main true true false true
Never compare doubles with ==
0.1 + 0.2 is really 0.30000000000000004 (lesson 2), so == says false. Check that the difference is tiny instead: 1e-9 is 0.000000001, “close enough”.
double a = 0.1 + 0.2; System.out.println(a == 0.3); System.out.println(Math.abs(a - 0.3) < 1e-9); System.out.println(Double.NaN == Double.NaN);
$ java Main false true false
NaN (“not a number”) is the only value that is not equal even to itself.
// 3.3
Comparing Strings: == or .equals?
A String variable holds a reference: where the object lives in memory (lesson 2). == compares the references. .equals compares the text. Click a comparison.
String a = "Baku";
String b = new String("Baku");
String c = "Baku";
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(a == c);
System.out.println(a.equalsIgnoreCase("BAKU"));
$ java Main
false
true
true
true
Stack · main method
Heap · objects
Identical literals in your code share one object, so a and c point to the same @1. new always makes a fresh object.
The rule
==.equals()Why == bugs hide: "Baku" == "Baku" is true, because both literals are the same pooled object. Text typed by a user, read from a file or built at run time is a separate object, and then the same == check fails. Use equalsIgnoreCase when upper and lower case shouldn’t matter.
// 3.4
Game: true or false?
Each expression is a boolean. Decide what Java gives, then read why.
True or false?
Ask yourself
- What is worked out first?Arithmetic happens before the comparison, with int division if both sides are ints.
- Is a char involved?Then it takes part as its code:
'A'is 65,'a'is 97,'0'is 48. - Is it a double?Rounding errors can make “equal” numbers differ.
- Is it a String?
==compares references,.equalscompares text, and case matters.
// 3.5
Checkpoint: comparing values
Eight questions on Part 3.
Part 4 of 7
Logical operators
Combine conditions with &&, || and !, and learn the shortcut Java takes when it already knows the answer.
// 4.1
AND, OR, NOT and XOR
Logical operators take booleans and give a boolean. Flip the switches, or click a row of the truth table.
boolean canEnter = age >= 18 && hasTicket; boolean dayOff = isWeekend || isHoliday; boolean canLogin = !isBlocked;
Read && as “and”, || as “or”, ! as “not”. ^ on booleans means “exactly one of them”, which is the same as a != b.
// 4.2
Short-circuit evaluation
&& skips its right side when the left side is false: the answer is already false. || skips its right side when the left side is true. This shortcut lets the left side guard the right one. Pick an expression and a value.
Expression
Values
// 4.3
Precedence and De Morgan’s laws
! comes first, then &&, then ||, just like minus, times and plus in maths. And there are two laws for moving a ! into brackets.
! before && before ||
In true || false && false, the && groups first: false && false is false, and true || false is true. Brackets change the grouping, and the answer.
System.out.println(true || false && false); System.out.println((true || false) && false);
$ java Main
true
falseMixing && and ||? Add brackets even when Java doesn’t need them. They are for the people who read your code.
Name your conditions
A long condition is easier to read, and to debug, when its parts have names. Each name is just a boolean variable.
int age = 20; boolean hasTicket = true; boolean isBlocked = false; boolean isAdult = age >= 18; boolean canEnter = isAdult && hasTicket && !isBlocked; System.out.println(canEnter);
$ java Main
trueDe Morgan’s laws: pushing ! inside
Push a ! into brackets and two things happen: every part gets its own !, and && and || swap.
!(a && b) == !a || !b
“Not both” means “at least one is not”.
!(a || b) == !a && !b
“Neither” means “not this and not that”.
// 4.4
Game: write the condition
Turn the requirement into Java. Exactly one option is right; the others compile but give the wrong answer for some values, or don’t compile at all.
Which expression matches?
// 4.5
Checkpoint: logical operators
Eight questions on Part 4.
Part 5 of 7
Bitwise and shift operators
Operators that work on the individual bits of an int. You won’t use them every day, but you will meet them in permissions, flags, colours and fast maths.
// 5.1
The bit lab: & | ^ ~
Remember the 8 bits of a byte from lesson 2? The bitwise operators compare two numbers bit by bit, column by column. Click bits to change A and B, then pick an operator.
A and B are shown as 8 bits so they fit on screen. In Java they are 32-bit ints, with 24 more zero bits on the left. The result shows the real int value.
The bit operators
- & AND1 only where both bits are 1
- | OR1 where at least one bit is 1
- ^ XOR1 where the bits are different
- ~ NOTFlips every bit. Unary: one operand
- << >> >>>Shift the bits left or right (5.2)
&, | and ^ also work on booleans (Part 4). On ints they work on every bit at once.
// 5.2
Shift operators: << >> >>>
A shift moves all 32 bits left or right by n places. Bits that fall off the end are lost. The highlighted bits are the ones that came in.
<< n multiplies by 2ⁿ
Zeros come in on the right. 1 << 3 is 8 and 3 << 4 is 48. Shift too far and bits fall off the left: it overflows like any int.
>> n divides by 2ⁿ
The sign bit is copied in on the left, so negative numbers stay negative: -16 >> 2 is -4. It rounds down: -17 >> 1 is -9, while -17 / 2 is -8.
>>> n fills with zeros
Zeros come in on the left, even for negatives, so for n from 1 to 31 the result is never negative. -1 >>> 1 is 2147483647, the biggest int.
Fun trap: for an int, Java only uses the lowest 5 bits of the shift distance, so it counts modulo 32. 1 << 32 is 1, not 0, and 1 << 33 is 2. For a long it is modulo 64: 1L << 32 is 4294967296.
// 5.3
Where backend developers meet bits
One int can hold many yes/no flags, one per bit. That is how Linux stores file permissions, and how many systems store user roles and settings.
Permission flags
Read, write and execute get one bit each: READ = 4 (100), WRITE = 2 (010), EXECUTE = 1 (001). Run the statements and watch perms.
chmod 755 on a Linux server
When you deploy a backend, you set permissions with digits like 755. Each digit is three bits for one group of users: 7 = 111 = rwx for the owner, 5 = 101 = r-x for the group and for everyone else. In Java, 0755 is an octal literal (lesson 2) and equals 493.
Even or odd, with one bit
The last bit of an odd number is always 1, also for negatives. So (n & 1) == 0 means even: (6 & 1) == 0 is true, (7 & 1) == 0 and (-7 & 1) == 0 are false.
Colours: 0xFF8800
A colour packs red, green and blue into one int, 8 bits each. Shift the part you want to the right end, then keep only the last 8 bits with & 0xFF.
int color = 0xFF8800; int red = (color >> 16) & 0xFF; int green = (color >> 8) & 0xFF; int blue = color & 0xFF; System.out.println(red + " " + green + " " + blue);
$ java Main
255 136 0// 5.4
Checkpoint: bits
Eight questions on Part 5. Write the bits on paper if it helps.
Part 6 of 7
Precedence and evaluation order
The rules that decide what happens first in a long expression, a stepper to watch them work, and a gallery of mistakes to break on purpose.
// 6.1
The precedence table
Precedence says which operator groups first: higher rows win. Associativity says what happens between operators on the same row. The highlighted rows are the ones you will use every day.
| # | Operators | What they are | Associativity |
|---|---|---|---|
| 1 | x++x-- | Postfix increment and decrement | left to right |
| 2 | ++x--x+x-x!~(type) | Unary operators and casts | right to left |
| 3 | */% | Multiplication, division, remainder | left to right |
| 4 | +- | Addition, subtraction, String joining | left to right |
| 5 | <<>>>>> | Shifts | left to right |
| 6 | <><=>=instanceof | Relational (instanceof checks an object’s type: later, with classes) | left to right |
| 7 | ==!= | Equality | left to right |
| 8 | & | Bitwise AND | left to right |
| 9 | ^ | Bitwise XOR | left to right |
| 10 | | | Bitwise OR | left to right |
| 11 | && | Logical AND | left to right |
| 12 | || | Logical OR | left to right |
| 13 | ?: | Ternary, the conditional operator (lesson 4) | right to left |
| 14 | =+=-=*=/=%= … | Assignment | right to left |
Remember the shape, not the numbers
One sentence to keep: “Unary, Maths, Compare, Logic, Assign.” Shifts and bit operators sit in between, but you rarely mix them with anything else.
Associativity in two examples
- Left to right:
10 - 4 - 3means(10 - 4) - 3, which is 3, not 9. - Right to left:
a = b = 5meansa = (b = 5), and!!donemeans!(!done).
// 6.2
The expression stepper
Pick an expression and press Step. Each step highlights the next operation, says which rule chose it, and replaces it with its value.
// 6.3
Grouping is not the same as order
Precedence decides how an expression is grouped. It doesn’t mean the high-precedence part runs first. Java always evaluates the operands from left to right, and then applies the operators in the grouped order.
Who runs first in i++ + i * 10?
- Grouping by precedence:
i++ + (i * 10) - Left operand first:
i++gives 1, and i becomes 2 - Then the right operand:
i * 10is 2 × 10 = 20 - Finally the
+:1 + 20 = 21
If “multiplication first” meant i * 10 ran before i++, you would get 1 × 10 + 1 = 11. Java gives 21.
int i = 1; int r = i++ + i * 10; System.out.println(r);
$ java Main
21Precedence groups
It decides which operands belong to which operator, like invisible brackets.
Operands go left to right
Java guarantees it. That is why x++ + ++x is always 12, on every computer.
In doubt? Add brackets
Brackets cost nothing and make the grouping obvious to the next person. And keep ++ out of long expressions.
// 6.4
Break it on purpose
Real mistakes with operators. Most are caught by javac, some only crash while the program runs, and the worst ones compile and run but give the wrong answer. Pick one to see what really happens.
// 6.5
Checkpoint: precedence and order
Eight questions on Part 6.
Part 7 of 7
Wrap-up
What to remember, a final quiz with your checkpoint scores, and your homework.
// 7.1
Key takeaways
An expression is operands joined by operators. Every expression has a value and a type, and even an assignment has a value.
int / intdrops the decimals.%gives the remainder, and its sign follows the left operand:-7 % 3is-1.In arithmetic,
byte,shortandcharbecomeint, and the wider type wins. Overflow happens before widening, so write24L * ….+with a String joins text from left to right:1 + 2 + "3"is"33", but"1" + 2 + 3is"123".x++gives the old value,++xthe new one, and+=hides a cast. Keep++on its own line.Comparisons give booleans. Use
==for primitives,.equalsfor Strings, and a small tolerance for doubles.&&and||short-circuit, so put the guard on the left.!comes before&&, and&&before||.Bit operators work on single bits, and
^is XOR, not power. Precedence groups, operands run left to right, and brackets make it clear.
// 7.2
Check yourself
Twenty questions from the whole lesson. You see the answer right after you pick.
Your checkpoints
// 7.3
Homework
Tick items as you finish them. Your ticks are saved in this browser only.
// 7.4
Questions?
Next lesson: Control Flow Statements. if and else, switch, and the ternary operator ?:, the only operator with three operands. The boolean expressions you wrote today will finally decide what your program does.
int score = 78;
if (score >= 90) {
System.out.println("A");
} else if (score >= 75) {
System.out.println("B");
} else {
System.out.println("C");
}
String result = score >= 50 ? "pass" : "fail";
System.out.println(result);