com um clique
java-11-to-17
// Upgrade Java projects from JDK 11 to JDK 17 covering Records, Sealed Classes, Pattern Matching for instanceof, Switch Expressions, Text Blocks
// Upgrade Java projects from JDK 11 to JDK 17 covering Records, Sealed Classes, Pattern Matching for instanceof, Switch Expressions, Text Blocks
Assess a Java project's modernization posture — Java version, framework versions, dependency health, vulnerabilities, and migration opportunities
Deploy Java applications to Azure services including App Service, Container Apps, AKS, and Spring Apps
Upgrade Java projects from JDK 17 to JDK 21 covering Virtual Threads, Pattern Matching for switch, Record Patterns, Sequenced Collections
Upgrade Java projects from JDK 21 to JDK 25 covering Flexible Constructor Bodies, Stream Gatherers, Class-File API, Scoped Values, Security Manager removal
Upgrade Java projects from JDK 8 to JDK 11 covering module system, removed Java EE modules (JAXB, JAX-WS, CORBA), var keyword, HTTP Client, collection factory methods
Migrate Java application services and dependencies to Azure equivalents using predefined migration tasks
| name | java-11-to-17 |
| description | Upgrade Java projects from JDK 11 to JDK 17 covering Records, Sealed Classes, Pattern Matching for instanceof, Switch Expressions, Text Blocks |
This guide provides comprehensive GitHub Copilot instructions for upgrading Java projects from JDK 11 to JDK 17, covering major language features, API changes, and migration patterns based on 47 JEPs integrated between these versions.
Migration Pattern: Convert data classes to records
// Old: Traditional data class
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
@Override
public boolean equals(Object obj) { /* boilerplate */ }
@Override
public int hashCode() { /* boilerplate */ }
@Override
public String toString() { /* boilerplate */ }
}
// New: Record (Java 16+)
public record Person(String name, int age) {
// Compact constructor for validation
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
// Custom methods can be added
public boolean isAdult() {
return age >= 18;
}
}
Migration Pattern: Use sealed classes for restricted inheritance
// New: Sealed class hierarchy
public sealed class Shape
permits Circle, Rectangle, Triangle {
public abstract double area();
}
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public final class Rectangle extends Shape {
private final double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
public non-sealed class Triangle extends Shape {
// Non-sealed allows further inheritance
private final double base, height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
}
Migration Pattern: Simplify instanceof checks
// Old: Traditional instanceof with casting
public String processObject(Object obj) {
if (obj instanceof String) {
String str = (String) obj;
return str.toUpperCase();
} else if (obj instanceof Integer) {
Integer num = (Integer) obj;
return "Number: " + num;
} else if (obj instanceof List<?>) {
List<?> list = (List<?>) obj;
return "List with " + list.size() + " elements";
}
return "Unknown type";
}
// New: Pattern matching for instanceof (Java 16+)
public String processObject(Object obj) {
if (obj instanceof String str) {
return str.toUpperCase();
} else if (obj instanceof Integer num) {
return "Number: " + num;
} else if (obj instanceof List<?> list) {
return "List with " + list.size() + " elements";
}
return "Unknown type";
}
// Works great with sealed classes
public String describeShape(Shape shape) {
if (shape instanceof Circle circle) {
return "Circle with radius " + circle.radius();
} else if (shape instanceof Rectangle rect) {
return "Rectangle " + rect.width() + "x" + rect.height();
} else if (shape instanceof Triangle triangle) {
return "Triangle with base " + triangle.base();
}
return "Unknown shape";
}
Migration Pattern: Convert switch statements to expressions
// Old: Traditional switch statement
public String getDayType(DayOfWeek day) {
String result;
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
result = "Workday";
break;
case SATURDAY:
case SUNDAY:
result = "Weekend";
break;
default:
throw new IllegalArgumentException("Unknown day: " + day);
}
return result;
}
// New: Switch expression (Java 14+)
public String getDayType(DayOfWeek day) {
return switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Workday";
case SATURDAY, SUNDAY -> "Weekend";
};
}
// With yield for complex logic
public int calculateScore(Grade grade) {
return switch (grade) {
case A -> 100;
case B -> 85;
case C -> 70;
case D -> {
System.out.println("Consider improvement");
yield 55;
}
case F -> {
System.out.println("Needs retake");
yield 0;
}
};
}
Migration Pattern: Use text blocks for multi-line strings
// Old: Concatenated strings
String html = "<html>\n" +
" <body>\n" +
" <h1>Hello World</h1>\n" +
" <p>Welcome to Java 17!</p>\n" +
" </body>\n" +
"</html>";
String sql = "SELECT p.id, p.name, p.email, " +
" a.street, a.city, a.state " +
"FROM person p " +
"JOIN address a ON p.address_id = a.id " +
"WHERE p.active = true " +
"ORDER BY p.name";
// New: Text blocks (Java 15+)
String html = """
<html>
<body>
<h1>Hello World</h1>
<p>Welcome to Java 17!</p>
</body>
</html>
""";
String sql = """
SELECT p.id, p.name, p.email,
a.street, a.city, a.state
FROM person p
JOIN address a ON p.address_id = a.id
WHERE p.active = true
ORDER BY p.name
""";
// With string interpolation methods
String json = """
{
"name": "%s",
"age": %d,
"city": "%s"
}
""".formatted(name, age, city);
Migration Guidance: Better NPE debugging (enabled by default in Java 17)
// Old NPE message: "Exception in thread 'main' java.lang.NullPointerException"
// New NPE message shows exactly what was null:
// "Cannot invoke 'String.length()' because the return value of 'Person.getName()' is null"
public class PersonProcessor {
public void processPersons(List<Person> persons) {
// This will show exactly which person.getName() returned null
persons.stream()
.mapToInt(person -> person.getName().length()) // Clear NPE if getName() returns null
.sum();
}
// Better error messages help with complex expressions
public void complexExample(Map<String, List<Person>> groups) {
// NPE will show exactly which part of the chain is null
int totalNameLength = groups.get("admins")
.get(0)
.getName()
.length();
}
}
Migration Pattern: Use for framework and proxy generation
// For frameworks creating dynamic proxies
public class DynamicProxyExample {
public static <T> T createProxy(Class<T> interfaceClass, InvocationHandler handler) {
// Hidden classes provide better encapsulation for dynamically generated classes
MethodHandles.Lookup lookup = MethodHandles.lookup();
// Framework code would use hidden classes for better isolation
// This is typically handled by frameworks, not application code
return interfaceClass.cast(
Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class<?>[]{interfaceClass},
handler
)
);
}
}
Migration Pattern: Use for compile-time constants
import java.lang.constant.*;
// For advanced metaprogramming and tooling
public class ConstantExample {
// Use dynamic constants for computed values
public static final DynamicConstantDesc<String> COMPUTED_CONSTANT =
DynamicConstantDesc.of(
ConstantDescs.BSM_INVOKE,
"computeValue",
ConstantDescs.CD_String
);
// Primarily used by compiler and framework developers
public static String computeValue() {
return "Computed at runtime, cached as constant";
}
}
Migration Pattern: Enhanced security for object deserialization
import java.io.*;
public class SecureDeserialization {
// Set up deserialization filters for security
public static void setupSerializationFilters() {
// Global filter
ObjectInputFilter globalFilter = ObjectInputFilter.Config.createFilter(
"java.base/*;java.util.*;!*"
);
ObjectInputFilter.Config.setSerialFilter(globalFilter);
}
public <T> T deserializeSecurely(byte[] data, Class<T> expectedType) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bis)) {
// Context-specific filter
ObjectInputFilter contextFilter = ObjectInputFilter.Config.createFilter(
expectedType.getName() + ";java.lang.*;!*"
);
ois.setObjectInputFilter(contextFilter);
return expectedType.cast(ois.readObject());
}
}
}
Migration Pattern: Use new random generator interfaces
import java.util.random.*;
// Old: Limited Random class
Random oldRandom = new Random();
int oldValue = oldRandom.nextInt(100);
// New: Enhanced random generators (Java 17+)
RandomGenerator generator = RandomGeneratorFactory
.of("Xoshiro256PlusPlus")
.create(System.nanoTime());
RandomGenerator.SplittableGenerator splittableGenerator =
RandomGeneratorFactory.of("L64X128MixRandom").create();
// Better for parallel processing
splittableGenerator.splits(4)
.parallel()
.mapToInt(rng -> rng.nextInt(1000))
.forEach(System.out::println);
// Streamable random values
generator.ints(10, 1, 101)
.forEach(System.out::println);
Migration Pattern: Use Unix domain sockets for local IPC
import java.net.UnixDomainSocketAddress;
import java.nio.channels.*;
// Old: TCP sockets for local communication
// ServerSocketChannel server = ServerSocketChannel.open();
// server.bind(new InetSocketAddress("localhost", 8080));
// New: Unix domain sockets (Java 16+)
public class UnixSocketExample {
public void createUnixDomainServer() throws IOException {
Path socketPath = Path.of("/tmp/my-app.socket");
UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);
try (ServerSocketChannel server = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
server.bind(address);
while (true) {
try (SocketChannel client = server.accept()) {
// Handle client connection
handleClient(client);
}
}
}
}
public void connectToUnixSocket() throws IOException {
Path socketPath = Path.of("/tmp/my-app.socket");
UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);
try (SocketChannel client = SocketChannel.open(address)) {
// Communicate with server
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
}
}
private void handleClient(SocketChannel client) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = client.read(buffer);
// Process client data
}
}
Migration Pattern: Use for persistent memory operations
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
public class PersistentMemoryExample {
public void usePersistentMemory() throws IOException {
Path nvmFile = Path.of("/mnt/pmem/data.bin");
try (FileChannel channel = FileChannel.open(nvmFile,
StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE)) {
// Map as persistent memory
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_WRITE, 0, 1024,
ExtendedMapMode.READ_WRITE_SYNC
);
// Write data that persists across crashes
buffer.putLong(0, System.currentTimeMillis());
buffer.putInt(8, 12345);
// Force write to persistent storage
buffer.force();
}
}
}
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
tasks.withType<JavaCompile> {
options.release.set(17)
}
tasks.withType<Test> {
useJUnitPlatform()
}
Migration Pattern: Remove Security Manager dependencies
// Old: Using Security Manager
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
// New: Alternative security approaches
// Use application-level security, containers, or process isolation
// Most applications don't need Security Manager functionality
Migration Pattern: Migrate from Applets to modern web technologies
// Old: Java Applet (deprecated)
public class MyApplet extends Applet {
@Override
public void start() {
// Applet code
}
}
// New: Modern alternatives
// 1. Convert to standalone Java application
public class MyApplication extends JFrame {
public MyApplication() {
setTitle("My Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Application code
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new MyApplication().setVisible(true);
});
}
}
// 2. Use Java Web Start alternative (jlink)
// 3. Convert to web application using modern frameworks
Migration Pattern: Use alternative JavaScript engines
// Old: Nashorn (removed in Java 17)
// ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
// New: Alternative approaches
// 1. Use GraalVM JavaScript engine
ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");
// 2. Use external JavaScript execution
ProcessBuilder pb = new ProcessBuilder("node", "script.js");
Process process = pb.start();
// 3. Use web-based approach or embedded browser
Migration Pattern: Enable ZGC for low-latency applications
# Enable ZGC (production feature since Java 15)
-XX:+UseZGC
# Monitor ZGC performance
-XX:+LogVMOutput
-XX:LogFile=gc.log
Migration Pattern: Enable Shenandoah for consistent latency
# Enable Shenandoah (production feature since Java 15)
-XX:+UseShenandoahGC
# Shenandoah tuning
-XX:ShenandoahGCHeuristics=adaptive
Migration Pattern: Improved startup performance
# CDS is enabled by default, but you can create custom archives
# Create custom CDS archive
java -XX:DumpLoadedClassList=classes.lst -cp myapp.jar com.example.Main
java -Xshare:dump -XX:SharedClassListFile=classes.lst -XX:SharedArchiveFile=myapp.jsa -cp myapp.jar
# Use custom CDS archive
java -XX:SharedArchiveFile=myapp.jsa -cp myapp.jar com.example.Main
Update build system
Address removals and deprecations
Implement Records
Add Pattern Matching
Switch Expressions
Text Blocks
Design sealed hierarchies
Testing and validation
Use Records for Data Classes
Apply Pattern Matching Strategically
Adopt Text Blocks for Multi-line Content
Design with Sealed Classes
Leverage Enhanced Random Generators
This comprehensive guide enables GitHub Copilot to provide contextually appropriate suggestions when upgrading Java 11 projects to Java 17, focusing on language enhancements, API improvements, and modern Java development practices.