Chapter 03
Java essentials
What makes Java Java: the JDK and JVM, primitives and objects, equality, records, generics, checked exceptions and streams.
JDK, JVM and bytecode
Also called javac, Java Virtual Machine, JDK, JVM
- What
javaccompiles.javasource into bytecode (.classfiles). The Java Virtual Machine (JVM) runs that bytecode, compiling hot parts to machine code as it goes. The JDK bundles both with the tools.- Why
- It explains "write once, run anywhere", and why you need a JDK (not just a runtime) installed to build. Use a current long-term support release such as 21 or 25.
java --version
javac -d out src/app/Main.java
java -cp out app.Main
java Main.javaWatch out
Letting the IDE hide the build completely, then being lost when a classpath or package error appears on the command line.
Primitives and wrapper types
Also called autoboxing, int vs Integer
- What
- Primitives (
int,double,boolean) hold raw values. Wrapper classes (Integer,Double) are objects around them, needed in collections such asList<Integer>. Java converts between them automatically. - Why
- Primitives are faster and never null. Wrappers can be null and are compared as objects, which matters the moment you use collections.
Integer b = 1000;
Integer c = 1000;
System.out.println(b == c); // false
System.out.println(b.equals(c)); // true
Integer missing = null;
int boom = missing; // NullPointerExceptionWatch out
Comparing two Integer objects with ==: it compares references and fails for values outside the small cached range.
equals and hashCode
Also called object equality, String equals
- What
==checks whether two references point to the same object.equalschecks whether two objects have the same value.hashCodemust agree withequalsfor hash-based collections to work.- Why
HashMapandHashSetusehashCodeto find the bucket andequalsto match. Get either wrong and lookups silently fail.
class Point {
final int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) {
return o instanceof Point p && p.x == x && p.y == y;
}
@Override public int hashCode() { return Objects.hash(x, y); }
}
Set<Point> seen = new HashSet<>();
seen.add(new Point(1, 2));
seen.contains(new Point(1, 2)); // trueWatch out
Comparing strings with ==, or overriding equals without hashCode.
Records
Also called record class, data carrier
- What
- A
recordis a compact class for immutable data. Java writes the constructor, accessors,equals,hashCodeandtoStringfrom the component list. - Why
- Use records for values such as money, coordinates or API responses instead of hand-writing boilerplate that is easy to get wrong.
record Money(long cents, String currency) {
Money {
if (cents < 0) throw new IllegalArgumentException();
}
Money plus(Money other) {
return new Money(cents + other.cents, currency);
}
}
var price = new Money(250, "GBP");
System.out.println(price); // Money[cents=250, currency=GBP]Watch out
Putting a mutable list in a record and assuming the record is immutable; copy it with List.copyOf in the constructor.
Generics
Also called type parameters, <T>
- What
- Generics let a class or method work with any type while keeping type checks, as in
List<String>or a method<T> T max(List<T> items). - Why
- The compiler stops you adding a number to a list of names, and you avoid casts. Every collection in the standard library uses them.
static <T extends Comparable<T>> T max(List<T> items) {
T best = items.getFirst();
for (T item : items) {
if (item.compareTo(best) > 0) best = item;
}
return best;
}
List<String> names = new ArrayList<>();
names.add("Ada");Watch out
Using raw types like List without a type argument, which turns off the checks and moves errors to runtime.
Checked and unchecked exceptions
Also called throws, RuntimeException, IOException
- What
- Checked exceptions (such as
IOException) must be caught or declared withthrows. Unchecked ones extendRuntimeException(such asIllegalArgumentException) and need neither. - Why
- Checked exceptions mark failures a caller can reasonably recover from, like a missing file. Unchecked ones signal programming errors.
static String load(Path path) throws IOException {
return Files.readString(path);
}
static int percent(int part, int whole) {
if (whole == 0) throw new IllegalArgumentException("whole");
return part * 100 / whole;
}Watch out
Catching a checked exception with an empty block just to make the compiler quiet.
Streams and lambdas
Also called Stream API, method references, functional interfaces
- What
- A lambda is a short anonymous function such as
o -> o.total() > 10. The Stream API chains operations likefilter,mapandcollectover a collection. - Why
- Streams express "filter, group and sum" as a readable pipeline instead of nested loops with temporary maps.
record Order(String customer, double total) {}
Map<String, Double> byCustomer = orders.stream()
.filter(o -> o.total() > 10)
.collect(Collectors.groupingBy(
Order::customer,
Collectors.summingDouble(Order::total)));Watch out
Using streams with side effects, such as adding to an outside list inside forEach; collect the result instead.