Java 21 Examples

Virtual Threads

Virtual threads enable high-throughput concurrent applications with lightweight threads.

<code>
// Creating and starting a virtual thread
Thread virtualThread = Thread.ofVirtual().start(() -> {
    System.out.println("Hello from virtual thread!");
});
virtualThread.join();

// Creating virtual threads with custom names
Thread.Builder builder = Thread.ofVirtual().name("MyVirtualThread");
Runnable task = () -> System.out.println("Running in: " + Thread.currentThread().getName());
Thread thread = builder.start(task);
thread.join();

// Using ExecutorService with virtual threads
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1000; i++) {
        int taskId = i;
        executor.submit(() -> {
            System.out.println("Task " + taskId + " running on virtual thread");
        });
    }
} // Executor automatically closes and waits for tasks
</code>

Record Patterns

Record patterns simplify extracting data from records without explicit accessor method calls

<code>
// Define records
public record Position(int x, int y) {}
public record Path(Position from, Position to) {}

// Using record patterns with instanceof
public void printPosition(Object obj) {
    if (obj instanceof Position(int x, int y)) {
        System.out.printf("Position: x=%d, y=%d%n", x, y);
    }
}

// Nested record patterns
public void printPath(Object obj) {
    if (obj instanceof Path(Position(int x1, int y1), Position(int x2, int y2))) {
        System.out.printf("Path from (%d,%d) to (%d,%d)%n", x1, y1, x2, y2);
    }
}

// With different record types
public sealed interface Position permits Position2D, Position3D {}
public record Position2D(int x, int y) implements Position {}
public record Position3D(int x, int y, int z) implements Position {}

public void print(Object obj) {
    switch (obj) {
        case Path(Position2D from, Position2D to) ->
            System.out.printf("2D path: %d/%d -> %d/%d%n", 
                from.x(), from.y(), to.x(), to.y());
        case Path(Position3D from, Position3D to) ->
            System.out.printf("3D path: %d/%d/%d -> %d/%d/%d%n",
                from.x(), from.y(), from.z(), to.x(), to.y(), to.z());
        default -> System.out.println("Unknown type");
    }
}
</code>

Pattern Matching for switch

Pattern matching for switch enables type checking and extraction in switch statements.

<code>
// Pattern matching with guards
public void processObject(Object obj) {
    switch (obj) {
        case String s when s.length() > 5 -> 
            System.out.println(s.toUpperCase());
        case String s -> 
            System.out.println(s.toLowerCase());
        case Integer i -> 
            System.out.println(i * i);
        case Position(int x, int y) -> 
            System.out.println(x + "/" + y);
        default -> System.out.println("Unknown type");
    }
}

// Exhaustive switch with sealed types
public sealed interface Shape permits Rectangle, Circle {}
public record Rectangle(Position topLeft, Position bottomRight) implements Shape {}
public record Circle(Position center, int radius) implements Shape {}

public void debugShape(Shape shape) {
    switch (shape) {
        case Rectangle r -> 
            System.out.printf("Rectangle: %s to %s%n", r.topLeft(), r.bottomRight());
        case Circle c -> 
            System.out.printf("Circle: center=%s, radius=%d%n", c.center(), c.radius());
        // No default needed - compiler checks exhaustiveness
    }
}

// Qualified enum constants
public sealed interface Direction permits CompassDirection, VerticalDirection {}
public enum CompassDirection implements Direction { NORTH, SOUTH, EAST, WEST }
public enum VerticalDirection implements Direction { UP, DOWN }

void fly(Direction direction) {
    switch (direction) {
        case CompassDirection.NORTH -> System.out.println("Flying north");
        case CompassDirection.SOUTH -> System.out.println("Flying south");
        case CompassDirection.EAST -> System.out.println("Flying east");
        case CompassDirection.WEST -> System.out.println("Flying west");
        case VerticalDirection.UP -> System.out.println("Gaining altitude");
        case VerticalDirection.DOWN -> System.out.println("Losing altitude");
    }
}
</code>

Sequenced Collections

Sequenced collections provide direct access to first and last elements.

<code>
// Working with List
List<String> list = new ArrayList<>(List.of("A", "B", "C", "D"));
String first = list.getFirst();  // "A"
String last = list.getLast();    // "D"
list.addFirst("Z");
list.addLast("E");
String removed = list.removeFirst();

// Working with LinkedHashSet
LinkedHashSet<String> set = new LinkedHashSet<>(List.of("One", "Two", "Three"));
String firstElement = set.getFirst();  // "One"
String lastElement = set.getLast();    // "Three"

// Reversed view
List<String> events = List.of("Event1", "Event2", "Event3");
for (String event : events.reversed()) {
    System.out.println(event);  // Prints: Event3, Event2, Event1
}

// Working with Deque
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("Java 17");
deque.addLast("Java 21");
String front = deque.getFirst();  // "Java 17"
String back = deque.getLast();    // "Java 21"

// SequencedMap
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("first", 1);
map.put("second", 2);
map.put("third", 3);

Map.Entry<String, Integer> firstEntry = map.firstEntry();  // "first"=1
Map.Entry<String, Integer> lastEntry = map.lastEntry();    // "third"=3
map.putFirst("zero", 0);
map.putLast("fourth", 4);
</code>

String Templates (Preview)

String templates allow embedding expressions within strings for cleaner code.

<code>
// Basic string template
int a = 5;
int b = 10;
String result = STR."{a} times {b} = {Math.multiplyExact(a, b)}";
// Result: "5 times 10 = 50"

// Multi-line string template
String orderId = "12345";
String orderDate = "2025-11-02";
String product = "pens";
int quantity = 100;

String message = STR."""
    Order processed successfully:
    Order ID: {orderId}, placed on {orderDate}
    {product}, {quantity} ({product.contains("pens") ? "dozens" : "units"})
    Total Price: {calculatePrice(product, quantity)}
    """;

// Method that returns values
double calculatePrice(String product, int quantity) {
    return quantity * 2.5;
}

// Complex expressions in templates
String name = "John";
int age = 30;
String info = STR."Name: {name.toUpperCase()}, Age: {age}, Adult: {age >= 18}";
</code>

Unnamed Patterns and Variables (Preview)

Use underscore _ to ignore unused variables.

<code>
// Unused exception variable
try {
    int number = Integer.parseInt(string);
} catch (NumberFormatException _) {
    System.err.println("Not a number");
}

// Unused lambda parameter
Map<String, List<String>> map = new HashMap<>();
map.computeIfAbsent(key, _ -> new ArrayList<>()).add(value);

// Unused pattern variable
if (object instanceof Path(Position(int x1, int y1), _)) {
    System.out.printf("Path starting at x=%d, y=%d%n", x1, y1);
}

// Multiple unused variables
try {
    // Some code
} catch (IOException | SQLException _) {
    System.err.println("Error occurred");
}
</code>

New String Methods

Enhanced string manipulation methods.

<code>
// indexOf with range
String text = "Hello World Hello";
int index = text.indexOf("Hello", 5, 15);  // Search between indices 5 and 15

// splitWithDelimiters - includes delimiters in result
String sentence = "the red brown fox jumps over the lazy dog";
String[] parts = sentence.splitWithDelimiters(" ", 5);
// Result: ["the", " ", "red", " ", "brown", " ", "fox", " ", "jumps over the lazy dog"]

System.out.println(Arrays.toString(parts));
</code>

StringBuilder/StringBuffer Enhancements

Repeat operations for efficient string building.

<code>
// Repeat CharSequence
StringBuilder sb1 = new StringBuilder();
sb1.repeat("Hello ", 3);  // "Hello Hello Hello "
System.out.println(sb1);

// Repeat character/code point
StringBuilder sb2 = new StringBuilder();
sb2.repeat('*', 10);  // "**********"
sb2.repeat(0x1f600, 5);  // Five emoji faces
sb2.repeat('!', 3);  // "!!!"
System.out.println(sb2);

// Practical example
StringBuilder divider = new StringBuilder();
divider.repeat("-=", 20);  // "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
</code>

Character Emoji Methods

Check emoji properties.

<code>
// Check if character is an emoji
boolean isEmoji = Character.isEmoji('😀');  // true
boolean isNotEmoji = Character.isEmoji('A');  // false

// Check if emoji can have modifier (like skin tone)
boolean canModify = Character.isEmojiModifierBase('✋');  // true

// Check emoji presentation
int codePoint = "😀".codePointAt(0);
boolean hasEmojiPresentation = Character.isEmojiPresentation(codePoint);

// Check if it's an emoji component
boolean isComponent = Character.isEmojiComponent(0x1F3FB);  // Skin tone modifier
</code>

Math.clamp() Method

Clamp values within a range.

<code>
// Clamp integer values
int value = 150;
int clamped = Math.clamp(value, 0, 100);  // Result: 100

// Clamp to ensure value is in range
int score = -10;
int validScore = Math.clamp(score, 0, 100);  // Result: 0

// Clamp double values
double temperature = 105.5;
double safeTempC = Math.clamp(temperature, -50.0, 50.0);  // Result: 50.0

// Practical usage
int brightness = getUserInput();
int finalBrightness = Math.clamp(brightness, 0, 255);
</code>

HttpClient Lifecycle Management

Enhanced lifecycle control for HttpClient.

<code>
// Auto-closeable HttpClient
try (HttpClient client = HttpClient.newHttpClient()) {
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/data"))
        .build();
    
    HttpResponse<String> response = client.send(request, 
        HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
} // Automatically closes when done

// Manual shutdown
HttpClient client = HttpClient.newHttpClient();
// ... use the client
client.shutdown();  // Initiates graceful shutdown

// Wait for termination with timeout
if (client.awaitTermination(Duration.ofMinutes(1))) {
    System.out.println("Client terminated successfully");
} else {
    System.out.println("Client termination took longer than expected");
}

// Immediate shutdown
client.shutdownNow();  // Forces immediate shutdown

// Check termination status
if (client.isTerminated()) {
    System.out.println("Client has terminated");
}
</code>

Unnamed Classes and Instance Main Methods (Preview)

Simplified program entry point.

<code>
// Simple main method - complete Java program
void main() {
    System.out.println("Hello World!");
}

// With fields and helper methods
final String GREETING_TEMPLATE = "Hello %s!";

void main() {
    System.out.println(greet("World"));
}

String greet(String name) {
    return GREETING_TEMPLATE.formatted(name);
}

// Traditional main still works
public static void main(String[] args) {
    System.out.println("Traditional main method");
}
</code>

Previous Post Next Post

Blog ads

ads