askesis

Java essentials: 50 cards on OOP, collections, generics and streams

Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.

ProgrammingBeginner50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
Java’s eight primitive types

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    Java’s eight primitive types
    byte, short, int, long, float, double, char and boolean; everything else, including String and arrays, is a reference type
  2. 2
    What does this print?
    java
    System.out.println(7 / 2);
    System.out.println(7 / 2.0);
    3, then 3.5: dividing two ints is integer division, which truncates toward zero
  3. 3
    == vs .equals() on strings
    == checks whether two references point to the same object; .equals() compares the characters. Use .equals() for content
    "a" == "a" can be true because literals are interned, which hides the bug until a string comes from user input.
  4. 4
    What does this print?
    java
    Integer a = 127, b = 127;
    Integer c = 128, d = 128;
    System.out.println(a == b);
    System.out.println(c == d);
    true, then false: autoboxing reuses cached Integer objects from -128 to 127, but larger values are separate objects. Compare boxed numbers with .equals()
  5. 5
    What does this print?
    java
    String s = "hello";
    s.toUpperCase();
    System.out.println(s);
    hello: strings are immutable, so toUpperCase() returns a new string, which is thrown away here
    Write s = s.toUpperCase(); to keep the result.
  6. 6
    Why use StringBuilder to build a string in a loop?
    Each s += x copies the whole string into a new object, which is quadratic over a loop; StringBuilder appends into a growable buffer
    java
    var sb = new StringBuilder();
    for (String w : words) sb.append(w).append(' ');
    String result = sb.toString();
  7. 7
    What does var do in Java?
    Infers a local variable’s type from its initializer at compile time (Java 10+); the variable is still statically typed
    Only for local variables with an initializer (and loop variables), not fields, parameters or return types.
  8. 8
    What is Integer.MAX_VALUE + 1?
    -2147483648 (Integer.MIN_VALUE): int arithmetic overflows silently and wraps around
    Math.addExact and friends throw ArithmeticException on overflow instead.
  9. 9
    Is Java pass-by-value or pass-by-reference?
    Always pass-by-value. For objects the value copied is the reference, so a method can mutate the object, but reassigning the parameter doesn’t affect the caller
  10. 10
    A switch expression with arrow labels
    java
    String size = switch (n) {
        case 0 -> "none";
        case 1, 2 -> "few";
        default -> "many";
    };
    Java 14+. Arrow cases never fall through, and a switch expression must cover every possible value.
  11. 11
    A multi-line string literal
    java
    String json = """
        {
          "name": "Ada"
        }
        """;
    Text blocks (Java 15+) strip the common indentation; the position of the closing """ counts too.
  12. 12
    Java’s four access levels
    private (the class only), package-private (no modifier: the same package), protected (the package plus subclasses) and public (everywhere)
  13. 13
    What does static mean on a field or method?
    It belongs to the class, not to an instance: there is one shared copy, accessed as ClassName.member. Static methods have no this
  14. 14
    final on a variable, a method and a class
    A final variable can’t be reassigned, a final method can’t be overridden, and a final class can’t be subclassed
    A final List<String> can still be modified with add; only the reference is fixed.
  15. 15
    Abstract class vs interface
    A class can extend only one class, which may have fields and constructors; it can implement many interfaces, which hold no instance state but can have default, static and private methods
  16. 16
    Overloading vs overriding
    Overloading: same name, different parameter lists, picked at compile time. Overriding: a subclass redefines an inherited method with the same signature, picked at runtime from the object’s actual class
  17. 17
    What does this print?
    java
    class Animal { String sound() { return "..."; } }
    class Dog extends Animal { String sound() { return "Woof"; } }
    
    Animal a = new Dog();
    System.out.println(a.sound());
    Woof: instance methods are dispatched on the object’s runtime class, not the variable’s declared type
    Annotate overriding methods with @Override so the compiler catches signature typos.
  18. 18
    If you override equals, what else must you override?
    hashCode: objects that are equal must return the same hash code, or HashMap and HashSet won’t find them
    Records generate both methods for you.
  19. 19
    What does record Point(int x, int y) {} give you?
    An immutable data class (Java 16+): private final fields, a constructor, accessors x() and y(), and equals, hashCode and toString
    Accessors are p.x(), not p.getX(). Records are implicitly final and can’t extend another class.
  20. 20
    Validate a record’s fields
    java
    record Range(int lo, int hi) {
        Range {
            if (lo > hi) throw new IllegalArgumentException("lo > hi");
        }
    }
    A compact constructor has no parameter list; the fields are assigned automatically after its body runs.
  21. 21
    What does sealed do?
    Limits which classes may extend or implement a type (Java 17+), so the compiler knows every subtype
    java
    sealed interface Shape permits Circle, Square {}
    record Circle(double r) implements Shape {}
    record Square(double side) implements Shape {}
  22. 22
    Check a type and cast it in one step
    java
    if (obj instanceof String s && !s.isBlank()) {
        System.out.println(s.length());
    }
    Pattern matching for instanceof, Java 16+.
  23. 23
    Switch over a sealed type without a default
    java
    double area = switch (shape) {
        case Circle c -> Math.PI * c.r() * c.r();
        case Square s -> s.side() * s.side();
    };
    Pattern matching for switch (Java 21+). Because Shape is sealed, the compiler can check that the cases are exhaustive.
  24. 24
    Destructure a record in a pattern
    java
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x + y);
    }
    Record patterns, Java 21+. They work in switch cases too.
  25. 25
    The core collection interfaces and their usual implementations
    List (ordered, allows duplicates): ArrayList. Set (no duplicates): HashSet. Map (keys to values): HashMap. Deque (queue or stack): ArrayDeque
  26. 26
    ArrayList vs LinkedList
    ArrayList has O(1) indexed access and fast appends; LinkedList has O(n) indexed access and poor memory locality, so ArrayList (or ArrayDeque for queues) is almost always the better choice
  27. 27
    HashMap vs LinkedHashMap vs TreeMap
    Iteration order: unspecified, insertion order, and sorted by key. The hash maps are O(1) on average; TreeMap is O(log n)
  28. 28
    What happens here?
    java
    List<String> xs = List.of("a", "b");
    xs.add("c");
    An UnsupportedOperationException: List.of (Java 9+) returns an unmodifiable list
    It also rejects null elements. Use new ArrayList<>(List.of("a", "b")) for a mutable copy.
  29. 29
    What goes wrong when you remove items from a list inside a for-each loop?
    It usually throws ConcurrentModificationException. Use list.removeIf(x -> …), or an explicit Iterator and its remove()
  30. 30
    Count words in a Map<String, Integer>
    java
    counts.merge(word, 1, Integer::sum);
    Stores 1 for a new key; otherwise adds 1 to the existing count.
  31. 31
    Add to a Map<String, List<String>>, creating each list on first use
    java
    byCity.computeIfAbsent(city, k -> new ArrayList<>()).add(name);
  32. 32
    Sort people by age, then by name
    java
    people.sort(Comparator.comparing(Person::age)
            .thenComparing(Person::name));
    Add .reversed() to flip an order.
  33. 33
    The Arrays.asList(array) gotcha
    The list is fixed-size and backed by the array: set writes through to the array, but add and remove throw UnsupportedOperationException
  34. 34
    Why doesn’t List<int> compile?
    Type arguments must be reference types. Use List<Integer>; autoboxing converts between int and Integer
    Unboxing a null Integer into an int throws NullPointerException.
  35. 35
    What is type erasure?
    Generic type arguments exist only at compile time; at runtime List<String> and List<Integer> are both plain List. So new T(), T.class and x instanceof List<String> aren’t allowed
  36. 36
    Is a List<Integer> a List<Number>?
    No. Generics are invariant; otherwise you could add a Double to a list of integers. Accept List<? extends Number> to take both
    Arrays are covariant instead, so Object[] a = new String[1]; a[0] = 1; compiles and throws ArrayStoreException.
  37. 37
    ? extends T vs ? super T
    “Producer extends, consumer super” (PECS): you can read Ts out of a List<? extends T> and add Ts to a List<? super T>
    java
    static void addOnes(List<? super Integer> xs) {
        xs.add(1); // works for List<Integer>, List<Number>, List<Object>
    }
  38. 38
    Checked vs unchecked exceptions
    Checked exceptions (subclasses of Exception other than RuntimeException) must be caught or declared with throws; unchecked ones (RuntimeException, Error and their subclasses) needn’t be
    IOException is checked; NullPointerException and IllegalArgumentException are unchecked.
  39. 39
    Read a file and make sure it gets closed
    java
    try (var reader = Files.newBufferedReader(path)) {
        return reader.readLine();
    }
    try-with-resources closes any AutoCloseable when the block exits, even when it throws.
  40. 40
    What does f() return?
    java
    static int f() {
        try { return 1; }
        finally { return 2; }
    }
    2: finally runs even after return, and a return inside it replaces the earlier one
    Never return from finally: it also silently discards exceptions.
  41. 41
    Handle two exception types in one catch
    java
    try {
        load();
    } catch (IOException | SQLException e) {
        log.error("load failed", e);
    }
  42. 42
    Get the sorted names of all adults with a stream
    java
    List<String> names = people.stream()
            .filter(p -> p.age() >= 18)
            .map(Person::name)
            .sorted()
            .toList();
    Stream.toList() (Java 16+) returns an unmodifiable list; before that, use collect(Collectors.toList()).
  43. 43
    When do stream operations actually run?
    Lazily: intermediate operations like filter and map do nothing until a terminal operation such as toList, forEach or count runs
    A stream can be consumed only once; reusing it throws IllegalStateException.
  44. 44
    How should you use the Optional returned by findFirst()?
    Unwrap it with orElse(default), orElseThrow(), map(…) or ifPresent(…) instead of calling get() blindly
    java
    String name = users.stream()
            .filter(User::isAdmin)
            .map(User::name)
            .findFirst()
            .orElse("nobody");
  45. 45
    Group people by city
    java
    Map<String, List<Person>> byCity = people.stream()
            .collect(Collectors.groupingBy(Person::city));
    groupingBy(Person::city, Collectors.counting()) counts per city instead.
  46. 46
    What is a functional interface?
    An interface with a single abstract method, which a lambda or method reference can implement: Function<T, R>, Predicate<T>, Supplier<T>, Consumer<T>, Runnable…
  47. 47
    Why doesn’t this compile?
    java
    int count = 0;
    list.forEach(x -> count++);
    Lambdas can only capture local variables that are effectively final, i.e. never reassigned
    Use list.size() or a stream’s count() instead.
  48. 48
    What happens between javac and running a Java program?
    javac compiles .java source into platform-independent bytecode (.class files); the JVM loads it, interprets it, and JIT-compiles hot code to native machine code
    Since Java 11, java Main.java compiles and runs a single source file in one step.
  49. 49
    Stack vs heap in the JVM
    Each thread’s stack holds method frames with local variables and references; objects live on the shared heap, where the garbage collector frees them once they’re unreachable
    Deep recursion throws StackOverflowError; running out of heap throws OutOfMemoryError.
  50. 50
    What are virtual threads?
    Lightweight threads scheduled by the JVM rather than the OS (Java 21+), so you can afford one thread per task for blocking I/O, even millions of them
    java
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        urls.forEach(url -> executor.submit(() -> fetch(url)));
    } // close() waits for the tasks

Frequently asked questions

Which Java version do the cards use?

Modern Java: everything works on the current LTS releases (21 and 25). Newer features are marked with the version that introduced them, such as records (16), sealed classes (17) and virtual threads (21).

Is this deck useful for Java interviews?

Yes. It covers the classic questions (equals and hashCode, checked vs unchecked exceptions, ArrayList vs LinkedList, pass-by-value) alongside modern Java.

Does it cover Spring or other frameworks?

No. It sticks to the language, the standard library and the JVM, which every Java framework builds on.

Intermediate

JavaScript

Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.

50 cards
Intermediate

Advanced JS

Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.

50 cards
Intermediate

TypeScript

Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.

50 cards
Beginner

Python

Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.

50 cards
Beginner

Git & terminal

The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.

50 cards
Beginner

C#

C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.

50 cards
Beginner

Kotlin

Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.

50 cards
Beginner

Swift

Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.

50 cards
Beginner

Flutter

Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.

50 cards
Beginner

PHP

Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.

50 cards
Beginner

Ruby

Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.

50 cards
Beginner

Go

Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.

50 cards
Intermediate

Rust

Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.

50 cards
Intermediate

C++

Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.

50 cards
Intermediate

Haskell

Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.

50 cards
Intermediate

Elixir

Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.

50 cards
Intermediate

x86-64 assembly

x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.

50 cards
Beginner

Regex

Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.

50 cards