Operators, expressions, and casting
Arithmetic, comparison, and logic, plus what integer division and type casting really do.
Finished reading?
Mark this session so you can track where you are.
Arithmetic, comparison, and logic, plus what integer division and type casting really do.
Finished reading?
Mark this session so you can track where you are.
You can now put a value in a box and read it back out. That on its own is a very quiet program. The moment things get interesting is when you start combining those values: adding two numbers, asking whether one is bigger than another, deciding whether two conditions are both true. That combining is what operators do, and Java has a few sharp edges here that surprise almost everyone.
/ and % do.7 / 2 is not 3.5.boolean.char and its code.int, double, boolean, and char, and with declaring a variable before using it. We will not make any decisions or repeat anything yet; that is the next session. Here we only build and evaluate expressions.An expression is any piece of code that Java can work out to a single value. 3 + 4 is an expression; Java evaluates it to 7. The pieces that do the combining, like the + here, are operators. For numbers, Java gives you five:
+ addition- subtraction* multiplication/ division% remainder, also called moduloThe first three behave the way you expect from arithmetic class. The last two are where the sharp edges live, so we will spend most of our time on them. Let us start by running the easy cases.
public class Main { public static void main(String[] args) { int a = 10; int b = 3; System.out.println(a + b); System.out.println(a - b); System.out.println(a * b); }}13 7 30
That prints 13, 7, and 30, one per line. Nothing surprising. Now we change one operator and the ground shifts under us.
In normal arithmetic, 7 divided by 2 is 3.5. In Java it depends entirely on the types involved. Run this and watch the result closely.
public class Main { public static void main(String[] args) { int a = 7; int b = 2; int whole = a / b; int leftOver = a % b; double exact = 7.0 / 2; System.out.println(whole); System.out.println(leftOver); System.out.println(exact); }}3 1 3.5
Step through it. a / b is 7 / 2, and both a and b are int, so Java does integer division: it divides and then throws away anything after the decimal point. 7 / 2 would be 3.5, but the .5 is dropped, so whole is 3. It does not round. 3.9 would also become 3. The fraction is simply cut off.
Then a % b is 7 % 2. The % operator gives the remainder after that same division: 2 goes into 7 three times with 1 left over, so leftOver is1. Together, whole and leftOver tell you the full story: 7 is 3twos with 1 remaining.
The last line is different. 7.0 is a double, not an int. The moment one side of a division is a decimal, Java does decimal division and keeps the fraction, so 7.0 / 2 is3.5 and exact prints 3.5. The single character .0 changed the whole behavior.
If both sides of
/are integers, you get an integer answer with the fraction thrown away. If either side is a decimal, you get a decimal answer. The types decide, not the math.
double average = 7 / 2; does not give you 3.5. Java works out 7 / 2first, while both are still int, gets 3, and only then copies that 3 into thedouble as 3.0. Declaring the result as a double is too late. The damage is already done inside the division. We will fix this properly with a cast in a moment.The %operator earns its place. Here are a few results to build intuition. Read each one as “what is left after dividing”.
public class Main { public static void main(String[] args) { System.out.println(17 % 5); System.out.println(20 % 4); System.out.println(5 % 7); System.out.println(13 % 2); }}2 0 5 1
Walk through them. 17 % 5: five goes into seventeen three times (fifteen) with 2 left, so 2. 20 % 4: four divides twenty exactly, nothing left, so 0, which is the signal that one number divides another cleanly. 5 % 7: seven does not go into five at all, so the whole 5 is left over, giving 5. And 13 % 2 is 1 because thirteen is odd. A result of 0 from % 2 means even; 1 means odd.
So far our operators take numbers and give back numbers. The next group takes two values and gives back a boolean: either true or false. These are the comparison operators, and they are how a program asks a question.
== equal to (two equals signs, because one is already assignment)!= not equal to< less than, and > greater than<= less than or equal to, and >= greater than or equal topublic class Main { public static void main(String[] args) { int age = 20; boolean isAdult = age >= 18; boolean isExactly20 = age == 20; boolean isTeenager = age < 13; System.out.println(isAdult); System.out.println(isExactly20); System.out.println(isTeenager); }}true true false
Each comparison is worked out to a single true or false and stored in a boolean, exactly the type you met last session. age >= 18 is true, so isAdult is true. age == 20 is true. age < 13 is false, because 20 is not less than 13. The program prints true, true, false.
= is assignment: x = 5 puts 5 into x. == is a question: x == 5 asks whether x holds 5 and answers true or false. Mixing them up is one of the most common early mistakes. When you mean to compare, count the equals signs: you want two.true: serve adults one page, minors another. That branching is the whole of the next session, making decisions with if-else. For now, just get comfortable building conditions that come out right.Often one question is not enough. “Is the person an adult anda member?” “Is today Saturday or Sunday?” To join booleans you use the logical operators:
&& means and: true only when both sides are true.|| means or: true when at least one side is true.! means not: it flips a boolean, turning true into false and back.public class Main { public static void main(String[] args) { int age = 25; boolean hasTicket = true; boolean canEnter = age >= 18 && hasTicket; boolean weekend = false || true; boolean isMinor = !(age >= 18); System.out.println(canEnter); System.out.println(weekend); System.out.println(isMinor); }}true true false
Trace the first one. age >= 18 is true and hasTicket is true, and && needs both sides true, so canEnter is true. The second, false || true, needs only one side true, so weekend is true. The third takes age >= 18, which is true, and ! flips it, so isMinor is false. The program prints true, true, false.
We promised to fix the integer division bug, and casting is the tool. A cast is you telling Java, on purpose, to treat a value as a different type for this one expression. You write the target type in parentheses in front of the value: (double) x means “take x and hand it over as a doubleright here”.
public class Main { public static void main(String[] args) { int total = 7; int count = 2; double wrong = total / count; double right = (double) total / count; double careful = (double) (total / count); System.out.println(wrong); System.out.println(right); System.out.println(careful); }}3 3.5 3.0
Three lines, three different stories. wrong is the trap from earlier: total / count is7 / 2 with both still int, so it is 3, and copying that into a doublegives 3.0. The cast came too late, after the integer division had already happened.
right is the fix. (double) total turns 7 into 7.0 before the division, so the division sees a decimal on the left and does decimal division: 7.0 / 2 is 3.5. You only need to cast one side; the other comes along. This is the pattern to remember for averages and percentages.
careful shows why position matters. The parentheses force total / count to happen first, while both are still int, giving 3, and only then is that 3 cast to 3.0. So it prints 3.0, same as wrong. The lesson: (double) total / count and (double) (total / count) are not the same. Cast a piece before it is divided, not the answer after.
To get a decimal answer from integer division, cast one operand to
doublebefore the/.(double) a / bworks;(double) (a / b)is too late.
Casts go both directions. Putting (int) in front of a double does the opposite job: it truncates, cutting off everything after the decimal point, no rounding.
public class Main { public static void main(String[] args) { double price = 9.99; int dollars = (int) price; double almostFour = 3.99; int chopped = (int) almostFour; System.out.println(dollars); System.out.println(chopped); }}9 3
(int) 9.99 is 9, and (int) 3.99 is 3. Both prove the point: Java does not round to the nearest whole number, it simply drops the fraction. If you wanted real rounding you would reach for Math.round instead, but for now know that a plain (int) cast truncates.
double d = 5; works with no cast, because every int fits in a double and becomes 5.0. You only need an explicit cast when you might lose information, like squeezing a double into an intand dropping the fraction. The cast is Java asking you to confirm: “yes, I know I might lose something here”.Adding one to a variable is so common that Java gives it a shorthand. x++means “increase x by one”, exactly like writing x = x + 1;. Its partner x-- decreases by one. These are the increment and decrementoperators, and you will see them constantly once we reach loops.
public class Main { public static void main(String[] args) { int x = 5; x++; System.out.println(x); x--; x--; System.out.println(x); }}6 4
Starting at 5, one x++ makes it 6, then two x-- bring it down to 4. Each one nudges the value by exactly one.
There is a subtlety worth seeing once. You can write the operator before the variable (++x, the pre form) or after it (x++, the post form). The variable ends up increased either way. The difference only shows when you use the result in the same line: ++x increases first and then hands back the new value, while x++ hands back the old value and then increases.
public class Main { public static void main(String[] args) { int a = 5; int afterPost = a++; // hands back 5, THEN a becomes 6 System.out.println(afterPost); // 5 System.out.println(a); // 6 int b = 5; int afterPre = ++b; // b becomes 6 FIRST, then hands back 6 System.out.println(afterPre); // 6 System.out.println(b); // 6 }}5 6 6 6
x++ sits on its own line, as a whole statement, pre and post behave identically; both just add one. The difference only bites when you capture the result in the same expression. Until you have a reason, prefer the plain x++; on its own line, which is the common case in loops.Just as x++ shortens x = x + 1, Java shortens the pattern “change a variable using its own current value” for every arithmetic operator. x += 3 means x = x + 3. The same works for the others.
x += 3x = x + 3x -= 3x = x - 3x *= 3x = x * 3x /= 3x = x / 3x %= 3x = x % 3public class Main { public static void main(String[] args) { int total = 10; total += 5; System.out.println(total); total *= 2; System.out.println(total); total -= 3; System.out.println(total); }}15 30 27
Starting at 10: += 5 makes it 15, *= 2 makes it 30, and -= 3 makes it 27. Each shorthand reads its own value, applies the operator, and stores the result back in place.
Recall from last session that a char is really a small number under the hood, the code that stands for a letter. Casting lets you cross between the letter and that number on purpose.
public class Main { public static void main(String[] args) { char letter = 'A'; int code = (int) letter; char fromCode = (char) 66; char next = (char) (letter + 1); System.out.println(code); System.out.println(fromCode); System.out.println(next); }}65 B B
(int) 'A' asks for the code behind the letter A, which is 65, so code is 65. Going back, (char) 66 asks for the letter whose code is 66, which is B. And (char) (letter + 1) is the clever one: letter + 1 adds 1 to A's code, giving 66, and the cast turns that back into the letter B. So the program prints 65, B, B. The letters of the alphabet sit in order by code, which is why adding one steps to the next letter.
When several operators sit in one expression, Java needs a rule for the order. It is the same order you learned in arithmetic: multiplication and division happen before addition and subtraction. This ordering is called precedence.
public class Main { public static void main(String[] args) { System.out.println(2 + 3 * 4); System.out.println((2 + 3) * 4); System.out.println(10 - 4 - 3); System.out.println(20 / 4 * 2); }}14 20 3 10
First line: 3 * 4 binds tighter than the +, so Java does 3 * 4 to get 12, then 2 + 12 is 14. Second line: the parentheses override that and force 2 + 3 first, giving 5, then 5 * 4 is 20. Parentheses always win, which is exactly what makes them so useful.
The last two lines show what happens when operators are at the same level. Then Java works left to right. 10 - 4 - 3 is (10 - 4) - 3, which is 6 - 3, which is 3. And 20 / 4 * 2 is (20 / 4) * 2, which is 5 * 2, which is 10: * and / share a level, so neither jumps ahead, they just go in reading order.
! and casts like (int)*, /, %+, -+, -< > <= >=== !=&& then ||= last of allTry each one yourself first, then open the answer.
9 / 2 print, and what does 9 % 2 print? Say why each is what it is.double avg = 5 / 2; expecting 2.5, but it prints 2.0. What went wrong, and how do you fix it with a cast?2 + 6 / 2 by hand. Then work out (2 + 6) / 2. Are they the same?age >= 13 && age <= 19 produce when age is 16? What about when age is 21?(char) ('a' + 2) evaluate to, given that the code for 'a' is 97?Take these away. They continue exactly what we just did.
int minutes = 200;. Using only / and %, compute and print how many whole hours that is and how many minutes are left over. Confirm you get 3 hours and 20 minutes.int variables, then prints their exact average as a decimal. Make sure the answer keeps its fraction, for example three scores of 7, 8, and 10 should give 8.333..., not 8.0.10 % 3 + 4 * 2 - 1 step by step, writing down each intermediate result and the precedence rule you used. Then type it into the playground and check your answer matches.char grade = 'C';. Write code that prints the letter two grades better (so the letter A) by casting between char and its code. Then in a comment, explain in one sentence why the (char) cast is needed and what happens if you leave it out.