Title: java.evolved Code Snippets | java.evolved
Open Graph Title: java.evolved Code Snippets | java.evolved
X Title: java.evolved Code Snippets | java.evolved
Description: A collection of modern Java code snippets. Every old Java pattern next to its clean, modern replacement — side by side.
Open Graph Description: A collection of modern Java code snippets. Every old Java pattern next to its clean, modern replacement — side by side.
X Description: A collection of modern Java code snippets. Every old Java pattern next to its clean, modern replacement — side by side.
Opengraph URL: https://javaevolved.github.io
X: @bruaborges
Domain: javaevolved.github.io
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "java.evolved",
"url": "https:\/\/javaevolved.github.io",
"description": "A collection of modern Java code snippets. Every old Java pattern next to its clean, modern replacement — side by side."
}
{
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": "java.evolved: Java has evolved. Your code can too.",
"url": "https:\/\/javaevolved.github.io\/",
"description": "A collection of modern Java code snippets. Every old Java pattern next to its clean, modern replacement — side by side.",
"mainEntity": {
"@type": "ItemList",
"numberOfItems": 113,
"itemListElement": [
{"@type": "ListItem", "position": 1, "url": "https:\/\/javaevolved.github.io\/type-inference-with-var", "name": "Type inference with var"}
,{"@type": "ListItem", "position": 2, "url": "https:\/\/javaevolved.github.io\/text-blocks-for-multiline-strings", "name": "Text blocks for multiline strings"}
,{"@type": "ListItem", "position": 3, "url": "https:\/\/javaevolved.github.io\/switch-expressions", "name": "Switch expressions"}
,{"@type": "ListItem", "position": 4, "url": "https:\/\/javaevolved.github.io\/pattern-matching-instanceof", "name": "Pattern matching for instanceof"}
,{"@type": "ListItem", "position": 5, "url": "https:\/\/javaevolved.github.io\/records-for-data-classes", "name": "Records for data classes"}
,{"@type": "ListItem", "position": 6, "url": "https:\/\/javaevolved.github.io\/sealed-classes", "name": "Sealed classes for type hierarchies"}
,{"@type": "ListItem", "position": 7, "url": "https:\/\/javaevolved.github.io\/record-patterns", "name": "Record patterns (destructuring)"}
,{"@type": "ListItem", "position": 8, "url": "https:\/\/javaevolved.github.io\/unnamed-variables", "name": "Unnamed variables with _"}
,{"@type": "ListItem", "position": 9, "url": "https:\/\/javaevolved.github.io\/compact-source-files", "name": "Compact source files"}
,{"@type": "ListItem", "position": 10, "url": "https:\/\/javaevolved.github.io\/flexible-constructor-bodies", "name": "Flexible constructor bodies"}
,{"@type": "ListItem", "position": 11, "url": "https:\/\/javaevolved.github.io\/enhanced-for-with-var", "name": "Enhanced for with var"}
,{"@type": "ListItem", "position": 12, "url": "https:\/\/javaevolved.github.io\/diamond-operator", "name": "Diamond operator"}
,{"@type": "ListItem", "position": 13, "url": "https:\/\/javaevolved.github.io\/private-interface-methods", "name": "Private interface methods"}
,{"@type": "ListItem", "position": 14, "url": "https:\/\/javaevolved.github.io\/pattern-matching-switch", "name": "Pattern matching in switch"}
,{"@type": "ListItem", "position": 15, "url": "https:\/\/javaevolved.github.io\/guarded-patterns", "name": "Guarded patterns with when"}
,{"@type": "ListItem", "position": 16, "url": "https:\/\/javaevolved.github.io\/primitive-types-in-patterns", "name": "Primitive types in patterns"}
,{"@type": "ListItem", "position": 17, "url": "https:\/\/javaevolved.github.io\/module-import-declarations", "name": "Module import declarations"}
,{"@type": "ListItem", "position": 18, "url": "https:\/\/javaevolved.github.io\/exhaustive-switch", "name": "Exhaustive switch without default"}
,{"@type": "ListItem", "position": 19, "url": "https:\/\/javaevolved.github.io\/immutable-list-creation", "name": "Immutable list creation"}
,{"@type": "ListItem", "position": 20, "url": "https:\/\/javaevolved.github.io\/immutable-map-creation", "name": "Immutable map creation"}
,{"@type": "ListItem", "position": 21, "url": "https:\/\/javaevolved.github.io\/immutable-set-creation", "name": "Immutable set creation"}
,{"@type": "ListItem", "position": 22, "url": "https:\/\/javaevolved.github.io\/copying-collections-immutably", "name": "Copying collections immutably"}
,{"@type": "ListItem", "position": 23, "url": "https:\/\/javaevolved.github.io\/map-entry-factory", "name": "Map.entry() factory"}
,{"@type": "ListItem", "position": 24, "url": "https:\/\/javaevolved.github.io\/sequenced-collections", "name": "Sequenced collections"}
,{"@type": "ListItem", "position": 25, "url": "https:\/\/javaevolved.github.io\/collectors-teeing", "name": "Collectors.teeing()"}
,{"@type": "ListItem", "position": 26, "url": "https:\/\/javaevolved.github.io\/stream-toarray-typed", "name": "Typed stream toArray"}
,{"@type": "ListItem", "position": 27, "url": "https:\/\/javaevolved.github.io\/unmodifiable-collectors", "name": "Unmodifiable collectors"}
,{"@type": "ListItem", "position": 28, "url": "https:\/\/javaevolved.github.io\/string-isblank", "name": "String.isBlank()"}
,{"@type": "ListItem", "position": 29, "url": "https:\/\/javaevolved.github.io\/string-strip", "name": "String.strip() vs trim()"}
,{"@type": "ListItem", "position": 30, "url": "https:\/\/javaevolved.github.io\/string-repeat", "name": "String.repeat()"}
,{"@type": "ListItem", "position": 31, "url": "https:\/\/javaevolved.github.io\/string-indent-transform", "name": "String.indent() and transform()"}
,{"@type": "ListItem", "position": 32, "url": "https:\/\/javaevolved.github.io\/string-formatted", "name": "String.formatted()"}
,{"@type": "ListItem", "position": 33, "url": "https:\/\/javaevolved.github.io\/multiline-json-sql", "name": "Multiline JSON\/SQL\/HTML"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/string-chars-stream", "name": "String chars as stream"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stream-of-nullable", "name": "Stream.ofNullable()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stream-iterate-predicate", "name": "Stream.iterate() with predicate"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stream-takewhile-dropwhile", "name": "Stream takeWhile \/ dropWhile"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/collectors-flatmapping", "name": "Collectors.flatMapping()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stream-tolist", "name": "Stream.toList()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stream-mapmulti", "name": "Stream.mapMulti()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stream-gatherers", "name": "Stream gatherers"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/virtual-thread-executor", "name": "Virtual thread executor"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/optional-ifpresentorelse", "name": "Optional.ifPresentOrElse()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/optional-or", "name": "Optional.or() fallback"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/virtual-threads", "name": "Virtual threads"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/structured-concurrency", "name": "Structured concurrency"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/scoped-values", "name": "Scoped values"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/stable-values", "name": "Stable values"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/completablefuture-chaining", "name": "CompletableFuture chaining"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/executor-try-with-resources", "name": "ExecutorService auto-close"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/thread-sleep-duration", "name": "Thread.sleep with Duration"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/process-api", "name": "Modern Process API"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/concurrent-http-virtual", "name": "Concurrent HTTP with virtual threads"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/lock-free-lazy-init", "name": "Lock-free lazy initialization"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/http-client", "name": "Modern HTTP client"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/reading-files", "name": "Reading files"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/writing-files", "name": "Writing files"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/inputstream-transferto", "name": "InputStream.transferTo()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/path-of", "name": "Path.of() factory"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/try-with-resources-effectively-final", "name": "Try-with-resources improvement"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/files-mismatch", "name": "Files.mismatch()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/deserialization-filters", "name": "Deserialization filters"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/helpful-npe", "name": "Helpful NullPointerExceptions"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/optional-chaining", "name": "Optional chaining"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/require-nonnull-else", "name": "Objects.requireNonNullElse()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/multi-catch", "name": "Multi-catch exception handling"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/null-in-switch", "name": "Null case in switch"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/record-based-errors", "name": "Record-based error responses"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/java-time-basics", "name": "java.time API basics"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/duration-and-period", "name": "Duration and Period"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/date-formatting", "name": "Date formatting"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/instant-precision", "name": "Instant with nanosecond precision"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/math-clamp", "name": "Math.clamp()"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/hex-format", "name": "HexFormat"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/pem-encoding", "name": "PEM encoding\/decoding"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/key-derivation-functions", "name": "Key Derivation Functions"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/strong-random", "name": "Strong random generation"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/tls-default", "name": "TLS 1.3 by default"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/jshell-prototyping", "name": "JShell for prototyping"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/single-file-execution", "name": "Single-file execution"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/multi-file-source", "name": "Multi-file source launcher"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/jfr-profiling", "name": "JFR for profiling"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/compact-object-headers", "name": "Compact object headers"}
,{"@type": "ListItem", "position": 34, "url": "https:\/\/javaevolved.github.io\/aot-class-preloading", "name": "AOT class preloading"}
,{"@type": "ListItem", "position": 35, "url": "https:\/\/javaevolved.github.io\/default-interface-methods", "name": "Default interface methods"}
]
}
}
| theme-color | #f97316 |
| mobile-web-app-capable | yes |
| apple-mobile-web-app-status-bar-style | black-translucent |
| apple-mobile-web-app-title | java.evolved |
| og:type | website |
| og:site_name | java.evolved |
| og:locale | en |
| og:image | https://javaevolved.github.io/images/social-card.png |
| og:image:width | 1200 |
| og:image:height | 630 |
| og:image:type | image/png |
| twitter:card | summary_large_image |
| twitter:image | https://javaevolved.github.io/images/social-card.png |
Links:
| java.evolved | https://javaevolved.github.io/ |
| https://github.com/javaevolved/javaevolved.github.io | |
| ✕ Old public class Point { private final int x, y; public Point(int x, int y) { ... } public int getX() { return x; } public int getY() { return y; } // equals, hashCode, toString } ✓ Modern public record Point(int x, int y) {} | https://javaevolved.github.io/language/records-for-data-classes.html |
| 𝕏 | https://x.com/intent/tweet?url=https%3A%2F%2Fjavaevolved.github.io&text=java.evolved%20%E2%80%93%20Every%20old%20Java%20pattern%20next%20to%20its%20modern%20replacement%2C%20side%20by%20side. |
| 🦋 | https://bsky.app/intent/compose?text=java.evolved%20%E2%80%93%20Every%20old%20Java%20pattern%20next%20to%20its%20modern%20replacement%2C%20side%20by%20side.%20https%3A%2F%2Fjavaevolved.github.io |
| in | https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fjavaevolved.github.io |
| ⬡ | https://www.reddit.com/submit?url=https%3A%2F%2Fjavaevolved.github.io&title=java.evolved%20%E2%80%93%20Old%20Java%20vs%20Modern%20Java%2C%20side%20by%20side |
| App Modernization → | https://github.com/solutions/use-case/app-modernization |
| Java Guide → | https://docs.github.com/en/enterprise-cloud@latest/copilot/tutorials/modernize-java-applications |
|
Language
Calling out to C code from Java
Old
public class CallCFromJava {
static { System.loadLibrary("strlen-jni"); }
public static native long strlen(String s);
public static void main(String[] args) {
long ret = strlen("Bambi");
System.out.println("Return value " + ret); // 5
}
}
// Run javac -h to generate the .h file, then write C:
// #include "CallCFromJava.h"
// #include | https://javaevolved.github.io/language/call-c-from-java.html |
|
Language
Compact canonical constructor
Old
public record Person(String name,
List | https://javaevolved.github.io/language/compact-canonical-constructor.html |
| Language Compact source files Old public class HelloWorld { public static void main(String[] args) { System.out.println( "Hello, World!"); } } Modern void main() { IO.println("Hello, World!"); } hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/language/compact-source-files.html |
| Language Default interface methods Old // Need abstract class to share behavior public abstract class AbstractLogger { public void log(String msg) { System.out.println( timestamp() + ": " + msg); } abstract String timestamp(); } // Single inheritance only public class FileLogger extends AbstractLogger { ... } Modern public interface Logger { default void log(String msg) { IO.println( timestamp() + ": " + msg); } String timestamp(); } // Multiple interfaces allowed public class FileLogger implements Logger, Closeable { ... } hover to see modern → JDK 8+ learn more → | https://javaevolved.github.io/language/default-interface-methods.html |
|
Language
Diamond with anonymous classes
Old
Map | https://javaevolved.github.io/language/diamond-operator.html |
| Language Exhaustive switch without default Old // Must add default even though // all cases are covered double area(Shape s) { if (s instanceof Circle c) return Math.PI * c.r() * c.r(); else if (s instanceof Rect r) return r.w() * r.h(); else throw new IAE(); } Modern // sealed Shape permits Circle, Rect double area(Shape s) { return switch (s) { case Circle c -> Math.PI * c.r() * c.r(); case Rect r -> r.w() * r.h(); }; // no default needed! } hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/language/exhaustive-switch.html |
| Language Flexible constructor bodies Old class Square extends Shape { Square(double side) { super(side, side); // can't validate BEFORE super! if (side <= 0) throw new IAE("bad"); } } Modern class Square extends Shape { Square(double side) { if (side <= 0) throw new IAE("bad"); super(side, side); } } hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/language/flexible-constructor-bodies.html |
| Language Guarded patterns with when Old if (shape instanceof Circle) { Circle c = (Circle) shape; if (c.radius() > 10) { return "large circle"; } else { return "small circle"; } } else { return "not a circle"; } Modern return switch (shape) { case Circle c when c.radius() > 10 -> "large circle"; case Circle c -> "small circle"; default -> "not a circle"; }; hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/language/guarded-patterns.html |
|
Language
Markdown in Javadoc comments
Old
/**
* Returns the {@code User} with
* the given ID.
*
* Example: * {@code
* var user = findUser(123);
* }
*
* @param id the user ID
* @return the user
*/
public User findUser(int id) { ... }
Modern
/// Returns the `User` with
/// the given ID.
///
/// Example:
/// ```java
/// var user = findUser(123);
/// ```
///
/// @param id the user ID
/// @return the user
public User findUser(int id) { ... }
hover to see modern →
JDK 23+
learn more →
| https://javaevolved.github.io/language/markdown-javadoc-comments.html |
| Language Module import declarations Old import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; Modern import module java.base; // All of java.util, java.io, java.nio // etc. available in one line hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/language/module-import-declarations.html |
| Language Pattern matching for instanceof Old if (obj instanceof String) { String s = (String) obj; int length = s.length(); // do something with 'length' } Modern if (obj instanceof String s) { int length = s.length(); // do something with 'length' } hover to see modern → JDK 16+ learn more → | https://javaevolved.github.io/language/pattern-matching-instanceof.html |
| Language Pattern matching in switch Old String format(Object obj) { if (obj instanceof Integer i) return "int: " + i; else if (obj instanceof Double d) return "double: " + d; else if (obj instanceof String s) return "str: " + s; return "unknown"; } Modern String format(Object obj) { return switch (obj) { case Integer i -> "int: " + i; case Double d -> "double: " + d; case String s -> "str: " + s; default -> "unknown"; }; } hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/language/pattern-matching-switch.html |
| Language Primitive types in patterns Old String classify(int code) { if (code >= 200 && code < 300) return "success"; else if (code >= 400 && code < 500) return "client error"; else return "other"; } Modern String classify(int code) { return switch (code) { case int c when c >= 200 && c < 300 -> "success"; case int c when c >= 400 && c < 500 -> "client error"; default -> "other"; }; } hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/language/primitive-types-in-patterns.html |
| Language Private interface methods Old interface Logger { default void logInfo(String msg) { System.out.println( "[INFO] " + timestamp() + msg); } default void logWarn(String msg) { System.out.println( "[WARN] " + timestamp() + msg); } } Modern interface Logger { private String format(String lvl, String msg) { return "[" + lvl + "] " + timestamp() + msg; } default void logInfo(String msg) { IO.println(format("INFO", msg)); } default void logWarn(String msg) { IO.println(format("WARN", msg)); } } hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/language/private-interface-methods.html |
| Language Record patterns (destructuring) Old if (obj instanceof Point) { Point p = (Point) obj; int x = p.getX(); int y = p.getY(); System.out.println(x + y); } Modern if (obj instanceof Point(int x, int y)) { IO.println(x + y); } hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/language/record-patterns.html |
| Language Records for data classes Old public class Point { private final int x, y; public Point(int x, int y) { ... } public int getX() { return x; } public int getY() { return y; } // equals, hashCode, toString } Modern public record Point(int x, int y) {} hover to see modern → JDK 16+ learn more → | https://javaevolved.github.io/language/records-for-data-classes.html |
| Language Sealed classes for type hierarchies Old // Anyone can extend Shape public abstract class Shape { } public class Circle extends Shape { } public class Rect extends Shape { } // unknown subclasses possible Modern public sealed interface Shape permits Circle, Rect {} public record Circle(double r) implements Shape {} public record Rect(double w, double h) implements Shape {} hover to see modern → JDK 17+ learn more → | https://javaevolved.github.io/language/sealed-classes.html |
| Language Static members in inner classes Old class Library { // Must be static nested class static class Book { static int globalBookCount; Book() { globalBookCount++; } } } // Usage var book = new Library.Book(); Modern class Library { // Can be inner class with statics class Book { static int globalBookCount; Book() { Book.globalBookCount++; } } } // Usage var lib = new Library(); var book = lib.new Book(); hover to see modern → JDK 16+ learn more → | https://javaevolved.github.io/language/static-members-in-inner-classes.html |
| Language Static methods in interfaces Old // Separate utility class needed public class ValidatorUtils { public static boolean isBlank( String s) { return s == null || s.trim().isEmpty(); } } // Usage if (ValidatorUtils.isBlank(input)) { ... } Modern public interface Validator { boolean validate(String s); static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); } } // Usage if (Validator.isBlank(input)) { ... } hover to see modern → JDK 8+ learn more → | https://javaevolved.github.io/language/static-methods-in-interfaces.html |
| Language Switch expressions Old String msg; switch (day) { case MONDAY: msg = "Start"; break; case FRIDAY: msg = "End"; break; default: msg = "Mid"; } Modern String msg = switch (day) { case MONDAY -> "Start"; case FRIDAY -> "End"; default -> "Mid"; }; hover to see modern → JDK 14+ learn more → | https://javaevolved.github.io/language/switch-expressions.html |
| Language Text blocks for multiline strings Old String json = "{\n" + " \"name\": \"Duke\",\n" + " \"age\": 30\n" + "}"; Modern String json = """ { "name": "Duke", "age": 30 }"""; hover to see modern → JDK 15+ learn more → | https://javaevolved.github.io/language/text-blocks-for-multiline-strings.html |
|
Language
Type inference with var
Old
Map | https://javaevolved.github.io/language/type-inference-with-var.html |
| Language Unnamed variables with _ Old try { parse(input); } catch (Exception ignored) { log("parse failed"); } map.forEach((key, value) -> { process(value); // key unused }); Modern try { parse(input); } catch (Exception _) { log("parse failed"); } map.forEach((_, value) -> { process(value); }); hover to see modern → JDK 22+ learn more → | https://javaevolved.github.io/language/unnamed-variables.html |
| Collections Collectors.teeing() Old long count = items.stream().count(); double sum = items.stream() .mapToDouble(Item::price) .sum(); var result = new Stats(count, sum); Modern var result = items.stream().collect( Collectors.teeing( Collectors.counting(), Collectors.summingDouble(Item::price), Stats::new ) ); hover to see modern → JDK 12+ learn more → | https://javaevolved.github.io/collections/collectors-teeing.html |
|
Collections
Copying collections immutably
Old
List | https://javaevolved.github.io/collections/copying-collections-immutably.html |
|
Collections
Immutable list creation
Old
List | https://javaevolved.github.io/collections/immutable-list-creation.html |
|
Collections
Immutable map creation
Old
Map | https://javaevolved.github.io/collections/immutable-map-creation.html |
|
Collections
Immutable set creation
Old
Set | https://javaevolved.github.io/collections/immutable-set-creation.html |
|
Collections
Map.entry() factory
Old
Map.Entry | https://javaevolved.github.io/collections/map-entry-factory.html |
|
Collections
Reverse list iteration
Old
for (ListIterator | https://javaevolved.github.io/collections/reverse-list-iteration.html |
| Collections Sequenced collections Old // Get last element Object last = list.get(list.size() - 1); // Get first Object first = list.get(0); // Reverse iteration: manual Modern var last = list.getLast(); var first = list.getFirst(); var reversed = list.reversed(); hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/collections/sequenced-collections.html |
|
Collections
Typed stream toArray
Old
List | https://javaevolved.github.io/collections/stream-toarray-typed.html |
|
Collections
Unmodifiable collectors
Old
List | https://javaevolved.github.io/collections/unmodifiable-collectors.html |
| Strings String chars as stream Old for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (Character.isDigit(c)) { process(c); } } Modern str.chars() .filter(Character::isDigit) .forEach(c -> process((char) c)); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/strings/string-chars-stream.html |
| Strings String.formatted() Old String msg = String.format( "Hello %s, you are %d", name, age ); Modern String msg = "Hello %s, you are %d" .formatted(name, age); hover to see modern → JDK 15+ learn more → | https://javaevolved.github.io/strings/string-formatted.html |
| Strings String.indent() and transform() Old String[] lines = text.split("\n"); StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append(" ").append(line) .append("\n"); } String indented = sb.toString(); Modern String indented = text.indent(4); String result = text .transform(String::strip) .transform(s -> s.replace(" ", "-")); hover to see modern → JDK 12+ learn more → | https://javaevolved.github.io/strings/string-indent-transform.html |
| Strings String.isBlank() Old boolean blank = str.trim().isEmpty(); // or: str.trim().length() == 0 Modern boolean blank = str.isBlank(); // handles Unicode whitespace too hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/strings/string-isblank.html |
| Strings String.lines() for line splitting Old String text = "one\ntwo\nthree"; String[] lines = text.split("\n"); for (String line : lines) { System.out.println(line); } Modern String text = "one\ntwo\nthree"; text.lines().forEach(IO::println); hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/strings/string-lines.html |
| Strings String.repeat() Old StringBuilder sb = new StringBuilder(); for (int i = 0; i < 3; i++) { sb.append("abc"); } String result = sb.toString(); Modern String result = "abc".repeat(3); // "abcabcabc" hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/strings/string-repeat.html |
| Strings String.strip() vs trim() Old // trim() only removes ASCII whitespace // (chars <= U+0020) String clean = str.trim(); Modern // strip() removes all Unicode whitespace String clean = str.strip(); String left = str.stripLeading(); String right = str.stripTrailing(); hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/strings/string-strip.html |
|
Streams
Collectors.flatMapping()
Old
// Flatten within a grouping collector
// Required complex custom collector
Map | https://javaevolved.github.io/streams/collectors-flatmapping.html |
|
Streams
Optional.ifPresentOrElse()
Old
Optional | https://javaevolved.github.io/streams/optional-ifpresentorelse.html |
|
Streams
Optional.or() fallback
Old
Optional | https://javaevolved.github.io/streams/optional-or.html |
|
Streams
Predicate.not() for negation
Old
List | https://javaevolved.github.io/streams/predicate-not.html |
Streams
Stream gatherers
Old
// Sliding window: manual implementation
List
| https://javaevolved.github.io/streams/stream-gatherers.html |
| Streams Stream.iterate() with predicate Old Stream.iterate(1, n -> n * 2) .limit(10) .forEach(System.out::println); // can't stop at a condition Modern Stream.iterate( 1, n -> n < 1000, n -> n * 2 ).forEach(IO::println); // stops when n >= 1000 hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/streams/stream-iterate-predicate.html |
|
Streams
Stream.mapMulti()
Old
stream.flatMap(order ->
order.items().stream()
.map(item -> new OrderItem(
order.id(), item)
)
);
Modern
stream. | https://javaevolved.github.io/streams/stream-mapmulti.html |
|
Streams
Stream.ofNullable()
Old
Stream | https://javaevolved.github.io/streams/stream-of-nullable.html |
|
Streams
Stream takeWhile / dropWhile
Old
List | https://javaevolved.github.io/streams/stream-takewhile-dropwhile.html |
|
Streams
Stream.toList()
Old
List | https://javaevolved.github.io/streams/stream-tolist.html |
| Streams Virtual thread executor Old ExecutorService exec = Executors.newFixedThreadPool(10); try { futures = tasks.stream() .map(t -> exec.submit(t)) .toList(); } finally { exec.shutdown(); } Modern try (var exec = Executors .newVirtualThreadPerTaskExecutor()) { var futures = tasks.stream() .map(exec::submit) .toList(); } hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/streams/virtual-thread-executor.html |
|
Concurrency
CompletableFuture chaining
Old
Future | https://javaevolved.github.io/concurrency/completablefuture-chaining.html |
|
Concurrency
Concurrent HTTP with virtual threads
Old
ExecutorService pool =
Executors.newFixedThreadPool(10);
List | https://javaevolved.github.io/concurrency/concurrent-http-virtual.html |
| Concurrency ExecutorService auto-close Old ExecutorService exec = Executors.newCachedThreadPool(); try { exec.submit(task); } finally { exec.shutdown(); exec.awaitTermination( 1, TimeUnit.MINUTES); } Modern try (var exec = Executors.newCachedThreadPool()) { exec.submit(task); } // auto shutdown + await on close hover to see modern → JDK 19+ learn more → | https://javaevolved.github.io/concurrency/executor-try-with-resources.html |
|
Concurrency
Lock-free lazy initialization
Old
class Config {
private static volatile Config inst;
static Config get() {
if (inst == null) {
synchronized (Config.class) {
if (inst == null)
inst = load();
}
}
return inst;
}
}
Modern
class Config {
private static final
StableValue | https://javaevolved.github.io/concurrency/lock-free-lazy-init.html |
| Concurrency Modern Process API Old Process p = Runtime.getRuntime() .exec("ls -la"); int code = p.waitFor(); // no way to get PID // no easy process info Modern ProcessHandle ph = ProcessHandle.current(); long pid = ph.pid(); ph.info().command() .ifPresent(IO::println); ph.children().forEach( c -> IO.println(c.pid())); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/concurrency/process-api.html |
|
Concurrency
Scoped values
Old
static final ThreadLocal | https://javaevolved.github.io/concurrency/scoped-values.html |
|
Concurrency
Stable values
Old
private volatile Logger logger;
Logger getLogger() {
if (logger == null) {
synchronized (this) {
if (logger == null)
logger = createLogger();
}
}
return logger;
}
Modern
private final StableValue | https://javaevolved.github.io/concurrency/stable-values.html |
|
Concurrency
Structured concurrency
Old
ExecutorService exec =
Executors.newFixedThreadPool(2);
Future | https://javaevolved.github.io/concurrency/structured-concurrency.html |
| Concurrency Thread.sleep with Duration Old // What unit is 5000? ms? us? Thread.sleep(5000); // 2.5 seconds: math required Thread.sleep(2500); Modern Thread.sleep( Duration.ofSeconds(5) ); Thread.sleep( Duration.ofMillis(2500) ); hover to see modern → JDK 19+ learn more → | https://javaevolved.github.io/concurrency/thread-sleep-duration.html |
| Concurrency Virtual threads Old Thread thread = new Thread(() -> { System.out.println("hello"); }); thread.start(); thread.join(); Modern Thread.startVirtualThread(() -> { IO.println("hello"); }).join(); hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/concurrency/virtual-threads.html |
| I/O Deserialization filters Old // Dangerous: accepts any class ObjectInputStream ois = new ObjectInputStream(input); Object obj = ois.readObject(); // deserialization attacks possible! Modern ObjectInputFilter filter = ObjectInputFilter.Config .createFilter( "com.myapp.*;!*" ); ois.setObjectInputFilter(filter); Object obj = ois.readObject(); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/io/deserialization-filters.html |
| I/O File memory mapping Old try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) { MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_WRITE, 0, (int) channel.size()); // Limited to 2GB // Freed by GC, no control } Modern FileChannel channel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE); try (Arena arena = Arena.ofShared()) { MemorySegment segment = channel.map( FileChannel.MapMode.READ_WRITE, 0, channel.size(), arena); // No size limit // ... } // Deterministic cleanup hover to see modern → JDK 22+ learn more → | https://javaevolved.github.io/io/file-memory-mapping.html |
| I/O Files.mismatch() Old // Compare two files byte by byte byte[] f1 = Files.readAllBytes(path1); byte[] f2 = Files.readAllBytes(path2); boolean equal = Arrays.equals(f1, f2); // loads both files entirely into memory Modern long pos = Files.mismatch(path1, path2); // -1 if identical // otherwise: position of first difference hover to see modern → JDK 12+ learn more → | https://javaevolved.github.io/io/files-mismatch.html |
| I/O Modern HTTP client Old URL url = new URL("https://api.com/data"); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); BufferedReader in = new BufferedReader( new InputStreamReader(con.getInputStream())); // read lines, close streams... Modern var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .uri(URI.create("https://api.com/data")) .build(); var response = client.send( request, BodyHandlers.ofString()); String body = response.body(); hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/io/http-client.html |
| I/O InputStream.transferTo() Old byte[] buf = new byte[8192]; int n; while ((n = input.read(buf)) != -1) { output.write(buf, 0, n); } Modern input.transferTo(output); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/io/inputstream-transferto.html |
| I/O IO class for console I/O Old import java.util.Scanner; Scanner sc = new Scanner(System.in); System.out.print("Name: "); String name = sc.nextLine(); System.out.println("Hello, " + name); sc.close(); Modern String name = IO.readln("Name: "); IO.println("Hello, " + name); hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/io/io-class-console-io.html |
| I/O Path.of() factory Old Path path = Paths.get("src", "main", "java", "App.java"); Modern var path = Path.of("src", "main", "java", "App.java"); hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/io/path-of.html |
| I/O Reading files Old StringBuilder sb = new StringBuilder(); try (BufferedReader br = new BufferedReader( new FileReader("data.txt"))) { String line; while ((line = br.readLine()) != null) sb.append(line).append("\n"); } String content = sb.toString(); Modern String content = Files.readString(Path.of("data.txt")); hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/io/reading-files.html |
| I/O Try-with-resources improvement Old Connection conn = getConnection(); // Must re-declare in try try (Connection c = conn) { use(c); } Modern Connection conn = getConnection(); // Use existing variable directly try (conn) { use(conn); } hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/io/try-with-resources-effectively-final.html |
| I/O Writing files Old try (FileWriter fw = new FileWriter("out.txt"); BufferedWriter bw = new BufferedWriter(fw)) { bw.write(content); } Modern Files.writeString( Path.of("out.txt"), content ); hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/io/writing-files.html |
| Errors Helpful NullPointerExceptions Old // Old NPE message: // "NullPointerException" // at MyApp.main(MyApp.java:42) // Which variable was null?! Modern // Modern NPE message: // Cannot invoke "String.length()" // because "user.address().city()" // is null // Exact variable identified! hover to see modern → JDK 14+ learn more → | https://javaevolved.github.io/errors/helpful-npe.html |
| Errors Multi-catch exception handling Old try { process(); } catch (IOException e) { log(e); } catch (SQLException e) { log(e); } catch (ParseException e) { log(e); } Modern try { process(); } catch (IOException | SQLException | ParseException e) { log(e); } hover to see modern → JDK 7+ learn more → | https://javaevolved.github.io/errors/multi-catch.html |
| Errors Null case in switch Old // Must check before switch if (status == null) { return "unknown"; } return switch (status) { case ACTIVE -> "active"; case PAUSED -> "paused"; default -> "other"; }; Modern return switch (status) { case null -> "unknown"; case ACTIVE -> "active"; case PAUSED -> "paused"; default -> "other"; }; hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/errors/null-in-switch.html |
| Errors Optional chaining Old String city = null; if (user != null) { Address addr = user.getAddress(); if (addr != null) { city = addr.getCity(); } } if (city == null) city = "Unknown"; Modern String city = Optional.ofNullable(user) .map(User::address) .map(Address::city) .orElse("Unknown"); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/errors/optional-chaining.html |
| Errors Optional.orElseThrow() without supplier Old // Risky: get() throws if empty, no clear intent String value = optional.get(); // Verbose: supplier just for NoSuchElementException String value = optional .orElseThrow(NoSuchElementException::new); Modern // Clear intent: throws NoSuchElementException if empty String value = optional.orElseThrow(); hover to see modern → JDK 10+ learn more → | https://javaevolved.github.io/errors/optional-orelsethrow.html |
| Errors Record-based error responses Old // Verbose error class public class ErrorResponse { private final int code; private final String message; // constructor, getters, equals, // hashCode, toString... } Modern public record ApiError( int code, String message, Instant timestamp ) { public ApiError(int code, String msg) { this(code, msg, Instant.now()); } } hover to see modern → JDK 16+ learn more → | https://javaevolved.github.io/errors/record-based-errors.html |
| Errors Objects.requireNonNullElse() Old String name = input != null ? input : "default"; // easy to get the order wrong Modern String name = Objects .requireNonNullElse( input, "default" ); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/errors/require-nonnull-else.html |
| Date/Time Date formatting Old // Not thread-safe! SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String formatted = sdf.format(date); // Must synchronize for concurrent use Modern DateTimeFormatter fmt = DateTimeFormatter.ofPattern( "uuuu-MM-dd"); String formatted = LocalDate.now().format(fmt); // Thread-safe, immutable hover to see modern → JDK 8+ learn more → | https://javaevolved.github.io/datetime/date-formatting.html |
| Date/Time Duration and Period Old // How many days between two dates? long diff = date2.getTime() - date1.getTime(); long days = diff / (1000 * 60 * 60 * 24); // ignores DST, leap seconds Modern long days = ChronoUnit.DAYS .between(date1, date2); Period period = Period.between( date1, date2); Duration elapsed = Duration.between( time1, time2); hover to see modern → JDK 8+ learn more → | https://javaevolved.github.io/datetime/duration-and-period.html |
| Date/Time HexFormat Old // Pad to 2 digits, uppercase String hex = String.format( "%02X", byteValue); // Parse hex string int val = Integer.parseInt( "FF", 16); Modern var hex = HexFormat.of() .withUpperCase(); String s = hex.toHexDigits( byteValue); byte[] bytes = hex.parseHex("48656C6C6F"); hover to see modern → JDK 17+ learn more → | https://javaevolved.github.io/datetime/hex-format.html |
| Date/Time Instant with nanosecond precision Old // Millisecond precision only long millis = System.currentTimeMillis(); // 1708012345678 Modern // Microsecond/nanosecond precision Instant now = Instant.now(); // 2025-02-15T20:12:25.678901234Z long nanos = now.getNano(); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/datetime/instant-precision.html |
| Date/Time java.time API basics Old // Mutable, confusing, zero-indexed months Calendar cal = Calendar.getInstance(); cal.set(2025, 0, 15); // January = 0! Date date = cal.getTime(); // not thread-safe Modern LocalDate date = LocalDate.of( 2025, Month.JANUARY, 15); LocalTime time = LocalTime.of(14, 30); Instant now = Instant.now(); // immutable, thread-safe hover to see modern → JDK 8+ learn more → | https://javaevolved.github.io/datetime/java-time-basics.html |
| Date/Time Math.clamp() Old // Clamp value between min and max int clamped = Math.min(Math.max(value, 0), 100); // or: min and max order confusion Modern int clamped = Math.clamp(value, 0, 100); // value constrained to [0, 100] hover to see modern → JDK 21+ learn more → | https://javaevolved.github.io/datetime/math-clamp.html |
| Security Key Derivation Functions Old SecretKeyFactory factory = SecretKeyFactory.getInstance( "PBKDF2WithHmacSHA256"); KeySpec spec = new PBEKeySpec( password, salt, 10000, 256); SecretKey key = factory.generateSecret(spec); Modern var kdf = KDF.getInstance("HKDF-SHA256"); SecretKey key = kdf.deriveKey( "AES", KDF.HKDFParameterSpec .ofExtract() .addIKM(inputKey) .addSalt(salt) .thenExpand(info, 32) .build() ); hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/security/key-derivation-functions.html |
| Security PEM encoding/decoding Old String pem = "-----BEGIN CERTIFICATE-----\n" + Base64.getMimeEncoder() .encodeToString( cert.getEncoded()) + "\n-----END CERTIFICATE-----"; Modern // Encode to PEM String pem = PEMEncoder.of() .encodeToString(cert); // Decode from PEM var cert = PEMDecoder.of() .decode(pemString); hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/security/pem-encoding.html |
| Security RandomGenerator interface Old // Hard-coded to one algorithm Random rng = new Random(); int value = rng.nextInt(100); // Or thread-local, but still locked in int value = ThreadLocalRandom.current() .nextInt(100); Modern // Algorithm-agnostic via factory var rng = RandomGenerator.of("L64X128MixRandom"); int value = rng.nextInt(100); // Or get a splittable generator var rng = RandomGeneratorFactory .of("L64X128MixRandom").create(); hover to see modern → JDK 17+ learn more → | https://javaevolved.github.io/security/random-generator.html |
| Security Strong random generation Old // Default algorithm — may not be // the strongest available SecureRandom random = new SecureRandom(); byte[] bytes = new byte[32]; random.nextBytes(bytes); Modern // Platform's strongest algorithm SecureRandom random = SecureRandom.getInstanceStrong(); byte[] bytes = new byte[32]; random.nextBytes(bytes); hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/security/strong-random.html |
| Security TLS 1.3 by default Old SSLContext ctx = SSLContext.getInstance("TLSv1.2"); ctx.init(null, trustManagers, null); SSLSocketFactory factory = ctx.getSocketFactory(); // Must specify protocol version Modern // TLS 1.3 is the default! var client = HttpClient.newBuilder() .sslContext(SSLContext.getDefault()) .build(); // Already using TLS 1.3 hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/security/tls-default.html |
| Tooling AOT class preloading Old // Every startup: // - Load 10,000+ classes // - Verify bytecode // - JIT compile hot paths // Startup: 2-5 seconds Modern // Training run: $ java -XX:AOTCacheOutput=app.aot \ -cp app.jar com.App // Production: $ java -XX:AOTCache=app.aot \ -cp app.jar com.App hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/tooling/aot-class-preloading.html |
| Tooling Built-in HTTP server Old // Install and configure a web server // (Apache, Nginx, or embedded Jetty) // Or write boilerplate with com.sun.net.httpserver HttpServer server = HttpServer.create( new InetSocketAddress(8080), 0); server.createContext("/", exchange -> { ... }); server.start(); Modern // Terminal: serve current directory $ jwebserver // Or use the API (JDK 18+) var server = SimpleFileServer.createFileServer( new InetSocketAddress(8080), Path.of("."), OutputLevel.VERBOSE); server.start(); hover to see modern → JDK 18+ learn more → | https://javaevolved.github.io/tooling/built-in-http-server.html |
| Tooling Compact object headers Old // Default: 128-bit object header // = 16 bytes overhead per object // A boolean field object = 32 bytes! // Mark word (64) + Klass pointer (64) Modern // -XX:+UseCompactObjectHeaders // 64-bit object header // = 8 bytes overhead per object // 50% less header memory // More objects fit in cache hover to see modern → JDK 25+ learn more → | https://javaevolved.github.io/tooling/compact-object-headers.html |
|
Tooling
JFR for profiling
Old
// Install VisualVM / YourKit / JProfiler
// Attach to running process
// Configure sampling
// Export and analyze
// External tool required
Modern
// Start with profiling enabled
$ java -XX:StartFlightRecording=
filename=rec.jfr MyApp
// Or attach to running app:
$ jcmd | https://javaevolved.github.io/tooling/jfr-profiling.html |
| Tooling JShell for prototyping Old // 1. Create Test.java // 2. javac Test.java // 3. java Test // Just to test one expression! Modern $ jshell jshell> "hello".chars().count() $1 ==> 5 jshell> List.of(1,2,3).reversed() $2 ==> [3, 2, 1] hover to see modern → JDK 9+ learn more → | https://javaevolved.github.io/tooling/jshell-prototyping.html |
| Tooling JUnit 6 with JSpecify null safety Old import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class UserServiceTest { // JUnit 5: no null contracts on the API // Can assertEquals() accept null? Check source... // Does fail(String) allow null message? Unknown. @Test void findUser_found() { // Is result nullable? API doesn't say User result = service.findById("u1"); assertNotNull(result); assertEquals("Alice", result.name()); } @Test void findUser_notFound() { // Hope this returns null, not throws... assertNull(service.findById("missing")); } } Modern import org.junit.jupiter.api.Test; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import static org.junit.jupiter.api.Assertions.*; @NullMarked // all refs non-null unless @Nullable class UserServiceTest { // JUnit 6 API is @NullMarked: // assertNull(@Nullable Object actual) // assertEquals(@Nullable Object, @Nullable Object) // fail(@Nullable String message) @Test void findUser_found() { // IDE warns: findById returns @Nullable User @Nullable User result = service.findById("u1"); assertNotNull(result); // narrows type to non-null assertEquals("Alice", result.name()); // safe } @Test void findUser_notFound() { @Nullable User result = service.findById("missing"); assertNull(result); // IDE confirms null expectation } } hover to see modern → JDK 17+ learn more → | https://javaevolved.github.io/tooling/junit6-with-jspecify.html |
| Tooling Multi-file source launcher Old $ javac *.java $ java Main // Must compile all files first // Need a build tool for dependencies Modern $ java Main.java // Automatically finds and compiles // other source files referenced // by Main.java hover to see modern → JDK 22+ learn more → | https://javaevolved.github.io/tooling/multi-file-source.html |
| Tooling Single-file execution Old $ javac HelloWorld.java $ java HelloWorld // Two steps every time Modern $ java HelloWorld.java // Compiles and runs in one step // Also works with shebangs: #!/usr/bin/java --source 25 hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/tooling/single-file-execution.html |
| Enterprise EJB Timer vs Jakarta Scheduler Old @Stateless public class ReportGenerator { @Resource TimerService timerService; @PostConstruct public void init() { timerService.createCalendarTimer( new ScheduleExpression() .hour("2").minute("0")); } @Timeout public void generateReport(Timer timer) { // runs every day at 02:00 buildDailyReport(); } } Modern @ApplicationScoped public class ReportGenerator { @Resource ManagedScheduledExecutorService scheduler; @PostConstruct public void init() { scheduler.scheduleAtFixedRate( this::generateReport, 0, 24, TimeUnit.HOURS); } public void generateReport() { buildDailyReport(); } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/ejb-timer-vs-jakarta-scheduler.html |
| Enterprise EJB versus CDI Old @Stateless public class OrderEJB { @EJB private InventoryEJB inventory; public void placeOrder(Order order) { // container-managed transaction inventory.reserve(order.getItem()); } } Modern @ApplicationScoped public class OrderService { @Inject private InventoryService inventory; @Transactional public void placeOrder(Order order) { inventory.reserve(order.getItem()); } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/ejb-vs-cdi.html |
|
Enterprise
JDBC ResultSet Mapping vs JPA Criteria API
Old
String sql = "SELECT * FROM users"
+ " WHERE status = ? AND age > ?";
try (Connection con = ds.getConnection();
PreparedStatement ps =
con.prepareStatement(sql)) {
ps.setString(1, status);
ps.setInt(2, minAge);
ResultSet rs = ps.executeQuery();
List | https://javaevolved.github.io/enterprise/jdbc-resultset-vs-jpa-criteria.html |
|
Enterprise
JDBC versus jOOQ
Old
String sql = "SELECT id, name, email FROM users "
+ "WHERE department = ? AND salary > ?";
try (Connection con = ds.getConnection();
PreparedStatement ps =
con.prepareStatement(sql)) {
ps.setString(1, department);
ps.setBigDecimal(2, minSalary);
ResultSet rs = ps.executeQuery();
List | https://javaevolved.github.io/enterprise/jdbc-vs-jooq.html |
|
Enterprise
JDBC versus JPA
Old
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection con = dataSource.getConnection();
PreparedStatement ps =
con.prepareStatement(sql)) {
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
User u = new User();
u.setId(rs.getLong("id"));
u.setName(rs.getString("name"));
}
}
Modern
@PersistenceContext
EntityManager em;
public User findUser(Long id) {
return em.find(User.class, id);
}
public List | https://javaevolved.github.io/enterprise/jdbc-vs-jpa.html |
|
Enterprise
JNDI Lookup vs CDI Injection
Old
public class OrderService {
private DataSource ds;
public void init() throws NamingException {
InitialContext ctx = new InitialContext();
ds = (DataSource) ctx.lookup(
"java:comp/env/jdbc/OrderDB");
}
public List | https://javaevolved.github.io/enterprise/jndi-lookup-vs-cdi-injection.html |
|
Enterprise
JPA versus Jakarta Data
Old
@PersistenceContext
EntityManager em;
public User findById(Long id) {
return em.find(User.class, id);
}
public List | https://javaevolved.github.io/enterprise/jpa-vs-jakarta-data.html |
| Enterprise JSF Managed Bean vs CDI Named Bean Old @ManagedBean @SessionScoped public class UserBean implements Serializable { @ManagedProperty("#{userService}") private UserService userService; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void setUserService(UserService svc) { this.userService = svc; } } Modern @Named @SessionScoped public class UserBean implements Serializable { @Inject private UserService userService; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/jsf-managed-bean-vs-cdi-named.html |
| Enterprise Manual JPA Transaction vs Declarative @Transactional Old @PersistenceContext EntityManager em; public void transferFunds(Long from, Long to, BigDecimal amount) { EntityTransaction tx = em.getTransaction(); tx.begin(); try { Account src = em.find(Account.class, from); Account dst = em.find(Account.class, to); src.debit(amount); dst.credit(amount); tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } } Modern @ApplicationScoped public class AccountService { @PersistenceContext EntityManager em; @Transactional public void transferFunds(Long from, Long to, BigDecimal amount) { var src = em.find(Account.class, from); var dst = em.find(Account.class, to); src.debit(amount); dst.credit(amount); } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/manual-transaction-vs-declarative.html |
| Enterprise Message-Driven Bean vs Reactive Messaging Old @MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "jakarta.jms.Queue"), @ActivationConfigProperty( propertyName = "destination", propertyValue = "java:/jms/OrderQueue") }) public class OrderMDB implements MessageListener { @Override public void onMessage(Message message) { TextMessage txt = (TextMessage) message; processOrder(txt.getText()); } } Modern @ApplicationScoped public class OrderProcessor { @Incoming("orders") public void process(Order order) { // automatically deserialized from // the "orders" channel fulfillOrder(order); } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/mdb-vs-reactive-messaging.html |
| Enterprise Servlet versus JAX-RS Old @WebServlet("/users") public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String id = req.getParameter("id"); res.setContentType("application/json"); res.getWriter().write("{\"id\":\"" + id + "\"}"); } } Modern @Path("/users") public class UserResource { @GET @Produces(MediaType.APPLICATION_JSON) public Response getUser( @QueryParam("id") String id) { return Response.ok(new User(id)).build(); } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/servlet-vs-jaxrs.html |
|
Enterprise
Singleton EJB vs CDI @ApplicationScoped
Old
@Singleton
@Startup
@ConcurrencyManagement(
ConcurrencyManagementType.CONTAINER)
public class ConfigCache {
private Map | https://javaevolved.github.io/enterprise/singleton-ejb-vs-cdi-application-scoped.html |
| Enterprise SOAP Web Services vs Jakarta REST Old @WebService public class UserWebService { @WebMethod public UserResponse getUser( @WebParam(name = "id") String id) { User user = findUser(id); UserResponse res = new UserResponse(); res.setId(user.getId()); res.setName(user.getName()); return res; } } Modern @Path("/users") @Produces(MediaType.APPLICATION_JSON) public class UserResource { @Inject UserService userService; @GET @Path("/{id}") public User getUser(@PathParam("id") String id) { return userService.findById(id); } } hover to see modern → JDK 11+ learn more → | https://javaevolved.github.io/enterprise/soap-vs-jakarta-rest.html |
| Enterprise Spring Framework 7 API Versioning Old // Version 1 controller @RestController @RequestMapping("/api/v1/products") public class ProductControllerV1 { @GetMapping("/{id}") public ProductDtoV1 getProduct( @PathVariable Long id) { return service.getV1(id); } } // Version 2 — duplicated structure @RestController @RequestMapping("/api/v2/products") public class ProductControllerV2 { @GetMapping("/{id}") public ProductDtoV2 getProduct( @PathVariable Long id) { return service.getV2(id); } } Modern // Configure versioning once @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureApiVersioning( ApiVersionConfigurer config) { config.useRequestHeader("X-API-Version"); } } // Single controller, version per method @RestController @RequestMapping("/api/products") public class ProductController { @GetMapping(value = "/{id}", version = "1") public ProductDtoV1 getV1(@PathVariable Long id) { return service.getV1(id); } @GetMapping(value = "/{id}", version = "2") public ProductDtoV2 getV2(@PathVariable Long id) { return service.getV2(id); } } hover to see modern → JDK 17+ learn more → | https://javaevolved.github.io/enterprise/spring-api-versioning.html |
|
Enterprise
Spring Null Safety with JSpecify
Old
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
public class UserService {
@Nullable
public User findById(@NonNull String id) {
return repository.findById(id).orElse(null);
}
@NonNull
public List | https://javaevolved.github.io/enterprise/spring-null-safety-jspecify.html |
|
Enterprise
Spring XML Bean Config vs Annotation-Driven
Old
| https://javaevolved.github.io/enterprise/spring-xml-config-vs-annotations.html |
| java.evolved | https://javaevolved.github.io/ |
| Bruno Borges | https://github.com/brunoborges |
| GitHub Copilot | https://github.com/features/copilot |
| modern-css.com | https://modern-css.com |
| View on GitHub | https://github.com/javaevolved/javaevolved.github.io |
Viewport: width=device-width, initial-scale=1.0
Robots: index, follow