The last post in the track is a reference rather than a lesson: the lookups you will make over and over, in one place. Every snippet here compiles as written against Java 21.
Strings
String s = " Hello, World ";
s.strip(); // "Hello, World" (not trim)
s.isBlank(); // false (not isEmpty)
s.toLowerCase().contains("world"); // true
String.join(", ", List.of("a", "b")); // "a, b"
"a,b,c".split(","); // ["a", "b", "c"] (regex — escape . and |)
"ab".repeat(3); // "ababab"
"%s is %d".formatted("age", 30); // "age is 30"
// Building one across iterations
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++) sb.append(i).append(",");
sb.toString(); // "0,1,2,"
// Reverse
new StringBuilder("Java").reverse().toString(); // "avaJ"
Numbers and parsing
Integer.parseInt("42"); // 42 — NumberFormatException if not a number
Double.parseDouble("3.14");
String.valueOf(42); // "42"
Integer.toString(255, 16); // "ff"
Math.round(3.6); // 4 — (int) 3.6 would truncate to 3
Math.max(3, 7);
Math.abs(-5);
double avg = (double) 17 / 5; // 3.4 — cast BEFORE dividing
// Money — never double
new BigDecimal("19.99").multiply(new BigDecimal("3"))
.setScale(2, RoundingMode.HALF_UP); // 59.97
// Safe parse
Optional<Integer> parsed;
try {
parsed = Optional.of(Integer.parseInt("nope"));
} catch (NumberFormatException e) {
parsed = Optional.empty();
}
Dates and times
LocalDate.now();
LocalDate.of(2026, 8, 20); // month is 1-based
LocalDate.parse("2026-08-20"); // ISO by default
Instant.now(); // store this, not LocalDateTime
LocalDate d = LocalDate.of(2026, 8, 20);
d.plusDays(7); // immutable — assign the result
d.withDayOfMonth(1); // 2026-08-01
d.with(TemporalAdjusters.lastDayOfMonth()); // 2026-08-31
ChronoUnit.DAYS.between(d, LocalDate.of(2026, 12, 25)); // 127
Period.between(LocalDate.of(1990, 5, 14), d).getYears(); // 36
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/yyyy"); // MM month, mm minutes
d.format(f); // "20/08/2026"
Instant.now().atZone(ZoneId.of("Pacific/Tongatapu"));
Collections
List<String> fixed = List.of("a", "b"); // immutable
List<String> list = new ArrayList<>(fixed); // mutable copy
Map<String, Integer> map = new HashMap<>();
map.getOrDefault("missing", 0); // 0, not null
map.merge("count", 1, Integer::sum); // increment or start at 1
Map<String, List<String>> multi = new HashMap<>();
multi.computeIfAbsent("k", k -> new ArrayList<>()).add("v"); // build a multimap
list.removeIf(s -> s.startsWith("a")); // safe removal while iterating
Collections.sort(list);
list.sort(Comparator.comparing(String::length).reversed());
// Array <-> List
String[] arr = list.toArray(new String[0]);
List<String> back = new ArrayList<>(Arrays.asList(arr)); // wrap: asList alone is fixed-size
Arrays.sort(new int[]{3, 1, 2}); // in place, returns void
Arrays.toString(new int[]{1, 2}); // "[1, 2]"
Iterating
List<String> names = new ArrayList<>(List.of("Ana", "Bo"));
Map<String, Integer> ages = new HashMap<>(Map.of("Ana", 30));
for (String name : names) { } // no index needed
for (int i = 0; i < names.size(); i++) { } // index needed
names.forEach(System.out::println);
for (Map.Entry<String, Integer> e : ages.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
ages.forEach((k, v) -> System.out.println(k + "=" + v));
// Removing while iterating
names.removeIf(n -> n.startsWith("A")); // not remove() inside a for-each
Streams
List<String> names = List.of("Ana", "Bo", "Christopher");
names.stream().filter(n -> n.length() > 2).toList();
names.stream().map(String::toUpperCase).toList();
names.stream().anyMatch(n -> n.startsWith("B")); // true
names.stream().findFirst().orElse("none");
names.stream().collect(Collectors.joining(", "));
List.of(1, 2, 3).stream().mapToInt(Integer::intValue).sum(); // 6
IntStream.rangeClosed(1, 5).sum(); // 15
// Group, and group-and-count
record Person(String name, String city) { }
List<Person> people = List.of(new Person("Ana", "Seattle"));
Map<String, List<Person>> byCity =
people.stream().collect(Collectors.groupingBy(Person::city));
Map<String, Long> countByCity =
people.stream().collect(Collectors.groupingBy(Person::city, Collectors.counting()));
Map<String, String> index =
people.stream().collect(Collectors.toMap(Person::name, Person::city, (a, b) -> b));
Files
class FileSnippets {
void run() throws IOException {
Path path = Path.of("data.txt");
String all = Files.readString(path); // whole file
List<String> lines = Files.readAllLines(path);
Files.writeString(path, "hello"); // overwrite
Files.writeString(path, "more\n", StandardOpenOption.APPEND);
Files.exists(path);
Files.createDirectories(Path.of("a/b/c"));
// Large files — stream instead of loading it all
try (Stream<String> stream = Files.lines(path)) {
stream.filter(l -> l.contains("ERROR")).forEach(System.out::println);
}
}
}
Records, Optional and switch
record Point(int x, int y) {
Point {
if (x < 0) throw new IllegalArgumentException("x must not be negative");
}
}
class Snippets {
void run() {
Point p = new Point(3, 4);
p.x(); // accessor is x(), not getX()
Optional<String> maybe = Optional.ofNullable(null);
maybe.orElse("default");
maybe.orElseGet(() -> "computed"); // lazy
maybe.map(String::toUpperCase).orElse("none");
maybe.ifPresent(System.out::println);
int day = 6;
String type = switch (day) {
case 1, 2, 3, 4, 5 -> "weekday";
case 6, 7 -> "weekend";
default -> "invalid";
};
System.out.println(type); // weekend
}
}
Async with CompletableFuture
CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> "one");
CompletableFuture<String> b = CompletableFuture.supplyAsync(() -> "two");
CompletableFuture.allOf(a, b).join(); // start both, THEN wait
System.out.println(a.join() + b.join());
CompletableFuture
.supplyAsync(() -> "x")
.thenApply(String::toUpperCase)
.exceptionally(e -> "fallback")
.join();
Equality, null and comparison
String a = "hello", b = "hello";
boolean same = a.equals(b); // true — compare objects with equals
boolean identity = (a == b); // true here, but do NOT rely on it
boolean safe = "hello".equals(a); // null-safe: literal first
boolean bothSafe = Objects.equals(a, b); // null-safe both ways
Objects.requireNonNull(a, "a"); // fail fast with a named message
int h = Objects.hash(1, 2); // implementing hashCode
String orDefault = Objects.toString(null, "default"); // "default"
Integer x = 128, y = 128;
boolean equalValues = x.equals(y); // true — == would be false outside -128..127
Command line
javac Hello.java # compile -> Hello.class
java Hello # run (class name, no extension)
java Hello.java # compile in memory and run, single file
java -cp out com.example.App # with a classpath and a package
java -jar app.jar # a packaged jar
jstack <pid> # what every thread is doing right now
jmap -histo <pid> | head -20 # what is on the heap
That is the track
Twenty-nine posts from installing a JDK to writing modern Java. If you read it end to end, the thing to do now is build something small and use this page when you get stuck — the material only sticks once you have hit the errors yourself.
The console banking app that several of these examples came from is worth reading end to end once
you are comfortable: plain Java 21, no framework, no database, about a dozen small classes covering
records, enums, an exception hierarchy, BigDecimal money, file I/O and streams — all of
it in the shapes this track described.
For where to go next, the site has deeper tracks on Spring Boot, SQL, data structures and algorithms and backend development as a whole.