NexusCS

Java

Programming
Modern Java cheatsheet covering Java 17+ features. Focuses on current best practices and modern syntax. Legacy patterns (pre-Java 8) are intentionally excluded.
featured

Getting started

Hello World

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Compile and run:

javac HelloWorld.java
java HelloWorld

Version note

This cheatsheet focuses on modern Java (Java 17+) with coverage of Java 21 features like Virtual Threads and Record Patterns.

Version Coverage
Java 21+ Virtual Threads, Sequenced Collections, Record Patterns
Java 17+ Sealed classes, pattern matching enhancements
Java 16+ Records, instanceof patterns
Java 11+ var in lambdas, new String/Files methods
Java 8+ Lambdas, Streams, Optional, CompletableFuture

Features are marked with version badges (e.g., Java 17+) where version-specific.

Variables

// Primitive types
int age = 25;
double price = 19.99;
boolean isActive = true;
char grade = 'A';
long bigNumber = 100000L;
float decimal = 3.14f;

// Reference types
String name = "John";
Integer boxedInt = 42;

Constants

final int MAX_SIZE = 100;
final String APP_NAME = "MyApp";

// Static constants
public static final double PI = 3.14159;

Type casting

// Implicit casting (widening)
int i = 100;
double d = i;        // int to double

// Explicit casting (narrowing)
double d = 9.99;
int i = (int) d;     // 9 (loses decimal)

// Object casting
Object obj = "Hello";
String str = (String) obj;

String operations

Common methods

String str = "Hello World";

str.length()              // 11
str.charAt(0)             // 'H'
str.substring(0, 5)       // "Hello"
str.toLowerCase()         // "hello world"
str.toUpperCase()         // "HELLO WORLD"
str.trim()                // Remove whitespace
str.replace("World", "Java")  // "Hello Java"
str.contains("World")     // true
str.startsWith("Hello")   // true
str.endsWith("World")     // true
str.split(" ")            // ["Hello", "World"]

String comparison

String a = "hello";
String b = "hello";
String c = new String("hello");

a == b                    // true (same reference)
a == c                    // false (different objects)
a.equals(c)               // true (same content)
a.equalsIgnoreCase("HELLO")  // true
a.compareTo(b)            // 0 (equal)

String formatting

// String.format()
String.format("Hello %s", "World")        // "Hello World"
String.format("Number: %d", 42)           // "Number: 42"
String.format("Price: %.2f", 19.99)       // "Price: 19.99"

// Text blocks (Java 15+)
String json = """
    {
        "name": "John",
        "age": 30
    }
    """;

StringBuilder

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
sb.insert(5, ",");         // "Hello, World"
sb.reverse();              // "dlroW ,olleH"
sb.delete(0, 6);           // "World"
String result = sb.toString();

Arrays

Array basics

// Declaration and initialization
int[] numbers = {1, 2, 3, 4, 5};
String[] names = new String[3];
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Charlie";

// Access
int first = numbers[0];
int length = numbers.length;  // ⚠️ No parentheses

Multidimensional arrays

// 2D array
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// Access
int element = matrix[1][2];   // 6

// Jagged array
int[][] jagged = new int[3][];
jagged[0] = new int[2];
jagged[1] = new int[3];
jagged[2] = new int[1];

Array operations

import java.util.Arrays;

int[] arr = {5, 2, 8, 1, 9};

// Sorting
Arrays.sort(arr);             // {1, 2, 5, 8, 9}

// Searching (on sorted array)
int index = Arrays.binarySearch(arr, 5);  // 2

// Copying
int[] copy = Arrays.copyOf(arr, arr.length);
int[] range = Arrays.copyOfRange(arr, 0, 3);

// Filling
Arrays.fill(arr, 0);          // All elements = 0

// Comparison
Arrays.equals(arr, copy);     // true

// Converting to string
Arrays.toString(arr);         // "[1, 2, 5, 8, 9]"

Control flow

Conditionals

// if-else
if (age >= 18) {
    System.out.println("Adult");
} else if (age >= 13) {
    System.out.println("Teen");
} else {
    System.out.println("Child");
}

// Ternary operator
String status = (age >= 18) ? "Adult" : "Minor";

// Switch (traditional)
switch (day) {
    case "Monday":
        System.out.println("Start of week");
        break;
    case "Friday":
        System.out.println("End of week");
        break;
    default:
        System.out.println("Midweek");
}

Switch expressions (Java 14+)

// Arrow syntax
String result = switch (day) {
    case "Monday", "Tuesday" -> "Early week";
    case "Wednesday" -> "Midweek";
    case "Thursday", "Friday" -> "Late week";
    default -> "Weekend";
};

// With yield
int numDays = switch (month) {
    case "February" -> {
        if (isLeapYear) {
            yield 29;
        } else {
            yield 28;
        }
    }
    case "April", "June", "September", "November" -> 30;
    default -> 31;
};

Loops

// for loop
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

// Enhanced for loop
String[] names = {"Alice", "Bob", "Charlie"};
for (String name : names) {
    System.out.println(name);
}

// while loop
int count = 0;
while (count < 5) {
    System.out.println(count);
    count++;
}

// do-while loop
do {
    System.out.println(count);
    count++;
} while (count < 5);

Loop control

// break - exit loop
for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    System.out.println(i);
}

// continue - skip iteration
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue;
    System.out.println(i);  // Odd numbers only
}

// Labeled break
outer: for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == 1 && j == 1) break outer;
        System.out.println(i + "," + j);
    }
}

Classes and objects

Basic class

public class Person {
    // Fields
    private String name;
    private int age;

    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters and setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // Method
    public void greet() {
        System.out.println("Hello, I'm " + name);
    }
}

// Usage
Person person = new Person("Alice", 30);
person.greet();

Constructor overloading

public class Rectangle {
    private int width;
    private int height;

    // Default constructor
    public Rectangle() {
        this(1, 1);  // Call another constructor
    }

    // Parameterized constructor
    public Rectangle(int size) {
        this(size, size);
    }

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
}

Static members

public class Counter {
    private static int count = 0;  // Shared by all instances
    private int id;

    public Counter() {
        count++;
        this.id = count;
    }

    public static int getCount() {
        return count;  // Static method
    }

    public int getId() {
        return id;
    }
}

// Usage
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter.getCount();  // 2 (call on class, not instance)

Inheritance

// Parent class
public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void makeSound() {
        System.out.println("Some sound");
    }
}

// Child class
public class Dog extends Animal {
    public Dog(String name) {
        super(name);  // Call parent constructor
    }

    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }

    public void fetch() {
        System.out.println(name + " is fetching");
    }
}

// Usage
Dog dog = new Dog("Buddy");
dog.makeSound();  // "Woof!"

Interfaces and abstract classes

Interfaces

// Interface definition
public interface Drawable {
    void draw();  // Abstract method (implicitly public)

    // Default method (Java 8+)
    default void display() {
        System.out.println("Displaying...");
    }

    // Static method (Java 8+)
    static void info() {
        System.out.println("Drawable interface");
    }
}

// Implementation
public class Circle implements Drawable {
    @Override
    public void draw() {
        System.out.println("Drawing circle");
    }
}

Multiple interfaces

interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

// Implement multiple interfaces
public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("Duck flying");
    }

    @Override
    public void swim() {
        System.out.println("Duck swimming");
    }
}

Abstract classes

public abstract class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    // Abstract method
    public abstract double getArea();

    // Concrete method
    public void displayColor() {
        System.out.println("Color: " + color);
    }
}

public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Interface vs Abstract class

// ⚠️ Interface: Only abstract methods and constants (pre-Java 8)
// ⚠️ Abstract class: Can have fields and concrete methods
// ⚠️ A class can implement multiple interfaces but extend only one class

// Choose interface when:
// - Defining capabilities (Flyable, Serializable)
// - Multiple inheritance needed

// Choose abstract class when:
// - Sharing code among related classes
// - Need non-static, non-final fields

Collections

ArrayList

import java.util.ArrayList;

ArrayList<String> list = new ArrayList<>();

// Adding
list.add("Apple");
list.add("Banana");
list.add(0, "Orange");   // Insert at index

// Accessing
String first = list.get(0);
int size = list.size();

// Removing
list.remove(0);          // By index
list.remove("Banana");   // By value
list.clear();            // Remove all

// Checking
boolean has = list.contains("Apple");
boolean empty = list.isEmpty();

// Iteration
for (String item : list) {
    System.out.println(item);
}

LinkedList

import java.util.LinkedList;

LinkedList<Integer> list = new LinkedList<>();

// Stack operations
list.push(1);            // Add to front
list.push(2);
int top = list.pop();    // Remove from front

// Queue operations
list.offer(1);           // Add to end
list.offer(2);
int first = list.poll(); // Remove from front

// Deque operations
list.addFirst(1);
list.addLast(2);
int head = list.getFirst();
int tail = list.getLast();

HashMap

import java.util.HashMap;

HashMap<String, Integer> map = new HashMap<>();

// Adding
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

// Accessing
int age = map.get("Alice");        // 25
int def = map.getOrDefault("Dave", 0);  // 0

// Checking
boolean has = map.containsKey("Alice");
boolean hasValue = map.containsValue(25);

// Removing
map.remove("Bob");

// Iteration
for (String key : map.keySet()) {
    System.out.println(key + ": " + map.get(key));
}

for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

HashSet

import java.util.HashSet;

HashSet<String> set = new HashSet<>();

// Adding
set.add("Apple");
set.add("Banana");
set.add("Apple");        // Ignored (no duplicates)

// Checking
boolean has = set.contains("Apple");

// Removing
set.remove("Banana");

// Iteration
for (String item : set) {
    System.out.println(item);
}

// Set operations
HashSet<Integer> a = new HashSet<>(Arrays.asList(1, 2, 3));
HashSet<Integer> b = new HashSet<>(Arrays.asList(3, 4, 5));

a.addAll(b);             // Union
a.retainAll(b);          // Intersection
a.removeAll(b);          // Difference

TreeMap (sorted)

import java.util.TreeMap;

TreeMap<String, Integer> map = new TreeMap<>();
map.put("Zebra", 3);
map.put("Apple", 1);
map.put("Mango", 2);

// Automatically sorted by key
// Keys: [Apple, Mango, Zebra]

String first = map.firstKey();    // "Apple"
String last = map.lastKey();      // "Zebra"

PriorityQueue

import java.util.PriorityQueue;

PriorityQueue<Integer> pq = new PriorityQueue<>();

pq.offer(5);
pq.offer(1);
pq.offer(3);

int min = pq.poll();     // 1 (smallest first)
int peek = pq.peek();    // 3 (next smallest)

Lambda expressions

Basic lambda

// Traditional anonymous class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda expression
Runnable r2 = () -> System.out.println("Hello");

// With parameters
Comparator<String> comp = (a, b) -> a.compareTo(b);

// Multi-line
Consumer<String> print = (s) -> {
    String result = s.toUpperCase();
    System.out.println(result);
};

Functional interfaces

// Predicate - returns boolean
Predicate<Integer> isEven = n -> n % 2 == 0;
isEven.test(4);          // true

// Function - transforms input
Function<String, Integer> len = s -> s.length();
len.apply("Hello");      // 5

// Consumer - accepts input, no return
Consumer<String> print = s -> System.out.println(s);
print.accept("Hello");

// Supplier - no input, returns value
Supplier<Double> random = () -> Math.random();
random.get();

// BiFunction - two inputs, one output
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
add.apply(5, 3);         // 8

Method references

// Static method reference
Function<String, Integer> parse = Integer::parseInt;
parse.apply("123");      // 123

// Instance method reference
String str = "Hello";
Supplier<Integer> getLen = str::length;
getLen.get();            // 5

// Constructor reference
Supplier<ArrayList<String>> create = ArrayList::new;
ArrayList<String> list = create.get();

// Array constructor reference
Function<Integer, int[]> createArray = int[]::new;
int[] arr = createArray.apply(10);

Common uses

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// forEach
names.forEach(name -> System.out.println(name));
names.forEach(System.out::println);  // Method reference

// Comparator
names.sort((a, b) -> a.compareTo(b));
names.sort(String::compareTo);

// Filter with removeIf
names.removeIf(name -> name.startsWith("A"));

// Map computeIfAbsent
Map<String, Integer> map = new HashMap<>();
map.computeIfAbsent("key", k -> k.length());

Streams API

Creating streams

import java.util.stream.*;

// From collection
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

// From array
String[] arr = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);

// Using Stream.of()
Stream<String> stream = Stream.of("a", "b", "c");

// Empty stream
Stream<String> empty = Stream.empty();

// Infinite stream
Stream<Integer> infinite = Stream.iterate(0, n -> n + 1);

// Random numbers
Stream<Double> randoms = Stream.generate(Math::random);

Intermediate operations

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

// filter - keep matching elements
numbers.stream()
    .filter(n -> n % 2 == 0)         // [2, 4, 6]

// map - transform elements
numbers.stream()
    .map(n -> n * 2)                 // [2, 4, 6, 8, 10, 12]

// flatMap - flatten nested streams
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4)
);
nested.stream()
    .flatMap(List::stream)           // [1, 2, 3, 4]

// distinct - remove duplicates
Stream.of(1, 2, 2, 3, 3, 3)
    .distinct()                      // [1, 2, 3]

// sorted - sort elements
numbers.stream()
    .sorted()                        // [1, 2, 3, 4, 5, 6]
    .sorted(Comparator.reverseOrder())  // [6, 5, 4, 3, 2, 1]

// limit - take first n
numbers.stream()
    .limit(3)                        // [1, 2, 3]

// skip - skip first n
numbers.stream()
    .skip(2)                         // [3, 4, 5, 6]

Terminal operations

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// forEach - iterate
numbers.stream()
    .forEach(System.out::println);

// collect - to collection
List<Integer> list = numbers.stream()
    .filter(n -> n > 2)
    .collect(Collectors.toList());

Set<Integer> set = numbers.stream()
    .collect(Collectors.toSet());

// toArray - to array
Integer[] arr = numbers.stream()
    .toArray(Integer[]::new);

// reduce - combine to single value
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);     // 15

Optional<Integer> max = numbers.stream()
    .reduce(Integer::max);           // Optional[5]

// count - count elements
long count = numbers.stream()
    .filter(n -> n > 2)
    .count();                        // 3

// anyMatch, allMatch, noneMatch
boolean hasEven = numbers.stream()
    .anyMatch(n -> n % 2 == 0);      // true

boolean allPositive = numbers.stream()
    .allMatch(n -> n > 0);           // true

// findFirst, findAny
Optional<Integer> first = numbers.stream()
    .filter(n -> n > 3)
    .findFirst();                    // Optional[4]

Collectors

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// Joining strings
String joined = names.stream()
    .collect(Collectors.joining(", "));  // "Alice, Bob, Charlie, David"

// Grouping by
Map<Integer, List<String>> byLength = names.stream()
    .collect(Collectors.groupingBy(String::length));
// {3=[Bob], 5=[Alice, David], 7=[Charlie]}

// Partitioning (boolean key)
Map<Boolean, List<String>> byFirstLetter = names.stream()
    .collect(Collectors.partitioningBy(s -> s.startsWith("A")));
// {false=[Bob, Charlie, David], true=[Alice]}

// Counting
Map<Integer, Long> counts = names.stream()
    .collect(Collectors.groupingBy(
        String::length,
        Collectors.counting()
    ));

// Summing
int totalLength = names.stream()
    .collect(Collectors.summingInt(String::length));

// Statistics
IntSummaryStatistics stats = numbers.stream()
    .collect(Collectors.summarizingInt(Integer::intValue));
stats.getAverage();
stats.getMax();

Stream examples

List<String> words = Arrays.asList("apple", "banana", "cherry", "date");

// Chain operations
List<String> result = words.stream()
    .filter(w -> w.length() > 5)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());
// [BANANA, CHERRY]

// Parallel stream (for performance)
long count = words.parallelStream()
    .filter(w -> w.startsWith("a"))
    .count();

// ⚠️ Order matters for performance
// Good: filter before map (fewer transformations)
words.stream()
    .filter(w -> w.length() > 5)
    .map(String::toUpperCase);

// Bad: map before filter (more transformations)
words.stream()
    .map(String::toUpperCase)
    .filter(w -> w.length() > 5);

Exception handling

Try-catch

try {
    int result = 10 / 0;  // ArithmeticException
} catch (ArithmeticException e) {
    System.out.println("Cannot divide by zero");
    e.printStackTrace();
} finally {
    System.out.println("Always executes");
}

// Multiple catch blocks
try {
    String str = null;
    System.out.println(str.length());
} catch (NullPointerException e) {
    System.out.println("Null pointer");
} catch (Exception e) {
    System.out.println("General error");
}

// Multi-catch (Java 7+)
try {
    // code
} catch (IOException | SQLException e) {
    System.out.println("IO or SQL error");
}

Try-with-resources

// Auto-closes resources
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    String line = br.readLine();
    System.out.println(line);
} catch (IOException e) {
    e.printStackTrace();
}
// BufferedReader automatically closed

// Multiple resources
try (FileInputStream fis = new FileInputStream("in.txt");
     FileOutputStream fos = new FileOutputStream("out.txt")) {
    // Use streams
}

Throwing exceptions

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException("Not enough funds");
    }
    balance -= amount;
}

// Runtime exception (unchecked, no throws needed)
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative");
    }
    this.age = age;
}

Custom exceptions

// Checked exception
public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

// Unchecked exception
public class InvalidAgeException extends RuntimeException {
    public InvalidAgeException(String message) {
        super(message);
    }
}

// Usage
try {
    withdraw(1000);
} catch (InsufficientFundsException e) {
    System.out.println(e.getMessage());
}

Generics

Generic class

public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
}

// Usage
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String str = stringBox.get();  // No casting needed

Box<Integer> intBox = new Box<>();
intBox.set(42);

Generic method

public class Utils {
    // Generic method
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    // Multiple type parameters
    public static <K, V> void printPair(K key, V value) {
        System.out.println(key + ": " + value);
    }
}

// Usage
Integer[] numbers = {1, 2, 3};
Utils.printArray(numbers);

Utils.printPair("Name", "Alice");

Bounded type parameters

// Upper bound (extends)
public class NumberBox<T extends Number> {
    private T number;

    public double getDoubleValue() {
        return number.doubleValue();  // Number method
    }
}

// Works with Number subclasses
NumberBox<Integer> intBox = new NumberBox<>();
NumberBox<Double> doubleBox = new NumberBox<>();
// NumberBox<String> fails - String is not a Number

// Multiple bounds
public class Container<T extends Number & Comparable<T>> {
    // T must be Number AND implement Comparable
}

Wildcards

// Upper bounded wildcard
public void processList(List<? extends Number> list) {
    for (Number n : list) {
        System.out.println(n.doubleValue());
    }
}
// Accepts List<Integer>, List<Double>, etc.

// Lower bounded wildcard
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}
// Accepts List<Integer>, List<Number>, List<Object>

// Unbounded wildcard
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}
// Accepts any List

// ⚠️ Cannot add to ? extends (read-only)
List<? extends Number> list = new ArrayList<Integer>();
// list.add(1);  // Compile error
Number n = list.get(0);  // OK

File I/O

Reading files

import java.nio.file.*;
import java.io.*;

// Read entire file (Java 11+)
String content = Files.readString(Path.of("file.txt"));

// Read all lines
List<String> lines = Files.readAllLines(Path.of("file.txt"));

// Read with BufferedReader
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

// Read with Scanner
try (Scanner scanner = new Scanner(new File("file.txt"))) {
    while (scanner.hasNextLine()) {
        String line = scanner.nextLine();
        System.out.println(line);
    }
}

Writing files

// Write string (Java 11+)
Files.writeString(Path.of("file.txt"), "Hello, World!");

// Write lines
List<String> lines = Arrays.asList("Line 1", "Line 2", "Line 3");
Files.write(Path.of("file.txt"), lines);

// Append to file
Files.writeString(
    Path.of("file.txt"),
    "New line",
    StandardOpenOption.APPEND
);

// Write with BufferedWriter
try (BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"))) {
    bw.write("Hello, World!");
    bw.newLine();
    bw.write("Second line");
}

File operations

import java.nio.file.*;

Path path = Path.of("file.txt");

// Check existence
boolean exists = Files.exists(path);

// Create file
Files.createFile(path);

// Create directory
Files.createDirectory(Path.of("mydir"));
Files.createDirectories(Path.of("path/to/dir"));  // Parent dirs too

// Delete
Files.delete(path);
Files.deleteIfExists(path);

// Copy
Files.copy(
    Path.of("source.txt"),
    Path.of("dest.txt"),
    StandardCopyOption.REPLACE_EXISTING
);

// Move/rename
Files.move(
    Path.of("old.txt"),
    Path.of("new.txt"),
    StandardCopyOption.REPLACE_EXISTING
);

// File attributes
long size = Files.size(path);
boolean isDir = Files.isDirectory(path);
boolean readable = Files.isReadable(path);

Directory listing

// List files in directory
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Path.of("."))) {
    for (Path file : stream) {
        System.out.println(file.getFileName());
    }
}

// List with filter
try (DirectoryStream<Path> stream =
        Files.newDirectoryStream(Path.of("."), "*.txt")) {
    for (Path file : stream) {
        System.out.println(file);
    }
}

// Walk directory tree
Files.walk(Path.of("."))
    .filter(Files::isRegularFile)
    .filter(p -> p.toString().endsWith(".java"))
    .forEach(System.out::println);

Modern Java features

Records (Java 16+)

// Compact data class
public record Person(String name, int age) {}

// Equivalent to:
// - private final fields
// - constructor
// - getters (name(), age())
// - equals(), hashCode(), toString()

// Usage
Person person = new Person("Alice", 30);
String name = person.name();  // ⚠️ Getter is name(), not getName()
int age = person.age();

// With validation
public record Person(String name, int age) {
    public Person {  // Compact constructor
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
}

// Custom methods
public record Point(int x, int y) {
    public double distanceFromOrigin() {
        return Math.sqrt(x * x + y * y);
    }
}

Sealed classes (Java 17+)

// Restrict which classes can extend
public sealed class Shape
    permits Circle, Rectangle, Triangle {
}

public final class Circle extends Shape {
    // final: cannot be extended
}

public non-sealed class Rectangle extends Shape {
    // non-sealed: can be extended by any class
}

public sealed class Triangle extends Shape
    permits RightTriangle {
    // sealed: only RightTriangle can extend
}

// Pattern matching with sealed classes
String describe(Shape shape) {
    return switch (shape) {
        case Circle c -> "Circle";
        case Rectangle r -> "Rectangle";
        case Triangle t -> "Triangle";
        // No default needed - compiler knows all cases
    };
}

Pattern matching (Java 16+)

// instanceof pattern (Java 16+)
if (obj instanceof String s) {
    System.out.println(s.toUpperCase());  // s in scope
}

// Pattern matching for switch (Java 21+)
String format(Object obj) {
    return switch (obj) {
        case Integer i -> String.format("int %d", i);
        case Long l -> String.format("long %d", l);
        case Double d -> String.format("double %f", d);
        case String s -> String.format("String %s", s);
        case null -> "null";
        default -> obj.toString();
    };
}

// Guard patterns
String process(Object obj) {
    return switch (obj) {
        case String s when s.length() > 5 -> "Long string";
        case String s -> "Short string";
        default -> "Not a string";
    };
}

Text blocks (Java 15+)

// Multi-line strings
String json = """
    {
        "name": "John",
        "age": 30,
        "city": "New York"
    }
    """;

String html = """
    <html>
        <body>
            <h1>Hello, World!</h1>
        </body>
    </html>
    """;

// With formatting
String query = """
    SELECT id, name, age
    FROM users
    WHERE age > %d
    AND city = '%s'
    """.formatted(18, "New York");

Local variable type inference (Java 10+)

// var keyword
var name = "Alice";              // String
var age = 30;                    // int
var list = new ArrayList<String>();  // ArrayList<String>

// In loops
for (var item : list) {
    System.out.println(item);
}

for (var entry : map.entrySet()) {
    var key = entry.getKey();
    var value = entry.getValue();
}

// ⚠️ Cannot use var without initializer
// var x;  // Compile error

// ⚠️ Cannot use var with null
// var x = null;  // Compile error

Optional API (Java 8+)

import java.util.Optional;

// Creating Optionals
Optional<String> opt1 = Optional.of("Hello");        // Non-null value
Optional<String> opt2 = Optional.ofNullable(null);   // May be null
Optional<String> opt3 = Optional.empty();            // Empty Optional

// Transforming values
Optional<String> name = Optional.of("Alice");
Optional<Integer> length = name.map(String::length);     // Optional[5]

Optional<String> upper = name
    .map(String::toUpperCase)
    .map(s -> s + "!");                              // Optional[ALICE!]

// flatMap - for nested Optionals
Optional<String> result = name.flatMap(n -> findAddress(n));

// Filtering
Optional<String> longName = name
    .filter(n -> n.length() > 5);                    // Optional.empty

// Retrieving values
String value = opt1.orElse("default");               // "Hello"
String value2 = opt3.orElse("default");              // "default"
String value3 = opt3.orElseGet(() -> "computed");    // Lazy evaluation
String value4 = opt1.orElseThrow();                  // "Hello" or throw
String value5 = opt3.orElseThrow(
    () -> new IllegalStateException("No value"));

// Conditional actions
opt1.ifPresent(s -> System.out.println(s));          // Prints "Hello"
opt1.ifPresentOrElse(
    s -> System.out.println("Value: " + s),
    () -> System.out.println("Empty")
);

// Converting to Stream (Java 9+)
Stream<String> stream = opt1.stream();               // Stream with 1 element

CompletableFuture (Java 8+)

import java.util.concurrent.CompletableFuture;

// Creating CompletableFutures
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return "Hello";  // Runs in ForkJoinPool
});

CompletableFuture<Void> voidFuture = CompletableFuture.runAsync(() -> {
    System.out.println("Task completed");
});

// Custom executor
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture<String> cf = CompletableFuture.supplyAsync(
    () -> "Hello",
    executor
);

// Chaining - thenApply (transform)
CompletableFuture<Integer> length = future
    .thenApply(String::length);

// Chaining - thenAccept (consume)
future.thenAccept(s -> System.out.println(s));

// Chaining - thenCompose (for nested CompletableFutures)
CompletableFuture<String> result = future
    .thenCompose(s -> fetchUser(s));

// Combining two futures
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "World");

CompletableFuture<String> combined = f1.thenCombine(
    f2,
    (s1, s2) -> s1 + " " + s2
);  // "Hello World"

// Wait for all
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2);
all.join();  // Blocks until all complete

// Wait for any
CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2);
Object firstResult = any.join();

// Error handling - exceptionally
CompletableFuture<String> safe = future
    .exceptionally(ex -> {
        System.err.println("Error: " + ex.getMessage());
        return "default";
    });

// Error handling - handle (for both success and error)
CompletableFuture<String> handled = future
    .handle((result, ex) -> {
        if (ex != null) {
            return "Error: " + ex.getMessage();
        }
        return result.toUpperCase();
    });

// Get result (blocking)
String value = future.get();              // May throw checked exceptions
String value2 = future.join();            // Unchecked exceptions
String value3 = future.getNow("default"); // Non-blocking with default

Virtual Threads (Java 21+)

// Creating virtual threads
Thread vThread = Thread.ofVirtual().start(() -> {
    System.out.println("Virtual thread running");
});

// Start and get thread
Thread vt = Thread.startVirtualThread(() -> {
    System.out.println("Hello from virtual thread");
});

// With name
Thread named = Thread.ofVirtual()
    .name("worker-", 0)
    .start(() -> {
        System.out.println(Thread.currentThread().getName());
    });

// Virtual thread executor
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

executor.submit(() -> {
    System.out.println("Task 1");
});

executor.submit(() -> {
    System.out.println("Task 2");
});

executor.shutdown();

// Structured concurrency with virtual threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    var future1 = executor.submit(() -> fetchUser(1));
    var future2 = executor.submit(() -> fetchUser(2));
    var future3 = executor.submit(() -> fetchUser(3));

    // All tasks complete when try-with-resources closes
    var user1 = future1.get();
    var user2 = future2.get();
    var user3 = future3.get();
}

// ⚠️ Virtual threads are lightweight (millions possible)
// ⚠️ Platform threads are heavy (~thousands max)
// ⚠️ Virtual threads are managed by JVM, not OS

Sequenced Collections (Java 21+)

import java.util.*;

// SequencedCollection - new interface
// Implemented by List, Deque, LinkedHashSet

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));

// Add at ends
list.addFirst("start");   // ["start", "a", "b", "c"]
list.addLast("end");      // ["start", "a", "b", "c", "end"]

// Get at ends
String first = list.getFirst();   // "start"
String last = list.getLast();     // "end"

// Remove at ends
list.removeFirst();       // ["a", "b", "c", "end"]
list.removeLast();        // ["a", "b", "c"]

// Reversed view (not a copy!)
List<String> reversed = list.reversed();
// reversed: ["c", "b", "a"]
// Changes to reversed affect original

reversed.addFirst("x");   // Adds to end of original!
// list: ["a", "b", "c", "x"]
// reversed: ["x", "c", "b", "a"]

// SequencedSet - implemented by LinkedHashSet
SequencedSet<String> set = new LinkedHashSet<>();
set.add("apple");
set.add("banana");
set.add("cherry");

set.addFirst("aardvark");         // ["aardvark", "apple", "banana", "cherry"]
String first = set.getFirst();    // "aardvark"
set.reversed().forEach(System.out::println);  // Reverse iteration

// SequencedMap - implemented by LinkedHashMap
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);

// Put at ends
map.putFirst("start", 0);         // {start=0, a=1, b=2, c=3}
map.putLast("end", 4);            // {start=0, a=1, b=2, c=3, end=4}

// Get entries at ends
Map.Entry<String, Integer> firstEntry = map.firstEntry();  // start=0
Map.Entry<String, Integer> lastEntry = map.lastEntry();    // end=4

// Reversed view
SequencedMap<String, Integer> reversedMap = map.reversed();
reversedMap.forEach((k, v) -> System.out.println(k + "=" + v));
// Prints: end=4, c=3, b=2, a=1, start=0

Record Patterns (Java 21+)

// Deconstructing records in instanceof
record Point(int x, int y) {}

Object obj = new Point(1, 2);

// Pattern matching with record deconstruction
if (obj instanceof Point(int x, int y)) {
    System.out.println("x=" + x + ", y=" + y);  // x=1, y=2
}

// Nested records
record Rectangle(Point topLeft, Point bottomRight) {}

Object rect = new Rectangle(new Point(0, 0), new Point(10, 10));

// Nested pattern matching
if (rect instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
    int width = x2 - x1;
    int height = y2 - y1;
    System.out.println("Width: " + width + ", Height: " + height);
}

// Record patterns in switch
String describe(Object obj) {
    return switch (obj) {
        case Point(int x, int y) when x == 0 && y == 0 ->
            "Origin";
        case Point(int x, int y) when x == y ->
            "On diagonal";
        case Point(int x, int y) ->
            "Point at (" + x + ", " + y + ")";
        case null -> "null";
        default -> "Unknown";
    };
}

// More complex example
record User(String name, int age) {}
record Admin(String name, int age, String role) {}

String getInfo(Object obj) {
    return switch (obj) {
        case User(String name, int age) when age < 18 ->
            name + " is a minor";
        case User(String name, int age) ->
            name + " is " + age + " years old";
        case Admin(String name, int age, String role) ->
            name + " is admin with role: " + role;
        default -> "Unknown";
    };
}

// Generic record patterns
record Box<T>(T value) {}

void process(Object obj) {
    if (obj instanceof Box(String s)) {
        System.out.println("String in box: " + s);
    } else if (obj instanceof Box(Integer i)) {
        System.out.println("Integer in box: " + i);
    }
}

Common patterns

Singleton pattern

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // Private constructor
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

// Thread-safe eager initialization
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Builder pattern

public class User {
    private final String name;        // Required
    private final String email;       // Required
    private final int age;            // Optional
    private final String phone;       // Optional

    private User(Builder builder) {
        this.name = builder.name;
        this.email = builder.email;
        this.age = builder.age;
        this.phone = builder.phone;
    }

    public static class Builder {
        private final String name;
        private final String email;
        private int age = 0;
        private String phone = "";

        public Builder(String name, String email) {
            this.name = name;
            this.email = email;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

// Usage
User user = new User.Builder("Alice", "alice@example.com")
    .age(30)
    .phone("555-1234")
    .build();

Factory pattern

// Factory method
public abstract class Animal {
    public abstract void makeSound();

    public static Animal createAnimal(String type) {
        return switch (type) {
            case "dog" -> new Dog();
            case "cat" -> new Cat();
            default -> throw new IllegalArgumentException("Unknown type");
        };
    }
}

// Usage
Animal animal = Animal.createAnimal("dog");
animal.makeSound();

Observer pattern

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

// Usage
Subject subject = new Subject();
subject.attach(msg -> System.out.println("Observer 1: " + msg));
subject.attach(msg -> System.out.println("Observer 2: " + msg));
subject.notifyObservers("Hello!");

Concurrency basics

Creating threads

// Extending Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}

MyThread thread = new MyThread();
thread.start();  // ⚠️ Call start(), not run()

// Implementing Runnable
Runnable task = () -> {
    System.out.println("Task running");
};

Thread thread = new Thread(task);
thread.start();

// With lambda
new Thread(() -> {
    System.out.println("Lambda thread");
}).start();

Thread methods

Thread thread = new Thread(() -> {
    // Task
});

thread.start();              // Start execution
thread.join();               // Wait for completion
thread.interrupt();          // Request interruption
boolean alive = thread.isAlive();

// Static methods
Thread.sleep(1000);          // Sleep 1 second
Thread.currentThread();      // Get current thread
Thread.yield();              // Hint to scheduler

Synchronized

public class Counter {
    private int count = 0;

    // Synchronized method
    public synchronized void increment() {
        count++;
    }

    // Synchronized block
    public void decrement() {
        synchronized (this) {
            count--;
        }
    }

    public synchronized int getCount() {
        return count;
    }
}

ExecutorService

import java.util.concurrent.*;

// Thread pool
ExecutorService executor = Executors.newFixedThreadPool(5);

// Submit tasks
executor.submit(() -> {
    System.out.println("Task 1");
});

executor.submit(() -> {
    System.out.println("Task 2");
});

// Shutdown
executor.shutdown();

// Wait for tasks to complete
executor.awaitTermination(1, TimeUnit.MINUTES);

// Single thread executor
ExecutorService single = Executors.newSingleThreadExecutor();

// Cached thread pool (creates threads as needed)
ExecutorService cached = Executors.newCachedThreadPool();

// Scheduled executor
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(3);
scheduled.schedule(() -> {
    System.out.println("Delayed task");
}, 5, TimeUnit.SECONDS);

Gotchas

String comparison

String a = "hello";
String b = "hello";
String c = new String("hello");

// ⚠️ Don't use == for strings
a == b       // true (string pool)
a == c       // false (different objects)

// ✅ Use equals()
a.equals(c)  // true (same content)

Integer caching

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;

// ⚠️ Integers -128 to 127 are cached
a == b       // true (cached)
c == d       // false (not cached)

// ✅ Use equals()
c.equals(d)  // true

Array length

int[] arr = {1, 2, 3};
String str = "hello";

// ⚠️ Array length is property, String length is method
arr.length      // 3 (no parentheses)
str.length()    // 5 (with parentheses)

NullPointerException

String str = null;

// ⚠️ Common NPE sources
str.length()              // NPE
str.equals("hello")       // NPE

// ✅ Safe alternatives
"hello".equals(str)       // false (no NPE)
Objects.equals(str, "hello")  // false (no NPE)
Optional.ofNullable(str)
    .map(String::length)
    .orElse(0);           // 0 (no NPE)

Array initialization

// ⚠️ Don't confuse declaration and initialization
int[] arr;               // Declared but not initialized
// arr[0] = 1;           // NullPointerException

// ✅ Initialize before use
int[] arr = new int[5];  // Initialized with zeros
arr[0] = 1;              // OK

Floating-point comparison

double a = 0.1 + 0.2;    // 0.30000000000000004
double b = 0.3;

// ⚠️ Don't use == for floating-point
a == b                   // false

// ✅ Use epsilon comparison
double epsilon = 0.0001;
Math.abs(a - b) < epsilon  // true

Stream reuse

Stream<String> stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println);

// ⚠️ Cannot reuse stream
// stream.forEach(System.out::println);  // IllegalStateException

// ✅ Create new stream or use collection
List<String> list = Arrays.asList("a", "b", "c");
list.stream().forEach(System.out::println);
list.stream().forEach(System.out::println);  // OK

Checked vs unchecked exceptions

// ⚠️ Checked exceptions must be caught or declared
public void readFile() throws IOException {
    Files.readString(Path.of("file.txt"));  // IOException
}

// ⚠️ Unchecked exceptions don't require declaration
public void divide(int a, int b) {
    return a / b;  // May throw ArithmeticException (unchecked)
}

Also see

Java Cheatsheet - NexusCS