Java language essentials
The Java you need for every later lesson: primitive types, variables, operators and precedence, control flow, methods, scope, overloading, static members and access modifiers. Examples target Java 17.
Learning goals
- Choose the right primitive type and know its range
- Predict the value of an expression using precedence and integer division
- Write methods with correct scope, overloads and access control
Prerequisites
Primitive types
Java has eight primitive types. For algorithms you mostly use int (32-bit), long (64-bit), double, boolean and char. byte and short appear in memory-tight arrays.
Integer types are signed two's complement, so the negative range is one larger than the positive range.
| Type | Size | Range / values |
|---|---|---|
| byte | 8 bits | -128 to 127 |
| short | 16 bits | -32,768 to 32,767 |
| int | 32 bits | -2,147,483,648 to 2,147,483,647 |
| long | 64 bits | -9.22 × 10¹⁸ to 9.22 × 10¹⁸ |
| float / double | 32 / 64 bits | IEEE 754 floating point |
| char | 16 bits | 0 to 65,535 (UTF-16 code unit) |
| boolean | JVM-defined | true or false |
1int max = Integer.MAX_VALUE; // 21474836472long big = 10_000_000_000L; // L suffix: the literal does not fit in an int3double d = 3.5;4char c = 'A'; // numeric value 655boolean ok = max > 0;
Variables
A variable has a type, a name and a value. Declare it close to first use, give it the narrowest scope, and mark values that never change with final. Since Java 10 var infers the type of a local variable from its initialiser.
Local variables have no default value and must be assigned before use; the compiler rejects reading them otherwise.
1final int n = 5; // cannot be reassigned2var list = new ArrayList<Integer>(); // inferred: ArrayList<Integer>3int total; // declared, not yet assigned4total = n * 2; // must happen before any read
Operators
Arithmetic (+ - * / %), comparison (== != < <= > >=), logical (&& || !, short-circuit), bitwise (& | ^ ~ << >> >>>), assignment (= += -= …), and the ternary cond ? a : b.
Integer division truncates toward zero, so 7 / 2 is 3 and -7 / 2 is -3. At least one operand must be floating point for a fractional result.
1int a = 7 / 2; // 32double b = 7 / 2; // 3.0 (integer division first, then widened)3double c = 7 / 2.0; // 3.54int r = 7 % 3; // 15boolean x = n > 0 && 10 / n > 1; // && stops early, so no division by zero when n == 0
Precedence
Higher precedence binds first: unary, then * / %, then + -, then shifts, then relational, then ==, then &, ^, |, then &&, ||, ternary, assignment.
The trap: == binds tighter than &, so x & 1 == 0 means x & (1 == 0) and does not compile for ints. Write (x & 1) == 0. When in doubt, add parentheses.
1int v = 1 + 2 * 3; // 72int w = (1 + 2) * 3; // 93boolean even = (v & 1) == 0; // parentheses are required4int s = 1 << 2 + 1; // 8: + binds tighter than <<, so 1 << 3
Conditions
if / else if / else for branching, the ternary for simple choices, and switch for many constants. Java 17 supports arrow-form switch expressions that return a value and do not fall through.
Compare strings and boxed values with equals, never ==, which compares references.
1int day = 3;2String name = switch (day) {3case 1, 7 -> "weekend";4case 2, 3, 4, 5, 6 -> "weekday";5default -> "invalid";6};7boolean same = "ab".equals(new String("ab")); // true; == would be false
Loops
for when the count is known, while when it depends on a condition, do … while when the body must run once, and enhanced for to read every element. Use break to leave a loop and continue to skip to the next iteration.
Choose the form that makes the loop's invariant and exit obvious. The enhanced for cannot modify array elements by index or know the index.
1int[] a = {4, 9, 2};2for (int i = 0; i < a.length; i++) a[i] *= 2; // index needed to write3int sum = 0;4for (int x : a) sum += x; // read-only traversal5int n = 100, steps = 0;6while (n > 1) { n /= 2; steps++; } // steps = 6
Methods
A method has a return type, name, parameters and body. Java passes everything by value: primitives are copied, and for objects the reference is copied. A method can mutate an object it receives but cannot make the caller's variable point somewhere else.
Keep methods short and name them for what they return or do.
1static int sumTo(int n) {2int s = 0;3for (int i = 1; i <= n; i++) s += i;4return s;5}67static void setFirst(int[] a) { a[0] = 99; } // caller sees the change8static void reassign(int[] a) { a = new int[]{1}; } // caller does not
Scope
A variable is visible only inside the block where it is declared. Loop variables exist for the loop, parameters for the method, fields for the object. A local variable may not shadow another local in an enclosing block.
Declare variables in the smallest scope. It reduces bugs and lets the JIT reason better about them.
1int total = 0;2for (int i = 0; i < 3; i++) { int sq = i * i; total += sq; }3// i and sq no longer exist here; reading them is a compile error
Overloading
Several methods may share a name if their parameter lists differ in type or count. The compiler picks the best match at compile time using the declared argument types. The return type alone cannot distinguish overloads.
Beware of autoboxing with overloads: list.remove(int index) and list.remove(Object o) behave differently for a List<Integer>.
1static int max(int a, int b) { return a > b ? a : b; }2static long max(long a, long b) { return a > b ? a : b; }3static int max(int[] a) { int m = a[0]; for (int x : a) m = Math.max(m, x); return m; }45List<Integer> list = new ArrayList<>(List.of(10, 20, 30));6list.remove(1); // removes index 1 -> [10, 30]7list.remove(Integer.valueOf(10)); // removes the value 10 -> [30]
Static members
A static field or method belongs to the class, not to an instance. Utility methods and constants are static. A static method cannot use this or instance fields.
In contest-style code most helpers are static so main can call them directly. Avoid mutable static state in library code; it complicates testing and threads.
1final class MathUtil {2static final int MOD = 1_000_000_007; // constant3private MathUtil() {} // no instances4static int addMod(int a, int b) { return (int) (((long) a + b) % MOD); }5}
Access modifiers
private: only the same class. Package-private (no modifier): same package. protected: package plus subclasses. public: everywhere. Make fields private and expose behaviour through methods so internal representation can change.
Top-level classes may be public or package-private. A nested helper class is typically private static.
| Modifier | Same class | Same package | Subclass | Anywhere |
|---|---|---|---|---|
| private | yes | no | no | no |
| (none) | yes | yes | no | no |
| protected | yes | yes | yes | no |
| public | yes | yes | yes | yes |
Practice ladder
Now try it yourself
Easy
- Predict
7 / 2,-7 / 2,7 % 3,-7 % 3and7 / 2.0 - Write a method that returns the larger of three ints
Medium
- Explain why
x & 1 == 0does not compile and fix it - Write a switch expression that maps a month number to its number of days (non-leap year)
Advanced
- Explain what
list.remove(1)does forList<Integer>compared withlist.remove(Integer.valueOf(1)), and why
Key takeaways
- Know int and long ranges; integer division truncates toward zero.
- Parenthesise mixed bitwise and comparison expressions.
- Java is pass-by-value; objects are passed as copied references.