Java · lesson 4 of 66
Beginner
17- 1 Introduction to Java
- 2 Setting Up Java
- 3 Data Types in Java
- 4 Operators in Java
- 5 Variables and Operators in Java
- 6 Control Flow in Java
- 7 Arrays in Java
- 8 Functions (Methods) in Java
- 9 Strings in Java
- 10 Methods in Java
- 11 Java OOP — Complete Guide
- 12 Introduction to Object-Oriented Programming in Java
- 13 Encapsulation in Java
- 14 Inheritance in Java
- 15 Polymorphism in Java
- 17 Abstraction in Java
- 18 Error Types in Java
Intermediate
38- 16 Regular Expressions in Java
- 19 Interfaces in Java — A Deep Dive
- 20 Abstract Classes in Java
- 21 Method Overloading in Java
- 22 Composition vs Inheritance in Java
- 23 Generics in Java
- 27 Exception Handling in Java
- 28 Java Streams API
- 29 Collections Framework in Java
- 30 Optional in Java
- 31 File Handling in Java
- 32 Records in Java
- 33 Java 8 Features
- 34 Sealed Classes and Interfaces in Java
- 35 Multithreading in Java
- 37 JDBC — Database Access in Java
- 39 Maven — Java Build and Dependency Management
- 42 Gradle — Modern Java Build Tool
- 44 Unit Testing with JUnit 5 and Mockito
- 45 Logging in Java with SLF4J and Logback
- 49 Spring Boot — Introduction
- 50 Dependency Injection in Spring Boot
- 51 Java Project Ideas
- 52 Building REST APIs with Spring Boot
- 53 Validation in Spring Boot
- 54 Spring Data JPA — Database Access
- 55 Exception Handling in Spring Boot
- 56 Spring Security — Authentication and Authorization
- 57 JWT Authentication in Spring Boot
- 58 Dockerizing Spring Boot Applications
- 59 Java Enums: Beyond Simple Constants
- 60 Inner and Anonymous Classes in Java
- 61 Java Generics: Bounded Types, Wildcards, and Generic Methods
- 62 Java Date/Time API: LocalDate, LocalDateTime, and Duration
- 63 Java NIO.2: Paths, Files, and WatchService
- 64 Java Annotations: Custom Types and Meta-Information
- 65 Java Reflection: Dynamic Type Inspection and Method Invocation
- 66 Java Localization: ResourceBundle and Locale Support
Advanced
11Operators in Java
Run Java's operators and see what they really do: integer division, overflow, ++, short-circuit logic, bitwise and shift operators, ternary, instanceof.
By SkillByExample · Updated
Operators combine values into new values. Most behave as in school arithmetic — the interesting
parts are where Java’s types change the answer. Every example is a single file run with the JDK 21
source launcher (java File.java).
Arithmetic: integer division and remainder
public class Arithmetic {
public static void main(String[] args) {
System.out.println(7 + 2 + " " + (7 - 2) + " " + 7 * 2);
System.out.println("7 / 2 = " + 7 / 2);
System.out.println("7 / 2.0 = " + 7 / 2.0);
System.out.println("-7 / 2 = " + -7 / 2);
System.out.println("7 % 3 = " + 7 % 3);
System.out.println("-7 % 3 = " + -7 % 3);
System.out.println("5.5 % 2 = " + 5.5 % 2);
System.out.println("1 + 2 + \"3\" = " + (1 + 2 + "3"));
}
}
$ java Arithmetic.java
9 5 14
7 / 2 = 3
7 / 2.0 = 3.5
-7 / 2 = -3
7 % 3 = 1
-7 % 3 = -1
5.5 % 2 = 1.5
1 + 2 + "3" = 33
Two ints give an int, so 7 / 2 truncates to 3, and toward zero for negatives. % takes the
sign of the left operand, so -7 % 3 is -1 — use Math.floorMod when you need a non-negative
result. + works left to right: 1 + 2 is added before meeting the string, and 7 + 2 on the first
line became "9" the same way.
Overflow wraps silently
public class Overflow {
public static void main(String[] args) {
int big = Integer.MAX_VALUE;
System.out.println(big);
System.out.println(big + 1);
long ok = big + 1L;
System.out.println(ok);
System.out.println(1 / 0.0 + " " + (0.0 / 0.0));
System.out.println(Math.addExact(big, 1));
}
}
$ java Overflow.java
2147483647
-2147483648
2147483648
Infinity NaN
Exception in thread "main" java.lang.ArithmeticException: integer overflow
at java.base/java.lang.Math.addExact(Math.java:911)
at Overflow.main(Overflow.java:9)
int arithmetic wraps around from the largest value to the smallest without any error. Making one
operand a long widens the whole expression. Floating point never throws on division by zero; it
yields Infinity or NaN. Math.addExact throws instead of wrapping when correctness matters.
Increment, decrement and compound assignment
public class Increment {
public static void main(String[] args) {
int a = 5;
int b = a++; // use, then increment
int c = ++a; // increment, then use
System.out.println("a=" + a + " b=" + b + " c=" + c);
int total = 10;
total += 5;
total *= 2;
total -= 3;
total /= 4;
System.out.println("total=" + total);
byte small = 120;
small += 10; // hidden cast back to byte
System.out.println("small=" + small);
}
}
$ java Increment.java
a=7 b=5 c=7
total=6
small=-126
a++ evaluates to the old value and ++a to the new one; on a line by themselves they are
identical. Compound assignment casts its result back to the variable’s type, so small += 10
compiles and overflows to -126 — whereas small = small + 10 would not compile, because
small + 10 is an int.
Comparing values and objects
public class Equality {
public static void main(String[] args) {
System.out.println((3 < 5) + " " + (3 >= 5) + " " + (3 != 5));
String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println("a == b " + (a == b));
System.out.println("a == c " + (a == c));
System.out.println("a.equals(c) " + a.equals(c));
Integer x = 127, y = 127, p = 128, q = 128;
System.out.println("127 == 127 " + (x == y));
System.out.println("128 == 128 " + (p == q));
System.out.println("0.1 + 0.2 == 0.3 " + (0.1 + 0.2 == 0.3));
}
}
$ java Equality.java
true false true
a == b true
a == c false
a.equals(c) true
127 == 127 true
128 == 128 false
0.1 + 0.2 == 0.3 false
On objects, == compares references. String literals are interned, so a and b share one object
while new String makes another. Boxed Integers from -128 to 127 are cached, which makes ==
appear to work until 128. And 0.1 + 0.2 is not exactly 0.3 in binary floating point — compare
doubles with a tolerance.
Logical operators short-circuit
&& and || skip the right-hand side when the left already decides the answer.
public class ShortCircuit {
static boolean check(String label, boolean result) {
System.out.println(" evaluated " + label);
return result;
}
public static void main(String[] args) {
System.out.println("false && ...");
boolean r1 = check("left", false) && check("right", true);
System.out.println("true || ...");
boolean r2 = check("left", true) || check("right", false);
System.out.println("false & ... (no short-circuit)");
boolean r3 = check("left", false) & check("right", true);
System.out.println(r1 + " " + r2 + " " + r3 + " " + !r1);
String s = null;
System.out.println(s != null && s.isEmpty());
}
}
$ java ShortCircuit.java
false && ...
evaluated left
true || ...
evaluated left
false & ... (no short-circuit)
evaluated left
evaluated right
false true false true
false
The last line is the everyday use: s.isEmpty() would throw on null, but && never reaches it.
& and | evaluate both sides, which is rarely what you want with booleans.
Bitwise operators
&, |, ^ and ~ work on each bit of an integer.
public class Bits {
static String bin(int n) {
return String.format("%8s", Integer.toBinaryString(n & 0xFF)).replace(' ', '0');
}
public static void main(String[] args) {
int a = 0b1100, b = 0b1010;
System.out.println("a " + bin(a));
System.out.println("b " + bin(b));
System.out.println("a & b " + bin(a & b) + " = " + (a & b));
System.out.println("a | b " + bin(a | b) + " = " + (a | b));
System.out.println("a ^ b " + bin(a ^ b) + " = " + (a ^ b));
System.out.println("~a = " + ~a);
final int READ = 1, WRITE = 2, EXEC = 4;
int perms = READ | WRITE;
System.out.println("can write? " + ((perms & WRITE) != 0) + ", can exec? " + ((perms & EXEC) != 0));
perms &= ~WRITE;
System.out.println("after revoking write: " + perms);
}
}
$ java Bits.java
a 00001100
b 00001010
a & b 00001000 = 8
a | b 00001110 = 14
a ^ b 00000110 = 6
~a = -13
can write? true, can exec? false
after revoking write: 1
& keeps bits set in both, | bits set in either, ^ bits set in exactly one. ~a flips every bit
of the 32-bit int, which in two’s complement equals -a - 1. Packing flags into one int is the
classic use: | sets a flag, & FLAG tests it, &= ~FLAG clears it.
Shift operators
public class Shifts {
public static void main(String[] args) {
System.out.println("1 << 4 = " + (1 << 4));
System.out.println("80 >> 2 = " + (80 >> 2));
System.out.println("-16 >> 2 = " + (-16 >> 2));
System.out.println("-16 >>> 2 = " + (-16 >>> 2));
System.out.println("1 << 32 = " + (1 << 32));
System.out.println("1L << 32 = " + (1L << 32));
}
}
$ java Shifts.java
1 << 4 = 16
80 >> 2 = 20
-16 >> 2 = -4
-16 >>> 2 = 1073741820
1 << 32 = 1
1L << 32 = 4294967296
<< n multiplies by 2ⁿ and >> n divides by it, keeping the sign. >>> shifts in zeros, so a
negative number becomes a large positive one. An int shift distance is taken modulo 32, which is
why 1 << 32 is 1; shift a long to use all 64 bits.
The ternary operator
condition ? a : b is an expression, so it can sit inside an assignment or a call.
public class Ternary {
public static void main(String[] args) {
for (int count : new int[] {0, 1, 5}) {
String label = count == 1 ? "item" : "items";
System.out.println(count + " " + label);
}
int score = 72;
String grade = score >= 90 ? "A" : score >= 70 ? "B" : "C";
System.out.println("grade " + grade);
System.out.println(true ? 1 : 2.0);
}
}
$ java Ternary.java
0 items
1 item
5 items
grade B
1.0
Chained ternaries read top to bottom like an if/else if ladder, but more than two levels hurts
readability. The last line is a trap: when the branches are int and double, the result type is
double, so 1 prints as 1.0.
instanceof and pattern matching
public class InstanceOf {
static String describe(Object o) {
if (o instanceof String s && !s.isEmpty()) {
return "String of length " + s.length();
} else if (o instanceof Integer n) {
return "Integer, doubled " + n * 2;
}
return "something else: " + o;
}
public static void main(String[] args) {
System.out.println(describe("hello"));
System.out.println(describe(21));
System.out.println(describe(""));
System.out.println(describe(null));
System.out.println(null instanceof Object);
}
}
$ java InstanceOf.java
String of length 5
Integer, doubled 42
something else:
something else: null
false
o instanceof String s tests and casts in one step; s can even be used later in the same &&
condition. instanceof is always false for null, so it doubles as a null check.
Precedence
When an expression mixes operators, precedence decides grouping. Parentheses override it.
public class Precedence {
public static void main(String[] args) {
System.out.println(2 + 3 * 4);
System.out.println((2 + 3) * 4);
System.out.println(10 - 4 - 3); // left to right
System.out.println(2 + 3 > 4 && 1 < 0); // arithmetic, then comparison, then &&
boolean t = true, f = false;
System.out.println(t || f && f); // && binds tighter than ||
}
}
$ java Precedence.java
14
20
3
false
true
Multiplication binds tighter than addition, comparisons tighter than &&, and && tighter than
||, so the last line is t || (f && f). Equal-precedence operators group left to right.
Bitwise operators sit below ==, which catches out everyone testing a flag:
public class BadPrecedence {
public static void main(String[] args) {
System.out.println(8 & 4 == 4);
}
}
$ java BadPrecedence.java
BadPrecedence.java:3: error: bad operand types for binary operator '&'
System.out.println(8 & 4 == 4);
^
first type: int
second type: boolean
1 error
error: compilation failed
It parses as 8 & (4 == 4) — an int AND a boolean. When in doubt, add parentheses.
Practice
- Fix
BadPrecedencewith parentheses so it prints whether bit4is set in8.
Solution
public class BitTest {
public static void main(String[] args) {
System.out.println((8 & 4) == 4);
System.out.println((12 & 4) == 4);
}
}
$ java BitTest.java
false
true
8 is 1000 in binary and has no 0100 bit; 12 is 1100 and does.
- Without using
%, test whether a number is even with a bitwise operator. Print the results for-3to3.
Solution
public class EvenBits {
public static void main(String[] args) {
for (int n = -3; n <= 3; n++) {
System.out.println(n + " even? " + ((n & 1) == 0));
}
}
}
$ java EvenBits.java
-3 even? false
-2 even? true
-1 even? false
0 even? true
1 even? false
2 even? true
3 even? false
The lowest bit is 1 exactly for odd numbers, including negative ones in two’s complement — unlike
n % 2 == 1, which is false for -3 because the remainder is -1.
- Compute the average of the
ints7and8so it prints7.5, not7.
Solution
public class Average {
public static void main(String[] args) {
int a = 7, b = 8;
System.out.println((a + b) / 2);
System.out.println((a + b) / 2.0);
System.out.println((double) (a + b) / 2);
}
}
$ java Average.java
7
7.5
7.5
The cast must apply before the division; (double) ((a + b) / 2) would convert the already
truncated 7.
- Use
Math.floorModto wrap a day-of-week index: starting from day1, go back10days in a 7-day week. Compare it with%.
Solution
public class Wrap {
public static void main(String[] args) {
int day = 1, offset = -10;
System.out.println("% -> " + (day + offset) % 7);
System.out.println("floorMod -> " + Math.floorMod(day + offset, 7));
}
}
$ java Wrap.java
% -> -2
floorMod -> 5
% keeps the dividend’s sign and gives -2, which is not a valid index; floorMod always returns
a value from 0 to 6.