BLOG POSTS
Java 14 Features You Should Know

Java 14 Features You Should Know

Java 14, released in March 2020, brought several significant enhancements to the language that developers and system administrators should definitely know about. While it wasn’t an LTS release, it introduced features that shaped future Java development and provided practical solutions for common programming challenges. This post covers the key features that can improve your code quality, development productivity, and application performance on your production servers.

Switch Expressions (Standard Feature)

Switch expressions, which were previewed in Java 12 and 13, became a standard feature in Java 14. Unlike traditional switch statements, switch expressions return values and support the new arrow syntax, making your code more concise and less error-prone.

// Traditional switch statement
String dayType;
switch (day) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        dayType = "Weekday";
        break;
    case SATURDAY:
    case SUNDAY:
        dayType = "Weekend";
        break;
    default:
        throw new IllegalArgumentException("Invalid day: " + day);
}

// Java 14 switch expression
String dayType = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
    case SATURDAY, SUNDAY -> "Weekend";
};

The new syntax eliminates the need for break statements and allows multiple case labels in a single branch. You can also use yield for more complex expressions:

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
    default -> {
        System.out.println("Calculating letters for: " + day);
        yield day.toString().length();
    }
};

Pattern Matching for instanceof (Preview)

Pattern matching for instanceof was introduced as a preview feature in Java 14, addressing the common pattern of type checking followed by casting. This feature reduces boilerplate code and eliminates potential ClassCastException errors.

// Before Java 14
if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.toUpperCase());
}

// Java 14 pattern matching
if (obj instanceof String str) {
    System.out.println(str.toUpperCase());
}

The pattern variable is only in scope when the instanceof check succeeds. This works particularly well in complex conditional logic:

public static String formatValue(Object obj) {
    if (obj instanceof Integer i && i > 100) {
        return "Large number: " + i;
    } else if (obj instanceof String s && s.length() > 10) {
        return "Long string: " + s.substring(0, 10) + "...";
    } else if (obj instanceof Double d) {
        return String.format("%.2f", d);
    }
    return obj.toString();
}

Records (Preview Feature)

Records provide a compact syntax for creating immutable data carrier classes. They automatically generate constructors, accessors, equals(), hashCode(), and toString() methods, significantly reducing boilerplate code.

// Traditional data class
public class PersonOld {
    private final String name;
    private final int age;
    
    public PersonOld(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
    
    @Override
    public boolean equals(Object obj) {
        // ... boilerplate code
    }
    
    @Override
    public int hashCode() {
        // ... boilerplate code
    }
    
    @Override
    public String toString() {
        // ... boilerplate code
    }
}

// Java 14 record
public record Person(String name, int age) {}

Records are particularly useful for DTOs, configuration objects, and data transfer between layers:

public record ServerConfig(String host, int port, boolean sslEnabled) {
    // Custom constructor with validation
    public ServerConfig {
        if (port < 1 || port > 65535) {
            throw new IllegalArgumentException("Invalid port: " + port);
        }
    }
    
    // Additional methods
    public String getConnectionString() {
        return (sslEnabled ? "https://" : "http://") + host + ":" + port;
    }
}

// Usage
ServerConfig config = new ServerConfig("localhost", 8080, true);
System.out.println(config.getConnectionString()); // https://localhost:8080

Text Blocks (Standard Feature)

Text blocks, previewed in Java 13, became standard in Java 14. They provide a cleaner way to write multi-line strings, especially useful for SQL queries, JSON, HTML, or configuration files.

// Traditional string concatenation
String html = "\n" +
              "    \n" +
              "        

Hello World

\n" + " \n" + ""; // Java 14 text block String html = """

Hello World

"""; // SQL queries become much more readable String query = """ SELECT u.name, u.email, p.title FROM users u JOIN posts p ON u.id = p.user_id WHERE u.active = true AND p.published_date > ? ORDER BY p.published_date DESC """;

Helpful NullPointerExceptions

Java 14 improved NullPointerException messages by pinpointing exactly which variable was null in complex expressions. This feature significantly reduces debugging time in production environments.

// Consider this code
String result = person.getAddress().getStreet().toUpperCase();

// Before Java 14
Exception in thread "main" java.lang.NullPointerException
    at Main.main(Main.java:10)

// Java 14 and later
Exception in thread "main" java.lang.NullPointerException: 
    Cannot invoke "Address.getStreet()" because the return value of 
    "Person.getAddress()" is null
    at Main.main(Main.java:10)

To enable this feature on VPS or dedicated servers, use the -XX:+ShowCodeDetailsInExceptionMessages JVM flag:

java -XX:+ShowCodeDetailsInExceptionMessages MyApplication

JFR Event Streaming

Java Flight Recorder (JFR) Event Streaming allows real-time monitoring of JVM events without writing data to disk first. This is particularly valuable for production monitoring and observability.

import jdk.jfr.consumer.RecordingStream;

// Monitor garbage collection events in real-time
try (var stream = new RecordingStream()) {
    stream.enable("jdk.GarbageCollection")
          .withThreshold(Duration.ofMillis(10));
    
    stream.onEvent("jdk.GarbageCollection", event -> {
        System.out.println("GC Event: " + event.getString("name") + 
                          ", Duration: " + event.getDuration("duration"));
    });
    
    stream.start(); // Non-blocking
}

Foreign Memory Access API (Incubator)

The Foreign Memory Access API provides a safe and efficient way to access memory outside the Java heap. This is useful for interacting with native libraries or managing large datasets.

import jdk.incubator.foreign.*;

// Allocate native memory
try (MemorySegment segment = MemorySegment.allocateNative(1024)) {
    MemoryAddress address = segment.baseAddress();
    
    // Write data
    MemoryAccess.setIntAtOffset(segment, 0, 42);
    MemoryAccess.setIntAtOffset(segment, 4, 84);
    
    // Read data
    int value1 = MemoryAccess.getIntAtOffset(segment, 0); // 42
    int value2 = MemoryAccess.getIntAtOffset(segment, 4); // 84
}

Performance and Best Practices

Here’s a comparison of Java 14 features and their performance impact:

Feature Performance Impact Best Use Case Production Ready
Switch Expressions Slight improvement Complex conditional logic Yes
Pattern Matching instanceof Minor improvement Type checking and casting Preview (use with –enable-preview)
Records Reduced memory footprint DTOs and immutable data Preview (use with –enable-preview)
Text Blocks No impact Multi-line strings Yes
JFR Event Streaming Low overhead monitoring Production observability Yes

Common Issues and Troubleshooting

When upgrading to Java 14, you might encounter these issues:

  • Preview Features: Records and pattern matching require –enable-preview flag and –source 14
  • IDE Support: Ensure your IDE supports Java 14 preview features
  • Build Tools: Update Maven/Gradle configurations for preview features
  • Dependency Conflicts: Some libraries may not be compatible with Java 14
# Maven configuration for preview features

    org.apache.maven.plugins
    maven-compiler-plugin
    3.8.1
    
        14
        
            --enable-preview
        
    


# Gradle configuration
tasks.withType(JavaCompile) {
    options.compilerArgs += ["--enable-preview"]
}

tasks.withType(Test) {
    jvmArgs += ["--enable-preview"]
}

Real-World Implementation Example

Here’s a practical example combining multiple Java 14 features in a REST API service:

public record ApiResponse(int status, String message, Object data) {}

public record UserRequest(String name, String email, int age) {
    public UserRequest {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Invalid age: " + age);
        }
    }
}

public class UserService {
    
    public ApiResponse processUser(Object request) {
        return switch (validateRequest(request)) {
            case VALID -> {
                if (request instanceof UserRequest user) {
                    // Process valid user
                    yield new ApiResponse(200, "User created", user);
                }
                yield new ApiResponse(500, "Internal error", null);
            }
            case INVALID_AGE -> new ApiResponse(400, "Invalid age provided", null);
            case INVALID_EMAIL -> new ApiResponse(400, "Invalid email format", null);
            case MISSING_DATA -> new ApiResponse(400, "Missing required fields", null);
        };
    }
    
    private ValidationResult validateRequest(Object obj) {
        if (obj instanceof UserRequest user) {
            if (user.email() == null || !user.email().contains("@")) {
                return ValidationResult.INVALID_EMAIL;
            }
            if (user.age() < 18) {
                return ValidationResult.INVALID_AGE;
            }
            return ValidationResult.VALID;
        }
        return ValidationResult.MISSING_DATA;
    }
}

enum ValidationResult { VALID, INVALID_AGE, INVALID_EMAIL, MISSING_DATA }

For more detailed information about Java 14 features, check the official OpenJDK documentation. The Oracle Java 14 language changes guide provides comprehensive coverage of all language enhancements.

While Java 14 wasn't an LTS release, its features laid important groundwork for future versions. Many of these preview features became standard in later releases, making Java 14 knowledge valuable for long-term Java development strategy on enterprise servers and cloud deployments.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked