Programming foundations: Java

Sefism.

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
javac compiles .java source into bytecode (.class files). 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.
Shell
java --version
javac -d out src/app/Main.java
java -cp out app.Main
java Main.java

Watch 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 as List<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.
Java
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; // NullPointerException

Watch 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. equals checks whether two objects have the same value. hashCode must agree with equals for hash-based collections to work.
Why
HashMap and HashSet use hashCode to find the bucket and equals to match. Get either wrong and lookups silently fail.
Java
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)); // true

Watch out

Comparing strings with ==, or overriding equals without hashCode.

Records

Also called record class, data carrier

What
A record is a compact class for immutable data. Java writes the constructor, accessors, equals, hashCode and toString from 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.
Java
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.
Java
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 with throws. Unchecked ones extend RuntimeException (such as IllegalArgumentException) and need neither.
Why
Checked exceptions mark failures a caller can reasonably recover from, like a missing file. Unchecked ones signal programming errors.
Java
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 like filter, map and collect over a collection.
Why
Streams express "filter, group and sum" as a readable pipeline instead of nested loops with temporary maps.
Java
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.

03 · Java essentials3 / 10