From 08035539f87de8bbd138ad0cfe5ef4ce12cf07f7 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 25 Feb 2026 18:06:30 -0500 Subject: [PATCH 1/3] Implement i18n with pt-BR localization - Add two-layer i18n: UI strings (translations/strings/) and content translations (translations/content/) - Extract all hard-coded English from templates into en.json - Add complete pt-BR UI string translations - Add 3 pt-BR content translations as proof of concept - Extend generate.java to iterate locales, merge strings with fallback, generate locale-specific output under site/{locale}/ - Add hreflang alternate links, locale picker dropdown, window.i18n script injection - Make app.js locale-aware: path detection, correct snippets.json loading, locale picker behavior, i18n string usage - Add locale picker and untranslated-notice CSS - Use hash links (#category) for breadcrumb category filtering - Scroll to filter section when index loads with hash Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- html-generators/generate.java | 339 +- html-generators/locales.properties | 3 + site/app.js | 87 +- site/pt-BR/collections/collectors-teeing.html | 368 ++ .../copying-collections-immutably.html | 362 ++ .../collections/immutable-list-creation.html | 367 ++ .../collections/immutable-map-creation.html | 363 ++ .../collections/immutable-set-creation.html | 368 ++ site/pt-BR/collections/map-entry-factory.html | 363 ++ .../collections/reverse-list-iteration.html | 366 ++ .../collections/sequenced-collections.html | 371 ++ .../collections/stream-toarray-typed.html | 368 ++ .../collections/unmodifiable-collectors.html | 364 ++ .../completablefuture-chaining.html | 388 ++ .../concurrency/concurrent-http-virtual.html | 404 ++ .../executor-try-with-resources.html | 377 ++ .../concurrency/lock-free-lazy-init.html | 393 ++ site/pt-BR/concurrency/process-api.html | 388 ++ site/pt-BR/concurrency/scoped-values.html | 388 ++ site/pt-BR/concurrency/stable-values.html | 388 ++ .../concurrency/structured-concurrency.html | 388 ++ .../concurrency/thread-sleep-duration.html | 388 ++ site/pt-BR/concurrency/virtual-threads.html | 378 ++ site/pt-BR/data/snippets.json | 4183 ++++++++++++++++ site/pt-BR/datetime/date-formatting.html | 376 ++ site/pt-BR/datetime/duration-and-period.html | 376 ++ site/pt-BR/datetime/hex-format.html | 378 ++ site/pt-BR/datetime/instant-precision.html | 378 ++ site/pt-BR/datetime/java-time-basics.html | 378 ++ site/pt-BR/datetime/math-clamp.html | 374 ++ .../ejb-timer-vs-jakarta-scheduler.html | 412 ++ site/pt-BR/enterprise/ejb-vs-cdi.html | 410 ++ .../jdbc-resultset-vs-jpa-criteria.html | 447 ++ site/pt-BR/enterprise/jdbc-vs-jooq.html | 450 ++ site/pt-BR/enterprise/jdbc-vs-jpa.html | 412 ++ .../jndi-lookup-vs-cdi-injection.html | 450 ++ .../pt-BR/enterprise/jpa-vs-jakarta-data.html | 424 ++ .../jsf-managed-bean-vs-cdi-named.html | 435 ++ .../manual-transaction-vs-declarative.html | 436 ++ .../enterprise/mdb-vs-reactive-messaging.html | 402 ++ site/pt-BR/enterprise/servlet-vs-jaxrs.html | 416 ++ ...ngleton-ejb-vs-cdi-application-scoped.html | 445 ++ .../enterprise/soap-vs-jakarta-rest.html | 414 ++ .../enterprise/spring-api-versioning.html | 445 ++ .../spring-null-safety-jspecify.html | 399 ++ .../spring-xml-config-vs-annotations.html | 458 ++ site/pt-BR/errors/helpful-npe.html | 383 ++ site/pt-BR/errors/multi-catch.html | 383 ++ site/pt-BR/errors/null-in-switch.html | 391 ++ site/pt-BR/errors/optional-chaining.html | 380 ++ site/pt-BR/errors/optional-orelsethrow.html | 373 ++ site/pt-BR/errors/record-based-errors.html | 385 ++ site/pt-BR/errors/require-nonnull-else.html | 381 ++ site/pt-BR/index.html | 4309 +++++++++++++++++ site/pt-BR/io/deserialization-filters.html | 372 ++ site/pt-BR/io/file-memory-mapping.html | 393 ++ site/pt-BR/io/files-mismatch.html | 371 ++ site/pt-BR/io/http-client.html | 377 ++ site/pt-BR/io/inputstream-transferto.html | 373 ++ site/pt-BR/io/io-class-console-io.html | 375 ++ site/pt-BR/io/path-of.html | 366 ++ site/pt-BR/io/reading-files.html | 375 ++ .../try-with-resources-effectively-final.html | 374 ++ site/pt-BR/io/writing-files.html | 374 ++ .../compact-canonical-constructor.html | 392 ++ site/pt-BR/language/compact-source-files.html | 379 ++ .../language/default-interface-methods.html | 397 ++ site/pt-BR/language/diamond-operator.html | 384 ++ site/pt-BR/language/exhaustive-switch.html | 382 ++ .../language/flexible-constructor-bodies.html | 385 ++ site/pt-BR/language/guarded-patterns.html | 380 ++ .../language/markdown-javadoc-comments.html | 386 ++ .../language/module-import-declarations.html | 380 ++ .../language/pattern-matching-instanceof.html | 370 ++ .../language/pattern-matching-switch.html | 383 ++ .../language/primitive-types-in-patterns.html | 387 ++ .../language/private-interface-methods.html | 390 ++ site/pt-BR/language/record-patterns.html | 382 ++ .../language/records-for-data-classes.html | 397 ++ site/pt-BR/language/sealed-classes.html | 389 ++ .../static-members-in-inner-classes.html | 402 ++ .../static-methods-in-interfaces.html | 409 ++ site/pt-BR/language/switch-expressions.html | 380 ++ .../text-blocks-for-multiline-strings.html | 386 ++ .../language/type-inference-with-var.html | 382 ++ site/pt-BR/language/unnamed-variables.html | 386 ++ .../security/key-derivation-functions.html | 385 ++ site/pt-BR/security/pem-encoding.html | 385 ++ site/pt-BR/security/random-generator.html | 386 ++ site/pt-BR/security/strong-random.html | 385 ++ site/pt-BR/security/tls-default.html | 386 ++ .../pt-BR/streams/collectors-flatmapping.html | 372 ++ .../streams/optional-ifpresentorelse.html | 374 ++ site/pt-BR/streams/optional-or.html | 371 ++ site/pt-BR/streams/predicate-not.html | 362 ++ site/pt-BR/streams/stream-gatherers.html | 372 ++ .../streams/stream-iterate-predicate.html | 380 ++ site/pt-BR/streams/stream-mapmulti.html | 374 ++ site/pt-BR/streams/stream-of-nullable.html | 371 ++ .../streams/stream-takewhile-dropwhile.html | 375 ++ site/pt-BR/streams/stream-tolist.html | 369 ++ .../streams/virtual-thread-executor.html | 382 ++ site/pt-BR/strings/string-chars-stream.html | 364 ++ site/pt-BR/strings/string-formatted.html | 368 ++ .../strings/string-indent-transform.html | 367 ++ site/pt-BR/strings/string-isblank.html | 366 ++ site/pt-BR/strings/string-lines.html | 366 ++ site/pt-BR/strings/string-repeat.html | 363 ++ site/pt-BR/strings/string-strip.html | 370 ++ site/pt-BR/tooling/aot-class-preloading.html | 371 ++ site/pt-BR/tooling/built-in-http-server.html | 384 ++ .../pt-BR/tooling/compact-object-headers.html | 368 ++ site/pt-BR/tooling/jfr-profiling.html | 371 ++ site/pt-BR/tooling/jshell-prototyping.html | 373 ++ site/pt-BR/tooling/junit6-with-jspecify.html | 442 ++ site/pt-BR/tooling/multi-file-source.html | 372 ++ site/pt-BR/tooling/single-file-execution.html | 370 ++ site/styles.css | 81 + templates/index-card.html | 8 +- templates/index.html | 87 +- templates/related-card.html | 4 +- templates/slug-template.html | 61 +- templates/social-share.html | 2 +- .../collections/immutable-list-creation.json | 54 + .../language/records-for-data-classes.json | 54 + .../language/type-inference-with-var.json | 54 + translations/strings/en.json | 94 + translations/strings/pt-BR.json | 93 + 128 files changed, 52618 insertions(+), 128 deletions(-) create mode 100644 html-generators/locales.properties create mode 100644 site/pt-BR/collections/collectors-teeing.html create mode 100644 site/pt-BR/collections/copying-collections-immutably.html create mode 100644 site/pt-BR/collections/immutable-list-creation.html create mode 100644 site/pt-BR/collections/immutable-map-creation.html create mode 100644 site/pt-BR/collections/immutable-set-creation.html create mode 100644 site/pt-BR/collections/map-entry-factory.html create mode 100644 site/pt-BR/collections/reverse-list-iteration.html create mode 100644 site/pt-BR/collections/sequenced-collections.html create mode 100644 site/pt-BR/collections/stream-toarray-typed.html create mode 100644 site/pt-BR/collections/unmodifiable-collectors.html create mode 100644 site/pt-BR/concurrency/completablefuture-chaining.html create mode 100644 site/pt-BR/concurrency/concurrent-http-virtual.html create mode 100644 site/pt-BR/concurrency/executor-try-with-resources.html create mode 100644 site/pt-BR/concurrency/lock-free-lazy-init.html create mode 100644 site/pt-BR/concurrency/process-api.html create mode 100644 site/pt-BR/concurrency/scoped-values.html create mode 100644 site/pt-BR/concurrency/stable-values.html create mode 100644 site/pt-BR/concurrency/structured-concurrency.html create mode 100644 site/pt-BR/concurrency/thread-sleep-duration.html create mode 100644 site/pt-BR/concurrency/virtual-threads.html create mode 100644 site/pt-BR/data/snippets.json create mode 100644 site/pt-BR/datetime/date-formatting.html create mode 100644 site/pt-BR/datetime/duration-and-period.html create mode 100644 site/pt-BR/datetime/hex-format.html create mode 100644 site/pt-BR/datetime/instant-precision.html create mode 100644 site/pt-BR/datetime/java-time-basics.html create mode 100644 site/pt-BR/datetime/math-clamp.html create mode 100644 site/pt-BR/enterprise/ejb-timer-vs-jakarta-scheduler.html create mode 100644 site/pt-BR/enterprise/ejb-vs-cdi.html create mode 100644 site/pt-BR/enterprise/jdbc-resultset-vs-jpa-criteria.html create mode 100644 site/pt-BR/enterprise/jdbc-vs-jooq.html create mode 100644 site/pt-BR/enterprise/jdbc-vs-jpa.html create mode 100644 site/pt-BR/enterprise/jndi-lookup-vs-cdi-injection.html create mode 100644 site/pt-BR/enterprise/jpa-vs-jakarta-data.html create mode 100644 site/pt-BR/enterprise/jsf-managed-bean-vs-cdi-named.html create mode 100644 site/pt-BR/enterprise/manual-transaction-vs-declarative.html create mode 100644 site/pt-BR/enterprise/mdb-vs-reactive-messaging.html create mode 100644 site/pt-BR/enterprise/servlet-vs-jaxrs.html create mode 100644 site/pt-BR/enterprise/singleton-ejb-vs-cdi-application-scoped.html create mode 100644 site/pt-BR/enterprise/soap-vs-jakarta-rest.html create mode 100644 site/pt-BR/enterprise/spring-api-versioning.html create mode 100644 site/pt-BR/enterprise/spring-null-safety-jspecify.html create mode 100644 site/pt-BR/enterprise/spring-xml-config-vs-annotations.html create mode 100644 site/pt-BR/errors/helpful-npe.html create mode 100644 site/pt-BR/errors/multi-catch.html create mode 100644 site/pt-BR/errors/null-in-switch.html create mode 100644 site/pt-BR/errors/optional-chaining.html create mode 100644 site/pt-BR/errors/optional-orelsethrow.html create mode 100644 site/pt-BR/errors/record-based-errors.html create mode 100644 site/pt-BR/errors/require-nonnull-else.html create mode 100644 site/pt-BR/index.html create mode 100644 site/pt-BR/io/deserialization-filters.html create mode 100644 site/pt-BR/io/file-memory-mapping.html create mode 100644 site/pt-BR/io/files-mismatch.html create mode 100644 site/pt-BR/io/http-client.html create mode 100644 site/pt-BR/io/inputstream-transferto.html create mode 100644 site/pt-BR/io/io-class-console-io.html create mode 100644 site/pt-BR/io/path-of.html create mode 100644 site/pt-BR/io/reading-files.html create mode 100644 site/pt-BR/io/try-with-resources-effectively-final.html create mode 100644 site/pt-BR/io/writing-files.html create mode 100644 site/pt-BR/language/compact-canonical-constructor.html create mode 100644 site/pt-BR/language/compact-source-files.html create mode 100644 site/pt-BR/language/default-interface-methods.html create mode 100644 site/pt-BR/language/diamond-operator.html create mode 100644 site/pt-BR/language/exhaustive-switch.html create mode 100644 site/pt-BR/language/flexible-constructor-bodies.html create mode 100644 site/pt-BR/language/guarded-patterns.html create mode 100644 site/pt-BR/language/markdown-javadoc-comments.html create mode 100644 site/pt-BR/language/module-import-declarations.html create mode 100644 site/pt-BR/language/pattern-matching-instanceof.html create mode 100644 site/pt-BR/language/pattern-matching-switch.html create mode 100644 site/pt-BR/language/primitive-types-in-patterns.html create mode 100644 site/pt-BR/language/private-interface-methods.html create mode 100644 site/pt-BR/language/record-patterns.html create mode 100644 site/pt-BR/language/records-for-data-classes.html create mode 100644 site/pt-BR/language/sealed-classes.html create mode 100644 site/pt-BR/language/static-members-in-inner-classes.html create mode 100644 site/pt-BR/language/static-methods-in-interfaces.html create mode 100644 site/pt-BR/language/switch-expressions.html create mode 100644 site/pt-BR/language/text-blocks-for-multiline-strings.html create mode 100644 site/pt-BR/language/type-inference-with-var.html create mode 100644 site/pt-BR/language/unnamed-variables.html create mode 100644 site/pt-BR/security/key-derivation-functions.html create mode 100644 site/pt-BR/security/pem-encoding.html create mode 100644 site/pt-BR/security/random-generator.html create mode 100644 site/pt-BR/security/strong-random.html create mode 100644 site/pt-BR/security/tls-default.html create mode 100644 site/pt-BR/streams/collectors-flatmapping.html create mode 100644 site/pt-BR/streams/optional-ifpresentorelse.html create mode 100644 site/pt-BR/streams/optional-or.html create mode 100644 site/pt-BR/streams/predicate-not.html create mode 100644 site/pt-BR/streams/stream-gatherers.html create mode 100644 site/pt-BR/streams/stream-iterate-predicate.html create mode 100644 site/pt-BR/streams/stream-mapmulti.html create mode 100644 site/pt-BR/streams/stream-of-nullable.html create mode 100644 site/pt-BR/streams/stream-takewhile-dropwhile.html create mode 100644 site/pt-BR/streams/stream-tolist.html create mode 100644 site/pt-BR/streams/virtual-thread-executor.html create mode 100644 site/pt-BR/strings/string-chars-stream.html create mode 100644 site/pt-BR/strings/string-formatted.html create mode 100644 site/pt-BR/strings/string-indent-transform.html create mode 100644 site/pt-BR/strings/string-isblank.html create mode 100644 site/pt-BR/strings/string-lines.html create mode 100644 site/pt-BR/strings/string-repeat.html create mode 100644 site/pt-BR/strings/string-strip.html create mode 100644 site/pt-BR/tooling/aot-class-preloading.html create mode 100644 site/pt-BR/tooling/built-in-http-server.html create mode 100644 site/pt-BR/tooling/compact-object-headers.html create mode 100644 site/pt-BR/tooling/jfr-profiling.html create mode 100644 site/pt-BR/tooling/jshell-prototyping.html create mode 100644 site/pt-BR/tooling/junit6-with-jspecify.html create mode 100644 site/pt-BR/tooling/multi-file-source.html create mode 100644 site/pt-BR/tooling/single-file-execution.html create mode 100644 translations/content/pt-BR/collections/immutable-list-creation.json create mode 100644 translations/content/pt-BR/language/records-for-data-classes.json create mode 100644 translations/content/pt-BR/language/type-inference-with-var.json create mode 100644 translations/strings/en.json create mode 100644 translations/strings/pt-BR.json diff --git a/html-generators/generate.java b/html-generators/generate.java index f927f1a..e46f84d 100644 --- a/html-generators/generate.java +++ b/html-generators/generate.java @@ -13,11 +13,14 @@ static final String BASE_URL = "https://javaevolved.github.io"; static final String CONTENT_DIR = "content"; static final String SITE_DIR = "site"; -static final Pattern TOKEN = Pattern.compile("\\{\\{(\\w+)}}"); +static final String TRANSLATIONS_DIR = "translations"; +static final Pattern TOKEN = Pattern.compile("\\{\\{([\\w.]+)}}"); static final ObjectMapper MAPPER = new ObjectMapper(); static final String CATEGORIES_FILE = "html-generators/categories.properties"; +static final String LOCALES_FILE = "html-generators/locales.properties"; static final SequencedMap CATEGORY_DISPLAY = loadCategoryDisplay(); +static final SequencedMap LOCALES = loadLocales(); static SequencedMap loadCategoryDisplay() { try { @@ -34,6 +37,66 @@ static SequencedMap loadCategoryDisplay() { } } +static SequencedMap loadLocales() { + try { + var map = new LinkedHashMap(); + for (var line : Files.readAllLines(Path.of(LOCALES_FILE))) { + line = line.strip(); + if (line.isEmpty() || line.startsWith("#")) continue; + var idx = line.indexOf('='); + if (idx > 0) map.put(line.substring(0, idx).strip(), line.substring(idx + 1).strip()); + } + return map; + } catch (IOException e) { + throw new UncheckedIOException(e); + } +} + +/** Flatten nested JSON into dot-separated keys: {"a":{"b":"c"}} → {"a.b":"c"} */ +static Map flattenJson(JsonNode node, String prefix) { + var map = new LinkedHashMap(); + var it = node.fields(); + while (it.hasNext()) { + var entry = it.next(); + var key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey(); + if (entry.getValue().isObject()) { + map.putAll(flattenJson(entry.getValue(), key)); + } else { + map.put(key, entry.getValue().asText()); + } + } + return map; +} + +/** Load UI strings for a locale, falling back to en.json for missing keys */ +static Map loadStrings(String locale) throws IOException { + var enPath = Path.of(TRANSLATIONS_DIR, "strings", "en.json"); + var enStrings = flattenJson(MAPPER.readTree(enPath.toFile()), ""); + + if (locale.equals("en")) return enStrings; + + var localePath = Path.of(TRANSLATIONS_DIR, "strings", locale + ".json"); + if (!Files.exists(localePath)) { + IO.println("[WARN] strings/%s.json not found — using all English strings".formatted(locale)); + return enStrings; + } + + var localeStrings = flattenJson(MAPPER.readTree(localePath.toFile()), ""); + var merged = new LinkedHashMap<>(enStrings); + for (var entry : localeStrings.entrySet()) { + if (enStrings.containsKey(entry.getKey())) { + merged.put(entry.getKey(), entry.getValue()); + } + } + // Warn about missing keys + for (var key : enStrings.keySet()) { + if (!localeStrings.containsKey(key)) { + IO.println("[WARN] strings/%s.json: missing key \"%s\" — using English fallback".formatted(locale, key)); + } + } + return merged; +} + static final Set EXCLUDED_KEYS = Set.of("_path", "prev", "next", "related"); record Snippet(JsonNode node) { @@ -85,41 +148,110 @@ static Templates load() throws IOException { } } -void main() throws IOException { +void main(String... args) throws IOException { var templates = Templates.load(); var allSnippets = loadAllSnippets(); IO.println("Loaded %d snippets".formatted(allSnippets.size())); + // Determine which locales to build + List localesToBuild; + if (args.length > 0 && args[0].equals("--all-locales")) { + localesToBuild = new ArrayList<>(LOCALES.sequencedKeySet()); + } else if (args.length > 1 && args[0].equals("--locale")) { + localesToBuild = List.of(args[1]); + } else { + localesToBuild = new ArrayList<>(LOCALES.sequencedKeySet()); + } + + for (var locale : localesToBuild) { + buildLocale(locale, templates, allSnippets); + } +} + +void buildLocale(String locale, Templates templates, SequencedMap allSnippets) throws IOException { + var isEnglish = locale.equals("en"); + var strings = loadStrings(locale); + var localeName = LOCALES.getOrDefault(locale, locale); + var sitePrefix = isEnglish ? "" : locale + "/"; + // basePrefix is the relative path from a detail page back to site root + var basePrefix = isEnglish ? "../" : "../../"; + var homeUrl = isEnglish ? "/" : "/%s/".formatted(locale); + + IO.println("Building locale: %s (%s)".formatted(locale, localeName)); + + // Build locale picker HTML + var localePickerHtml = renderLocalePicker(locale); + // Build hreflang links for index + var indexHreflang = renderHreflangLinks("", "index"); + // Build i18n script block + var i18nScript = renderI18nScript(strings, locale); + + // Load translated content if available for (var snippet : allSnippets.values()) { - var html = generateHtml(templates, snippet, allSnippets).strip(); - Files.createDirectories(Path.of(SITE_DIR, snippet.category())); - Files.writeString(Path.of(SITE_DIR, snippet.category(), snippet.slug() + ".html"), html); + var resolved = resolveSnippet(snippet, locale); + var detailHreflang = renderHreflangLinks(snippet.category() + "/", snippet.slug()); + + var extraTokens = new LinkedHashMap(); + extraTokens.putAll(strings); + extraTokens.put("locale", locale); + extraTokens.put("ogLocale", locale.replace("-", "_")); + extraTokens.put("basePrefix", basePrefix); + extraTokens.put("homeUrl", homeUrl); + extraTokens.put("localePicker", localePickerHtml); + extraTokens.put("hreflangLinks", detailHreflang); + extraTokens.put("i18nScript", i18nScript); + + var html = generateHtml(templates, resolved, allSnippets, extraTokens, locale).strip(); + + if (isEnglish) { + Files.createDirectories(Path.of(SITE_DIR, snippet.category())); + Files.writeString(Path.of(SITE_DIR, snippet.category(), snippet.slug() + ".html"), html); + } else { + Files.createDirectories(Path.of(SITE_DIR, locale, snippet.category())); + Files.writeString(Path.of(SITE_DIR, locale, snippet.category(), snippet.slug() + ".html"), html); + } } - IO.println("Generated %d HTML files".formatted(allSnippets.size())); + IO.println("Generated %d HTML files for %s".formatted(allSnippets.size(), locale)); // Rebuild data/snippets.json var snippetsList = allSnippets.values().stream() .map(s -> { - Map map = MAPPER.convertValue(s.node(), new TypeReference>() {}); + var resolved = resolveSnippet(s, locale); + Map map = MAPPER.convertValue(resolved.node(), new TypeReference>() {}); EXCLUDED_KEYS.forEach(map::remove); return map; }) .toList(); - Files.createDirectories(Path.of(SITE_DIR, "data")); + var dataDir = isEnglish ? Path.of(SITE_DIR, "data") : Path.of(SITE_DIR, locale, "data"); + Files.createDirectories(dataDir); var prettyMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - Files.writeString(Path.of(SITE_DIR, "data", "snippets.json"), prettyMapper.writeValueAsString(snippetsList) + "\n"); - IO.println("Rebuilt data/snippets.json with %d entries".formatted(snippetsList.size())); + Files.writeString(dataDir.resolve("snippets.json"), prettyMapper.writeValueAsString(snippetsList) + "\n"); + IO.println("Rebuilt data/snippets.json for %s with %d entries".formatted(locale, snippetsList.size())); // Generate index.html from template var tipCards = allSnippets.values().stream() - .map(s -> renderIndexCard(templates.indexCard(), s)) + .map(s -> renderIndexCard(templates.indexCard(), s, locale, strings)) .collect(Collectors.joining("\n")); - var indexHtml = replaceTokens(templates.index(), Map.of( - "tipCards", tipCards, - "snippetCount", String.valueOf(allSnippets.size()))); - Files.writeString(Path.of(SITE_DIR, "index.html"), indexHtml); - IO.println("Generated index.html with %d cards".formatted(allSnippets.size())); + + var indexTokens = new LinkedHashMap(); + indexTokens.putAll(strings); + indexTokens.put("tipCards", tipCards); + indexTokens.put("snippetCount", String.valueOf(allSnippets.size())); + indexTokens.put("locale", locale); + indexTokens.put("ogLocale", locale.replace("-", "_")); + indexTokens.put("canonicalUrl", isEnglish ? BASE_URL : BASE_URL + "/" + locale); + indexTokens.put("homeUrl", homeUrl); + indexTokens.put("indexBasePrefix", isEnglish ? "" : "../"); + indexTokens.put("localePicker", localePickerHtml); + indexTokens.put("hreflangLinks", indexHreflang); + indexTokens.put("i18nScript", i18nScript); + + var indexHtml = replaceTokens(templates.index(), indexTokens); + var indexPath = isEnglish ? Path.of(SITE_DIR, "index.html") : Path.of(SITE_DIR, locale, "index.html"); + if (!isEnglish) Files.createDirectories(indexPath.getParent()); + Files.writeString(indexPath, indexHtml); + IO.println("Generated index.html for %s with %d cards".formatted(locale, allSnippets.size())); } SequencedMap loadAllSnippets() throws IOException { @@ -159,11 +291,11 @@ String urlEncode(String s) { return URLEncoder.encode(s, StandardCharsets.UTF_8).replace("+", "%20"); } -String supportBadge(String state) { +String supportBadge(String state, Map strings) { return switch (state) { - case "preview" -> "Preview"; - case "experimental" -> "Experimental"; - default -> "Available"; + case "preview" -> strings.getOrDefault("support.preview", "Preview"); + case "experimental" -> strings.getOrDefault("support.experimental", "Experimental"); + default -> strings.getOrDefault("support.available", "Available"); }; } @@ -175,22 +307,29 @@ String supportBadgeClass(String state) { }; } -String renderNavArrows(Snippet snippet) { +String renderNavArrows(Snippet snippet, String locale) { + var prefix = locale.equals("en") ? "" : "/" + locale; var prev = snippet.optText("prev") - .map(p -> "".formatted(p)) + .map(p -> "".formatted(prefix, p)) .orElse(""); var next = snippet.optText("next") - .map(n -> "".formatted(n)) + .map(n -> "".formatted(prefix, n)) .orElse(""); return prev + "\n " + next; } -String renderIndexCard(String tpl, Snippet s) { +String renderIndexCard(String tpl, Snippet s, String locale, Map strings) { + var cardHref = locale.equals("en") + ? "/%s/%s.html".formatted(s.category(), s.slug()) + : "/%s/%s/%s.html".formatted(locale, s.category(), s.slug()); return replaceTokens(tpl, Map.ofEntries( Map.entry("category", s.category()), Map.entry("slug", s.slug()), Map.entry("catDisplay", s.catDisplay()), Map.entry("title", escape(s.title())), Map.entry("oldCode", escape(s.oldCode())), Map.entry("modernCode", escape(s.modernCode())), - Map.entry("jdkVersion", s.jdkVersion()))); + Map.entry("jdkVersion", s.jdkVersion()), Map.entry("cardHref", cardHref), + Map.entry("cards.old", strings.getOrDefault("cards.old", "Old")), + Map.entry("cards.modern", strings.getOrDefault("cards.modern", "Modern")), + Map.entry("cards.hoverHint", strings.getOrDefault("cards.hoverHint", "hover to see modern →")))); } String renderWhyCards(String tpl, JsonNode whyList) { @@ -203,14 +342,18 @@ String renderWhyCards(String tpl, JsonNode whyList) { return String.join("\n", cards); } -String renderRelatedCard(String tpl, Snippet rel) { +String renderRelatedCard(String tpl, Snippet rel, String locale, Map strings) { + var relatedHref = locale.equals("en") + ? "/%s/%s.html".formatted(rel.category(), rel.slug()) + : "/%s/%s/%s.html".formatted(locale, rel.category(), rel.slug()); return replaceTokens(tpl, Map.ofEntries( Map.entry("category", rel.category()), Map.entry("slug", rel.slug()), Map.entry("catDisplay", rel.catDisplay()), Map.entry("difficulty", rel.difficulty()), Map.entry("title", escape(rel.title())), Map.entry("oldLabel", escape(rel.oldLabel())), Map.entry("oldCode", escape(rel.oldCode())), Map.entry("modernLabel", escape(rel.modernLabel())), Map.entry("modernCode", escape(rel.modernCode())), - Map.entry("jdkVersion", rel.jdkVersion()))); + Map.entry("jdkVersion", rel.jdkVersion()), Map.entry("relatedHref", relatedHref), + Map.entry("cards.hoverHintRelated", strings.getOrDefault("cards.hoverHintRelated", "Hover to see modern ➜")))); } String renderDocLinks(String tpl, JsonNode docs) { @@ -222,20 +365,27 @@ String renderDocLinks(String tpl, JsonNode docs) { return String.join("\n", links); } -String renderRelatedSection(String tpl, Snippet snippet, Map all) { +String renderRelatedSection(String tpl, Snippet snippet, Map all, String locale, Map strings) { return snippet.related().stream().filter(all::containsKey) - .map(p -> renderRelatedCard(tpl, all.get(p))) + .map(p -> renderRelatedCard(tpl, all.get(p), locale, strings)) .collect(Collectors.joining("\n")); } -String renderSocialShare(String tpl, String slug, String title) { +String renderSocialShare(String tpl, String slug, String title, Map strings) { var encodedUrl = urlEncode("%s/%s.html".formatted(BASE_URL, slug)); var encodedText = urlEncode("%s \u2013 java.evolved".formatted(title)); - return replaceTokens(tpl, Map.of("encodedUrl", encodedUrl, "encodedText", encodedText)); + return replaceTokens(tpl, Map.of("encodedUrl", encodedUrl, "encodedText", encodedText, + "share.label", strings.getOrDefault("share.label", "Share"))); } -String generateHtml(Templates tpl, Snippet s, Map all) throws IOException { - return replaceTokens(tpl.page(), Map.ofEntries( +String generateHtml(Templates tpl, Snippet s, Map all, Map extraTokens, String locale) throws IOException { + var isEnglish = locale.equals("en"); + var canonicalUrl = isEnglish + ? "%s/%s/%s.html".formatted(BASE_URL, s.category(), s.slug()) + : "%s/%s/%s/%s.html".formatted(BASE_URL, locale, s.category(), s.slug()); + + var tokens = new LinkedHashMap<>(extraTokens); + tokens.putAll(Map.ofEntries( Map.entry("title", escape(s.title())), Map.entry("summary", escape(s.summary())), Map.entry("slug", s.slug()), Map.entry("category", s.category()), Map.entry("categoryDisplay", s.catDisplay()), Map.entry("difficulty", s.difficulty()), @@ -245,23 +395,128 @@ String generateHtml(Templates tpl, Snippet s, Map all) throws I Map.entry("oldApproach", escape(s.oldApproach())), Map.entry("modernApproach", escape(s.modernApproach())), Map.entry("explanation", escape(s.explanation())), Map.entry("supportDescription", escape(s.supportDesc())), - Map.entry("supportBadge", supportBadge(s.supportState())), + Map.entry("supportBadge", supportBadge(s.supportState(), extraTokens)), Map.entry("supportBadgeClass", supportBadgeClass(s.supportState())), - Map.entry("canonicalUrl", "%s/%s/%s.html".formatted(BASE_URL, s.category(), s.slug())), + Map.entry("canonicalUrl", canonicalUrl), Map.entry("flatUrl", "%s/%s.html".formatted(BASE_URL, s.slug())), Map.entry("titleJson", jsonEscape(s.title())), Map.entry("summaryJson", jsonEscape(s.summary())), Map.entry("categoryDisplayJson", jsonEscape(s.catDisplay())), - Map.entry("navArrows", renderNavArrows(s)), + Map.entry("navArrows", renderNavArrows(s, locale)), Map.entry("whyCards", renderWhyCards(tpl.whyCard(), s.whyModernWins())), Map.entry("docLinks", renderDocLinks(tpl.docLink(), s.node().withArray("docs"))), - Map.entry("relatedCards", renderRelatedSection(tpl.relatedCard(), s, all)), - Map.entry("socialShare", renderSocialShare(tpl.socialShare(), s.slug(), s.title())))); + Map.entry("relatedCards", renderRelatedSection(tpl.relatedCard(), s, all, locale, extraTokens)), + Map.entry("socialShare", renderSocialShare(tpl.socialShare(), s.slug(), s.title(), extraTokens)))); + return replaceTokens(tpl.page(), tokens); } -String replaceTokens(String template, Map replacements) { - var m = TOKEN.matcher(template); +/** Load translated content or fall back to English; overwrite code fields from English */ +Snippet resolveSnippet(Snippet englishSnippet, String locale) { + if (locale.equals("en")) return englishSnippet; + + var translatedPath = Path.of(TRANSLATIONS_DIR, "content", locale, + englishSnippet.category(), englishSnippet.slug() + ".json"); + if (!Files.exists(translatedPath)) return englishSnippet; + + try { + var translatedNode = (com.fasterxml.jackson.databind.node.ObjectNode) MAPPER.readTree(translatedPath.toFile()); + // Overwrite code fields with English values + translatedNode.put("oldCode", englishSnippet.oldCode()); + translatedNode.put("modernCode", englishSnippet.modernCode()); + return new Snippet(translatedNode); + } catch (IOException e) { + IO.println("[WARN] Failed to load %s — using English".formatted(translatedPath)); + return englishSnippet; + } +} + +/** Render hreflang tags for all locales */ +String renderHreflangLinks(String pathPart, String slug) { var sb = new StringBuilder(); - while (m.find()) m.appendReplacement(sb, Matcher.quoteReplacement(replacements.getOrDefault(m.group(1), m.group(0)))); - m.appendTail(sb); + for (var entry : LOCALES.entrySet()) { + var loc = entry.getKey(); + String href; + if (slug.equals("index")) { + href = loc.equals("en") ? BASE_URL + "/" : BASE_URL + "/" + loc + "/"; + } else { + href = loc.equals("en") + ? "%s/%s%s.html".formatted(BASE_URL, pathPart, slug) + : "%s/%s/%s%s.html".formatted(BASE_URL, loc, pathPart, slug); + } + sb.append(" \n".formatted(loc, href)); + } + // x-default points to English + var defaultHref = slug.equals("index") + ? BASE_URL + "/" + : "%s/%s%s.html".formatted(BASE_URL, pathPart, slug); + sb.append(" ".formatted(defaultHref)); + return sb.toString(); +} + +/** Render the locale picker dropdown HTML */ +String renderLocalePicker(String currentLocale) { + var sb = new StringBuilder(); + sb.append("
\n"); + sb.append(" \n"); + sb.append("
    \n"); + for (var entry : LOCALES.entrySet()) { + var selected = entry.getKey().equals(currentLocale); + sb.append("
  • %s
  • \n" + .formatted(entry.getKey(), selected, selected ? " class=\"active\"" : "", entry.getValue())); + } + sb.append("
\n"); + sb.append("
"); return sb.toString(); } + +/** Render the i18n script block for client-side JS */ +String renderI18nScript(Map strings, String locale) { + return """ + """.formatted( + locale, + jsEscape(strings.getOrDefault("search.placeholder", "Search snippets…")), + jsEscape(strings.getOrDefault("search.noResults", "No results found.")), + jsEscape(strings.getOrDefault("copy.copied", "Copied!")), + jsEscape(strings.getOrDefault("view.expandAll", "Expand All")), + jsEscape(strings.getOrDefault("view.collapseAll", "Collapse All")), + jsEscape(strings.getOrDefault("cards.hoverHint", "hover to see modern →")), + jsEscape(strings.getOrDefault("cards.touchHint", "👆 tap or swipe →"))); +} + +String jsEscape(String s) { + return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n"); +} + +String replaceTokens(String template, Map replacements) { + // Loop to handle tokens within replacement values (e.g., {{snippetCount}} inside i18n strings) + var result = template; + for (int pass = 0; pass < 3; pass++) { + var m = TOKEN.matcher(result); + var sb = new StringBuilder(); + boolean found = false; + while (m.find()) { + var replacement = replacements.get(m.group(1)); + if (replacement != null) { + m.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + found = true; + } else { + m.appendReplacement(sb, Matcher.quoteReplacement(m.group(0))); + } + } + m.appendTail(sb); + result = sb.toString(); + if (!found) break; + } + return result; +} diff --git a/html-generators/locales.properties b/html-generators/locales.properties new file mode 100644 index 0000000..e02c2f8 --- /dev/null +++ b/html-generators/locales.properties @@ -0,0 +1,3 @@ +# format: locale=Display name (first entry is the default/primary locale) +en=English +pt-BR=Português (Brasil) diff --git a/site/app.js b/site/app.js index 7ce11e7..3058079 100644 --- a/site/app.js +++ b/site/app.js @@ -6,12 +6,24 @@ (() => { 'use strict'; + /* ---------- Locale Detection ---------- */ + const detectLocale = () => { + const path = location.pathname; + const match = path.match(/^\/([a-z]{2}(?:-[A-Z]{2})?)\//); + return match ? match[1] : 'en'; + }; + const locale = detectLocale(); + const localePrefix = locale === 'en' ? '' : '/' + locale; + /* ---------- Snippets Data ---------- */ let snippets = []; const loadSnippets = async () => { try { - const res = await fetch('/data/snippets.json'); + const indexPath = locale === 'en' + ? '/data/snippets.json' + : '/' + locale + '/data/snippets.json'; + const res = await fetch(indexPath); snippets = await res.json(); } catch (e) { console.warn('Could not load snippets.json:', e); @@ -85,7 +97,7 @@ // Click handlers on results resultsContainer.querySelectorAll('.search-result').forEach(el => { el.addEventListener('click', () => { - window.location.href = '/' + el.dataset.category + '/' + el.dataset.slug + '.html'; + window.location.href = localePrefix + '/' + el.dataset.category + '/' + el.dataset.slug + '.html'; }); }); }; @@ -144,7 +156,7 @@ } else if (e.key === 'Enter') { e.preventDefault(); if (selectedIndex >= 0 && visibleResults[selectedIndex]) { - window.location.href = '/' + visibleResults[selectedIndex].category + '/' + visibleResults[selectedIndex].slug + '.html'; + window.location.href = localePrefix + '/' + visibleResults[selectedIndex].category + '/' + visibleResults[selectedIndex].slug + '.html'; } } }); @@ -202,6 +214,9 @@ : null; if (target) { target.click(); + // Scroll the filter section into view + const section = document.getElementById('all-comparisons'); + if (section) section.scrollIntoView({ behavior: 'smooth' }); } else { const allButton = document.querySelector('.filter-pill[data-filter="all"]'); if (allButton) allButton.click(); @@ -226,7 +241,7 @@ // Update hover hints for touch devices document.querySelectorAll('.hover-hint').forEach(hint => { - hint.textContent = '👆 tap or swipe →'; + hint.textContent = (window.i18n && window.i18n.touchHint) || '👆 tap or swipe →'; }); document.querySelectorAll('.tip-card').forEach(card => { @@ -318,7 +333,7 @@ navigator.clipboard.writeText(text).then(() => { btn.classList.add('copied'); const original = btn.textContent; - btn.textContent = 'Copied!'; + btn.textContent = (window.i18n && window.i18n.copied) || 'Copied!'; setTimeout(() => { btn.classList.remove('copied'); btn.textContent = original; @@ -335,7 +350,7 @@ document.body.removeChild(textarea); btn.classList.add('copied'); const original = btn.textContent; - btn.textContent = 'Copied!'; + btn.textContent = (window.i18n && window.i18n.copied) || 'Copied!'; setTimeout(() => { btn.classList.remove('copied'); btn.textContent = original; @@ -550,7 +565,7 @@ if (isExpanded) { tipsGrid.classList.add('expanded'); toggleBtn.querySelector('.view-toggle-icon').textContent = '⊟'; - toggleBtn.querySelector('.view-toggle-text').textContent = 'Collapse All'; + toggleBtn.querySelector('.view-toggle-text').textContent = (window.i18n && window.i18n.collapseAll) || 'Collapse All'; // Remove toggled class from all cards when expanding document.querySelectorAll('.tip-card').forEach(card => { @@ -559,7 +574,7 @@ } else { tipsGrid.classList.remove('expanded'); toggleBtn.querySelector('.view-toggle-icon').textContent = '⊞'; - toggleBtn.querySelector('.view-toggle-text').textContent = 'Expand All'; + toggleBtn.querySelector('.view-toggle-text').textContent = (window.i18n && window.i18n.expandAll) || 'Expand All'; } }); @@ -593,6 +608,61 @@ }); }; + /* ========================================================== + 8. Locale Picker + ========================================================== */ + const initLocalePicker = () => { + const picker = document.getElementById('localePicker'); + if (!picker) return; + + const toggleBtn = picker.querySelector('.locale-toggle'); + const list = picker.querySelector('ul'); + + const open = () => { + list.style.display = 'block'; + toggleBtn.setAttribute('aria-expanded', 'true'); + }; + + const close = () => { + list.style.display = 'none'; + toggleBtn.setAttribute('aria-expanded', 'false'); + }; + + toggleBtn.addEventListener('click', (e) => { + e.stopPropagation(); + list.style.display === 'block' ? close() : open(); + }); + + document.addEventListener('click', close); + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') close(); + }); + + list.querySelectorAll('li').forEach(li => { + li.addEventListener('click', (e) => { + e.stopPropagation(); + const targetLocale = li.dataset.locale; + if (targetLocale === locale) { close(); return; } + + // Rewrite current path for the target locale + let path = location.pathname; + + // Strip current locale prefix if present + if (locale !== 'en') { + path = path.replace(new RegExp('^/' + locale.replace('-', '\\-')), ''); + } + + // Add target locale prefix + if (targetLocale !== 'en') { + path = '/' + targetLocale + path; + } + + localStorage.setItem('preferred-locale', targetLocale); + window.location.href = path; + }); + }); + }; + /* ========================================================== Utilities ========================================================== */ @@ -616,5 +686,6 @@ initSyntaxHighlighting(); initNewsletter(); initThemeToggle(); + initLocalePicker(); }); })(); diff --git a/site/pt-BR/collections/collectors-teeing.html b/site/pt-BR/collections/collectors-teeing.html new file mode 100644 index 0000000..e34fc6d --- /dev/null +++ b/site/pt-BR/collections/collectors-teeing.html @@ -0,0 +1,368 @@ + + + + + + Collectors.teeing() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + intermediate +
+

Collectors.teeing()

+

Compute two aggregations in a single stream pass.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
long count = items.stream().count();
+double sum = items.stream()
+    .mapToDouble(Item::price)
+    .sum();
+var result = new Stats(count, sum);
+
+
+
+
+ ✓ Java 12+ + +
+
+
var result = items.stream().collect(
+    Collectors.teeing(
+        Collectors.counting(),
+        Collectors.summingDouble(Item::price),
+        Stats::new
+    )
+);
+
+
+
+
+ +
+ +
+
+
+

Single pass

+

Process the stream once instead of twice.

+
+
+
🧩
+

Composable

+

Combine any two collectors with a merger function.

+
+
+
🔒
+

Immutable result

+

Merge into a record or value object directly.

+
+
+
+ +
+
+
Abordagem Antiga
+
Two Passes
+
+
+
Abordagem Moderna
+
teeing()
+
+
+
Desde o JDK
+
12
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Collectors.teeing()
+ Disponível +

Widely available since JDK 12 (March 2019)

+
+
+ +
+ +

Collectors.teeing() sends each element to two downstream collectors and merges the results. This avoids streaming the data twice or using a mutable accumulator.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/copying-collections-immutably.html b/site/pt-BR/collections/copying-collections-immutably.html new file mode 100644 index 0000000..1305019 --- /dev/null +++ b/site/pt-BR/collections/copying-collections-immutably.html @@ -0,0 +1,362 @@ + + + + + + Copying collections immutably | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Copying collections immutably

+

Create an immutable copy of any collection in one call.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
List<String> copy =
+    Collections.unmodifiableList(
+        new ArrayList<>(original)
+    );
+
+
+
+
+ ✓ Java 10+ + +
+
+
List<String> copy =
+    List.copyOf(original);
+
+
+
+
+ +
+ +
+
+
+

Smart copy

+

Skips the copy if the source is already immutable.

+
+
+
📏
+

One call

+

No manual ArrayList construction + wrapping.

+
+
+
🛡️
+

Defensive copy

+

Changes to the original don't affect the copy.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Copy + Wrap
+
+
+
Abordagem Moderna
+
List.copyOf()
+
+
+
Desde o JDK
+
10
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Copying collections immutably
+ Disponível +

Widely available since JDK 10 (March 2018)

+
+
+ +
+ +

List.copyOf(), Set.copyOf(), and Map.copyOf() create immutable snapshots of existing collections. If the source is already an immutable collection, no copy is made.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/immutable-list-creation.html b/site/pt-BR/collections/immutable-list-creation.html new file mode 100644 index 0000000..9bb9c21 --- /dev/null +++ b/site/pt-BR/collections/immutable-list-creation.html @@ -0,0 +1,367 @@ + + + + + + Criação de listas imutáveis | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Criação de listas imutáveis

+

Crie listas imutáveis em uma expressão limpa.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
List<String> list =
+    Collections.unmodifiableList(
+        new ArrayList<>(
+            Arrays.asList("a", "b", "c")
+        )
+    );
+
+
+
+
+ ✓ Java 9+ + +
+
+
List<String> list =
+    List.of("a", "b", "c");
+
+
+
+
+ +
+ +
+
+
📏
+

Uma chamada

+

Substitua três chamadas aninhadas por um único método fábrica.

+
+
+
🔒
+

Verdadeiramente imutável

+

Não é apenas um wrapper — a lista em si é imutável.

+
+
+
🛡️
+

Seguro contra nulos

+

Rejeita elementos nulos no momento da criação, falhando rapidamente.

+
+
+
+ +
+
+
Abordagem Antiga
+
Encapsulamento verboso
+
+
+
Abordagem Moderna
+
List.of()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Criação de listas imutáveis
+ Disponível +

Amplamente disponível desde o JDK 9 (setembro de 2017)

+
+
+ +
+ +

List.of() cria uma lista verdadeiramente imutável — sem encapsulamento, sem cópia defensiva. Rejeita elementos nulos (null-hostile) e é estruturalmente imutável. O modo antigo exigia três chamadas aninhadas.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/immutable-map-creation.html b/site/pt-BR/collections/immutable-map-creation.html new file mode 100644 index 0000000..9deb16c --- /dev/null +++ b/site/pt-BR/collections/immutable-map-creation.html @@ -0,0 +1,363 @@ + + + + + + Immutable map creation | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Immutable map creation

+

Create immutable maps inline without a builder.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Map<String, Integer> map = new HashMap<>();
+map.put("a", 1);
+map.put("b", 2);
+map.put("c", 3);
+map = Collections.unmodifiableMap(map);
+
+
+
+
+ ✓ Java 9+ + +
+
+
Map<String, Integer> map =
+    Map.of("a", 1, "b", 2, "c", 3);
+
+
+
+
+ +
+ +
+
+
📏
+

Inline creation

+

No temporary mutable map needed.

+
+
+
🔒
+

Immutable result

+

The map cannot be modified after creation.

+
+
+
🚫
+

No null keys/values

+

Null entries are rejected immediately.

+
+
+
+ +
+
+
Abordagem Antiga
+
Map Builder Pattern
+
+
+
Abordagem Moderna
+
Map.of()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Immutable map creation
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Map.of() accepts key-value pairs inline and returns an immutable map. For more than 10 entries, use Map.ofEntries() with Map.entry() pairs.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/immutable-set-creation.html b/site/pt-BR/collections/immutable-set-creation.html new file mode 100644 index 0000000..e8b16c0 --- /dev/null +++ b/site/pt-BR/collections/immutable-set-creation.html @@ -0,0 +1,368 @@ + + + + + + Immutable set creation | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Immutable set creation

+

Create immutable sets with a single factory call.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Set<String> set =
+    Collections.unmodifiableSet(
+        new HashSet<>(
+            Arrays.asList("a", "b", "c")
+        )
+    );
+
+
+
+
+ ✓ Java 9+ + +
+
+
Set<String> set =
+    Set.of("a", "b", "c");
+
+
+
+
+ +
+ +
+
+
📏
+

Concise

+

One line instead of three nested calls.

+
+
+
🚫
+

Detects duplicates

+

Throws if you accidentally pass duplicate elements.

+
+
+
🔒
+

Immutable

+

No add/remove possible after creation.

+
+
+
+ +
+
+
Abordagem Antiga
+
Verbose Wrapping
+
+
+
Abordagem Moderna
+
Set.of()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Immutable set creation
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Set.of() creates a truly immutable set that rejects nulls and duplicate elements at creation time. No more wrapping mutable sets.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/map-entry-factory.html b/site/pt-BR/collections/map-entry-factory.html new file mode 100644 index 0000000..1263418 --- /dev/null +++ b/site/pt-BR/collections/map-entry-factory.html @@ -0,0 +1,363 @@ + + + + + + Map.entry() factory | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Map.entry() factory

+

Create map entries with a clean factory method.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Map.Entry<String, Integer> e =
+    new AbstractMap.SimpleEntry<>(
+        "key", 42
+    );
+
+
+
+
+ ✓ Java 9+ + +
+
+
var e = Map.entry("key", 42);
+
+
+
+
+ +
+ +
+
+
📏
+

Concise

+

One line instead of three with a clearer intent.

+
+
+
🔒
+

Immutable

+

The returned entry cannot be modified.

+
+
+
🧩
+

Composable

+

Works perfectly with Map.ofEntries() for large maps.

+
+
+
+ +
+
+
Abordagem Antiga
+
SimpleEntry
+
+
+
Abordagem Moderna
+
Map.entry()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Map.entry() factory
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Map.entry() replaces the verbose AbstractMap.SimpleEntry constructor. It returns an immutable entry, making it ideal for Map.ofEntries() and stream operations.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/reverse-list-iteration.html b/site/pt-BR/collections/reverse-list-iteration.html new file mode 100644 index 0000000..6be4598 --- /dev/null +++ b/site/pt-BR/collections/reverse-list-iteration.html @@ -0,0 +1,366 @@ + + + + + + Reverse list iteration | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Reverse list iteration

+

Iterate over a list in reverse order with a clean for-each loop.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
for (ListIterator<String> it =
+        list.listIterator(list.size());
+    it.hasPrevious(); ) {
+    String element = it.previous();
+    System.out.println(element);
+}
+
+
+
+
+ ✓ Java 21+ + +
+
+
for (String element : list.reversed()) {
+    System.out.println(element);
+}
+
+
+
+
+ +
+ +
+
+
📖
+

Natural syntax

+

Enhanced for loop instead of verbose ListIterator.

+
+
+
+

No copying

+

reversed() returns a view — no performance overhead.

+
+
+
🧩
+

Consistent API

+

Works on List, Deque, SortedSet uniformly.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual ListIterator
+
+
+
Abordagem Moderna
+
reversed()
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Reverse list iteration
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

The reversed() method from SequencedCollection returns a reverse-ordered view of the list. This view is backed by the original list, so no copying occurs. The enhanced for loop syntax makes reverse iteration as readable as forward iteration.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/sequenced-collections.html b/site/pt-BR/collections/sequenced-collections.html new file mode 100644 index 0000000..86c21a0 --- /dev/null +++ b/site/pt-BR/collections/sequenced-collections.html @@ -0,0 +1,371 @@ + + + + + + Sequenced collections | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Sequenced collections

+

Access first/last elements and reverse views with clean API methods.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Get last element
+var last = list.get(list.size() - 1);
+// Get first
+var first = list.get(0);
+// Reverse iteration: manual
+
+
+
+
+ ✓ Java 21+ + +
+
+
var last = list.getLast();
+var first = list.getFirst();
+var reversed = list.reversed();
+
+
+
+
+ +
+ +
+
+
📖
+

Self-documenting

+

getLast() is clearer than get(size()-1).

+
+
+
🔄
+

Reversed view

+

reversed() gives a view — no copying needed.

+
+
+
🧩
+

Uniform API

+

Works the same on List, Deque, SortedSet.

+
+
+
+ +
+
+
Abordagem Antiga
+
Index Arithmetic
+
+
+
Abordagem Moderna
+
getFirst/getLast
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Sequenced collections
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

SequencedCollection adds getFirst(), getLast(), reversed(), addFirst(), addLast() to List, Deque, SortedSet, and LinkedHashSet. No more size-1 arithmetic or manual reverse iteration.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/stream-toarray-typed.html b/site/pt-BR/collections/stream-toarray-typed.html new file mode 100644 index 0000000..fe7ea73 --- /dev/null +++ b/site/pt-BR/collections/stream-toarray-typed.html @@ -0,0 +1,368 @@ + + + + + + Typed stream toArray | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + beginner +
+

Typed stream toArray

+

Convert streams to typed arrays with a method reference.

+
+ +
+ +
+
+
+ ✕ Pre-Streams + +
+
+
List<String> list = getNames();
+String[] arr = new String[list.size()];
+for (int i = 0; i < list.size(); i++) {
+    arr[i] = list.get(i);
+}
+
+
+
+
+ ✓ Java 8+ + +
+
+
String[] arr = getNames().stream()
+    .filter(n -> n.length() > 3)
+    .toArray(String[]::new);
+
+
+
+
+ +
+ +
+
+
🎯
+

Type-safe

+

No Object[] cast — the array type is correct.

+
+
+
🔗
+

Chainable

+

Works at the end of any stream pipeline.

+
+
+
📏
+

Concise

+

One expression replaces the manual loop.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Array Copy
+
+
+
Abordagem Moderna
+
toArray(generator)
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Typed stream toArray
+ Disponível +

Widely available since JDK 8 (March 2014)

+
+
+ +
+ +

The toArray(IntFunction) method creates a properly typed array from a stream. The generator (String[]::new) tells the stream what type of array to create.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/collections/unmodifiable-collectors.html b/site/pt-BR/collections/unmodifiable-collectors.html new file mode 100644 index 0000000..bdc929a --- /dev/null +++ b/site/pt-BR/collections/unmodifiable-collectors.html @@ -0,0 +1,364 @@ + + + + + + Unmodifiable collectors | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Collections + intermediate +
+

Unmodifiable collectors

+

Collect directly to an unmodifiable list with stream.toList().

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
List<String> list = stream.collect(
+    Collectors.collectingAndThen(
+        Collectors.toList(),
+        Collections::unmodifiableList
+    )
+);
+
+
+
+
+ ✓ Java 16+ + +
+
+
List<String> list = stream.toList();
+
+
+
+
+ +
+ +
+
+
📏
+

Shortest yet

+

stream.toList() needs no collect() or Collectors import at all.

+
+
+
🔒
+

Immutable

+

Result cannot be modified — no accidental mutations.

+
+
+
📖
+

Readable

+

Reads naturally as the terminal step of any stream pipeline.

+
+
+
+ +
+
+
Abordagem Antiga
+
collectingAndThen
+
+
+
Abordagem Moderna
+
stream.toList()
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Unmodifiable collectors
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

Java 10 added toUnmodifiableList(), toUnmodifiableSet(), and toUnmodifiableMap() to replace the verbose collectingAndThen wrapper. For lists specifically, Java 16's stream.toList() provides an even simpler alternative — no collect() call at all. Use toUnmodifiableSet() and toUnmodifiableMap() for other collection types.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/completablefuture-chaining.html b/site/pt-BR/concurrency/completablefuture-chaining.html new file mode 100644 index 0000000..3692080 --- /dev/null +++ b/site/pt-BR/concurrency/completablefuture-chaining.html @@ -0,0 +1,388 @@ + + + + + + CompletableFuture chaining | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + intermediate +
+

CompletableFuture chaining

+

Chain async operations without blocking, using CompletableFuture.

+
+ +
+ +
+
+
+ ✕ Pre-Java 8 + +
+
+
Future<String> future =
+    executor.submit(this::fetchData);
+String data = future.get(); // blocks
+String result = transform(data);
+
+
+
+
+ ✓ Java 8+ + +
+
+
CompletableFuture.supplyAsync(
+    this::fetchData
+)
+.thenApply(this::transform)
+.thenAccept(System.out::println);
+
+
+
+
+ +
+ +
+
+
🔗
+

Chainable

+

Compose async steps into a readable pipeline.

+
+
+
🚫
+

Non-blocking

+

No thread sits idle waiting for results.

+
+
+
🛡️
+

Error handling

+

exceptionally() and handle() for clean error recovery.

+
+
+
+ +
+
+
Abordagem Antiga
+
Blocking Future.get()
+
+
+
Abordagem Moderna
+
CompletableFuture
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
CompletableFuture chaining
+ Disponível +

Widely available since JDK 8 (March 2014)

+
+
+ +
+ +

CompletableFuture enables non-blocking async pipelines. Chain operations with thenApply, thenCompose, thenAccept. Handle errors with exceptionally(). Combine multiple futures with allOf/anyOf.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/concurrent-http-virtual.html b/site/pt-BR/concurrency/concurrent-http-virtual.html new file mode 100644 index 0000000..fa10921 --- /dev/null +++ b/site/pt-BR/concurrency/concurrent-http-virtual.html @@ -0,0 +1,404 @@ + + + + + + Concurrent HTTP with virtual threads | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + intermediate +
+

Concurrent HTTP with virtual threads

+

Fetch many URLs concurrently with virtual threads and HttpClient.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
ExecutorService pool =
+    Executors.newFixedThreadPool(10);
+List<Future<String>> futures =
+    urls.stream()
+    .map(u -> pool.submit(
+        () -> fetchUrl(u)))
+    .toList();
+// manual shutdown, blocking get()
+
+
+
+
+ ✓ Java 21+ + +
+
+
try (var exec = Executors
+    .newVirtualThreadPerTaskExecutor()) {
+    var results = urls.stream()
+        .map(u -> exec.submit(
+            () -> client.send(req(u),
+                ofString()).body()))
+        .toList().stream()
+        .map(Future::join).toList();
+}
+
+
+
+
+ +
+ +
+
+
♾️
+

Thread per request

+

No pool sizing — one virtual thread per URL.

+
+
+
📖
+

Simple code

+

Write straightforward blocking code.

+
+
+
+

High throughput

+

Thousands of concurrent requests with minimal resources.

+
+
+
+ +
+
+
Abordagem Antiga
+
Thread Pool + URLConnection
+
+
+
Abordagem Moderna
+
Virtual Threads + HttpClient
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Concurrent HTTP with virtual threads
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Virtual threads make it practical to create a thread per HTTP request. Combined with HttpClient, this replaces complex async callback patterns with simple blocking code that scales.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/executor-try-with-resources.html b/site/pt-BR/concurrency/executor-try-with-resources.html new file mode 100644 index 0000000..a20a50c --- /dev/null +++ b/site/pt-BR/concurrency/executor-try-with-resources.html @@ -0,0 +1,377 @@ + + + + + + ExecutorService auto-close | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + beginner +
+

ExecutorService auto-close

+

Use try-with-resources for automatic executor shutdown.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
ExecutorService exec =
+    Executors.newCachedThreadPool();
+try {
+    exec.submit(task);
+} finally {
+    exec.shutdown();
+    exec.awaitTermination(
+        1, TimeUnit.MINUTES);
+}
+
+
+
+
+ ✓ Java 19+ + +
+
+
try (var exec =
+        Executors.newCachedThreadPool()) {
+    exec.submit(task);
+}
+// auto shutdown + await on close
+
+
+
+
+ +
+ +
+
+
🧹
+

Auto cleanup

+

Shutdown happens automatically when the block exits.

+
+
+
🛡️
+

No leaks

+

Executor always shuts down, even if exceptions occur.

+
+
+
📖
+

Familiar pattern

+

Same try-with-resources used for files, connections, etc.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Shutdown
+
+
+
Abordagem Moderna
+
try-with-resources
+
+
+
Desde o JDK
+
19
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
ExecutorService auto-close
+ Disponível +

Widely available since JDK 19 (Sept 2022)

+
+
+ +
+ +

Since Java 19, ExecutorService implements AutoCloseable. The close() method calls shutdown() and waits for tasks to complete. No more manual try/finally shutdown patterns.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/lock-free-lazy-init.html b/site/pt-BR/concurrency/lock-free-lazy-init.html new file mode 100644 index 0000000..7e2f616 --- /dev/null +++ b/site/pt-BR/concurrency/lock-free-lazy-init.html @@ -0,0 +1,393 @@ + + + + + + Lock-free lazy initialization | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + advanced +
+

Lock-free lazy initialization

+

Replace double-checked locking with StableValue for lazy singletons.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
class Config {
+    private static volatile Config inst;
+    static Config get() {
+        if (inst == null) {
+            synchronized (Config.class) {
+                if (inst == null)
+                    inst = load();
+            }
+        }
+        return inst;
+    }
+}
+
+
+
+
+ ✓ Java 25 (Preview) + +
+
+
class Config {
+    private static final
+        StableValue<Config> INST =
+            StableValue.of(Config::load);
+
+    static Config get() {
+        return INST.get();
+    }
+}
+
+
+
+
+ +
+ +
+
+
🧹
+

No boilerplate

+

No volatile, synchronized, or double-null-check.

+
+
+
+

Faster reads

+

JVM can constant-fold after initialization.

+
+
+
+

Provably correct

+

No subtle ordering bugs — the JVM handles it.

+
+
+
+ +
+
+
Abordagem Antiga
+
synchronized + volatile
+
+
+
Abordagem Moderna
+
StableValue
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Lock-free lazy initialization
+ Preview +

Preview in JDK 25 (JEP 502, StableValue). Requires --enable-preview.

+
+
+ +
+ +

StableValue encapsulates the lazy initialization pattern with correct thread safety. The JVM can optimize the read path after initialization, potentially making it faster than volatile reads.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/process-api.html b/site/pt-BR/concurrency/process-api.html new file mode 100644 index 0000000..fece9ac --- /dev/null +++ b/site/pt-BR/concurrency/process-api.html @@ -0,0 +1,388 @@ + + + + + + Modern Process API | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + intermediate +
+

Modern Process API

+

Inspect and manage OS processes with ProcessHandle.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Process p = Runtime.getRuntime()
+    .exec("ls -la");
+int code = p.waitFor();
+// no way to get PID
+// no easy process info
+
+
+
+
+ ✓ Java 9+ + +
+
+
ProcessHandle ph =
+    ProcessHandle.current();
+long pid = ph.pid();
+ph.info().command()
+    .ifPresent(System.out::println);
+ph.children().forEach(
+    c -> System.out.println(c.pid()));
+
+
+
+
+ +
+ +
+
+
🔍
+

Full info

+

Access PID, command, arguments, start time, CPU usage.

+
+
+
🌳
+

Process tree

+

Navigate parent, children, and descendants.

+
+
+
📊
+

Monitoring

+

onExit() returns a CompletableFuture for async monitoring.

+
+
+
+ +
+
+
Abordagem Antiga
+
Runtime.exec()
+
+
+
Abordagem Moderna
+
ProcessHandle
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Modern Process API
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

ProcessHandle provides PIDs, process info (command, arguments, start time, CPU usage), parent/child relationships, and process destruction. No more undocumented Process internals.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/scoped-values.html b/site/pt-BR/concurrency/scoped-values.html new file mode 100644 index 0000000..8cef9d2 --- /dev/null +++ b/site/pt-BR/concurrency/scoped-values.html @@ -0,0 +1,388 @@ + + + + + + Scoped values | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + advanced +
+

Scoped values

+

Share data across call stacks safely without ThreadLocal pitfalls.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
static final ThreadLocal<User> CURRENT =
+    new ThreadLocal<>();
+void handle(Request req) {
+    CURRENT.set(authenticate(req));
+    try { process(); }
+    finally { CURRENT.remove(); }
+}
+
+
+
+
+ ✓ Java 25 + +
+
+
static final ScopedValue<User> CURRENT =
+    ScopedValue.newInstance();
+void handle(Request req) {
+    ScopedValue.where(CURRENT,
+        authenticate(req)
+    ).run(this::process);
+}
+
+
+
+
+ +
+ +
+
+
🔒
+

Immutable

+

Callees can read but never modify the scoped value.

+
+
+
🧹
+

Auto cleanup

+

No manual remove() — value is scoped to the block.

+
+
+
+

Virtual-thread safe

+

Works efficiently with millions of virtual threads.

+
+
+
+ +
+
+
Abordagem Antiga
+
ThreadLocal
+
+
+
Abordagem Moderna
+
ScopedValue
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Scoped values
+ Disponível +

Finalized in JDK 25 LTS (JEP 506, Sept 2025).

+
+
+ +
+ +

ScopedValue provides immutable, inheritable, scope-limited context. Unlike ThreadLocal, scoped values are automatically cleaned up, work with virtual threads, and can't be mutated by callees.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/stable-values.html b/site/pt-BR/concurrency/stable-values.html new file mode 100644 index 0000000..86b2ca3 --- /dev/null +++ b/site/pt-BR/concurrency/stable-values.html @@ -0,0 +1,388 @@ + + + + + + Stable values | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + advanced +
+

Stable values

+

Thread-safe lazy initialization without volatile or synchronized.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
private volatile Logger logger;
+Logger getLogger() {
+    if (logger == null) {
+        synchronized (this) {
+            if (logger == null)
+                logger = createLogger();
+        }
+    }
+    return logger;
+}
+
+
+
+
+ ✓ Java 25 (Preview) + +
+
+
private final StableValue<Logger> logger =
+    StableValue.of(this::createLogger);
+
+Logger getLogger() {
+    return logger.get();
+}
+
+
+
+
+ +
+ +
+
+
🧹
+

Zero boilerplate

+

No volatile, synchronized, or null checks.

+
+
+
+

JVM-optimized

+

The JVM can fold the value after initialization.

+
+
+
🛡️
+

Guaranteed once

+

The supplier runs exactly once, even under contention.

+
+
+
+ +
+
+
Abordagem Antiga
+
Double-Checked Locking
+
+
+
Abordagem Moderna
+
StableValue
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Stable values
+ Preview +

Preview in JDK 25 (JEP 502). Requires --enable-preview.

+
+
+ +
+ +

StableValue provides a lazily initialized, immutable value with built-in thread safety. No double-checked locking, no volatile fields, no synchronized blocks. The JVM can even optimize the read path after initialization.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/structured-concurrency.html b/site/pt-BR/concurrency/structured-concurrency.html new file mode 100644 index 0000000..5ab68bd --- /dev/null +++ b/site/pt-BR/concurrency/structured-concurrency.html @@ -0,0 +1,388 @@ + + + + + + Structured concurrency | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + advanced +
+

Structured concurrency

+

Manage concurrent task lifetimes as a single unit of work.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
ExecutorService exec =
+    Executors.newFixedThreadPool(2);
+Future<User> u = exec.submit(this::fetchUser);
+Future<Order> o = exec.submit(this::fetchOrder);
+try {
+    return combine(u.get(), o.get());
+} finally { exec.shutdown(); }
+
+
+
+
+ ✓ Java 25 (Preview) + +
+
+
try (var scope = new StructuredTaskScope
+        .ShutdownOnFailure()) {
+    var u = scope.fork(this::fetchUser);
+    var o = scope.fork(this::fetchOrder);
+    scope.join().throwIfFailed();
+    return combine(u.get(), o.get());
+}
+
+
+
+
+ +
+ +
+
+
🛡️
+

No thread leaks

+

All forked tasks complete before the scope closes.

+
+
+
+

Fast failure

+

ShutdownOnFailure cancels siblings if one fails.

+
+
+
📐
+

Clear structure

+

Task lifetime matches the lexical scope in code.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Thread Lifecycle
+
+
+
Abordagem Moderna
+
StructuredTaskScope
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Structured concurrency
+ Preview +

Preview in JDK 25 (fifth preview, JEP 505). Requires --enable-preview.

+
+
+ +
+ +

Structured concurrency treats a group of concurrent tasks as one operation. If any subtask fails, the others are cancelled. The scope ensures no thread leaks and gives clear parent-child relationships.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/thread-sleep-duration.html b/site/pt-BR/concurrency/thread-sleep-duration.html new file mode 100644 index 0000000..5afe443 --- /dev/null +++ b/site/pt-BR/concurrency/thread-sleep-duration.html @@ -0,0 +1,388 @@ + + + + + + Thread.sleep with Duration | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + beginner +
+

Thread.sleep with Duration

+

Use Duration for self-documenting time values.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// What unit is 5000? ms? us?
+Thread.sleep(5000);
+
+// 2.5 seconds: math required
+Thread.sleep(2500);
+
+
+
+
+ ✓ Java 19+ + +
+
+
Thread.sleep(
+    Duration.ofSeconds(5)
+);
+Thread.sleep(
+    Duration.ofMillis(2500)
+);
+
+
+
+
+ +
+ +
+
+
📖
+

Self-documenting

+

Duration.ofSeconds(5) is unambiguous.

+
+
+
🛡️
+

Unit-safe

+

No accidentally passing microseconds as milliseconds.

+
+
+
🧩
+

Composable

+

Duration math: plus(), multipliedBy(), etc.

+
+
+
+ +
+
+
Abordagem Antiga
+
Milliseconds
+
+
+
Abordagem Moderna
+
Duration
+
+
+
Desde o JDK
+
19
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Thread.sleep with Duration
+ Disponível +

Widely available since JDK 19 (Sept 2022)

+
+
+ +
+ +

Thread.sleep(Duration) makes the time unit explicit. No more guessing whether 5000 means milliseconds or microseconds. Works with Duration.ofSeconds, ofMillis, ofMinutes, etc.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/concurrency/virtual-threads.html b/site/pt-BR/concurrency/virtual-threads.html new file mode 100644 index 0000000..cff1b46 --- /dev/null +++ b/site/pt-BR/concurrency/virtual-threads.html @@ -0,0 +1,378 @@ + + + + + + Virtual threads | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Concurrency + beginner +
+

Virtual threads

+

Create millions of lightweight virtual threads instead of heavy OS threads.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Thread thread = new Thread(() -> {
+    System.out.println("hello");
+});
+thread.start();
+thread.join();
+
+
+
+
+ ✓ Java 21+ + +
+
+
Thread.startVirtualThread(() -> {
+    System.out.println("hello");
+}).join();
+
+
+
+
+ +
+ +
+
+
+

Lightweight

+

Virtual threads use KB of memory, platform threads use MB.

+
+
+
♾️
+

Scalable

+

Create millions of threads — no pool sizing needed.

+
+
+
🧹
+

Simple model

+

Write blocking code that scales like async code.

+
+
+
+ +
+
+
Abordagem Antiga
+
Platform Threads
+
+
+
Abordagem Moderna
+
Virtual Threads
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Virtual threads
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Virtual threads are lightweight threads managed by the JVM, not the OS. You can create millions of them without tuning thread pools. They're ideal for I/O-bound tasks like HTTP calls and database queries.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/data/snippets.json b/site/pt-BR/data/snippets.json new file mode 100644 index 0000000..125611d --- /dev/null +++ b/site/pt-BR/data/snippets.json @@ -0,0 +1,4183 @@ +[ { + "id" : 95, + "slug" : "compact-canonical-constructor", + "title" : "Compact canonical constructor", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "16", + "oldLabel" : "Java 16", + "modernLabel" : "Java 16+", + "oldApproach" : "Explicit constructor validation", + "modernApproach" : "Compact constructor", + "oldCode" : "public record Person(String name,\n List pets) {\n // Full canonical constructor\n public Person(String name,\n List pets) {\n Objects.requireNonNull(name);\n this.name = name;\n this.pets = List.copyOf(pets);\n }\n}", + "modernCode" : "public record Person(String name,\n List pets) {\n // Compact constructor\n public Person {\n Objects.requireNonNull(name);\n pets = List.copyOf(pets);\n }\n}", + "summary" : "Validate and normalize record fields without repeating parameter lists.", + "explanation" : "Records can declare a compact canonical constructor that omits the parameter list and field assignments. The compiler automatically assigns parameters to fields after your validation logic runs. This is ideal for precondition checks, defensive copies, and normalization.", + "whyModernWins" : [ { + "icon" : "✂️", + "title" : "Less repetition", + "desc" : "No need to repeat parameter list or assign each field manually." + }, { + "icon" : "🛡️", + "title" : "Validation", + "desc" : "Perfect for null checks, range validation, and defensive copies." + }, { + "icon" : "📖", + "title" : "Clearer intent", + "desc" : "Compact syntax emphasizes validation, not boilerplate." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ ] +}, { + "id" : 9, + "slug" : "compact-source-files", + "title" : "Compact source files", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25", + "oldApproach" : "Main Class Ceremony", + "modernApproach" : "void main()", + "oldCode" : "public class HelloWorld {\n public static void main(String[] args) {\n System.out.println(\n \"Hello, World!\");\n }\n}", + "modernCode" : "void main() {\n IO.println(\"Hello, World!\");\n}", + "summary" : "Write a complete program without class declaration or public static void main.", + "explanation" : "Compact source files remove the ceremony of class declarations and the main method signature for simple programs. Combined with implicit import of java.io.IO, even println is available directly.", + "whyModernWins" : [ { + "icon" : "🚀", + "title" : "Zero ceremony", + "desc" : "No class, no public static void main, no String[] args." + }, { + "icon" : "🎓", + "title" : "Beginner-friendly", + "desc" : "New programmers can write useful code from line 1." + }, { + "icon" : "📝", + "title" : "Script-like", + "desc" : "Perfect for quick prototypes, scripts, and examples." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 25 LTS (JEP 512, Sept 2025)." + }, + "docs" : [ { + "title" : "Simple Source Files and Instance Main Methods (JEP 495)", + "href" : "https://openjdk.org/jeps/495" + } ] +}, { + "id" : 86, + "slug" : "default-interface-methods", + "title" : "Default interface methods", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "8", + "oldLabel" : "Java 7", + "modernLabel" : "Java 8+", + "oldApproach" : "Abstract classes for shared behavior", + "modernApproach" : "Default methods on interfaces", + "oldCode" : "// Need abstract class to share behavior\npublic abstract class AbstractLogger {\n public void log(String msg) {\n System.out.println(\n timestamp() + \": \" + msg);\n }\n abstract String timestamp();\n}\n\n// Single inheritance only\npublic class FileLogger\n extends AbstractLogger { ... }", + "modernCode" : "public interface Logger {\n default void log(String msg) {\n System.out.println(\n timestamp() + \": \" + msg);\n }\n String timestamp();\n}\n\n// Multiple interfaces allowed\npublic class FileLogger\n implements Logger, Closeable { ... }", + "summary" : "Add method implementations directly in interfaces, enabling multiple inheritance of behavior.", + "explanation" : "Before Java 8, sharing behavior across unrelated classes required abstract classes, which limited you to single inheritance. Default methods let interfaces provide method implementations, so classes can inherit behavior from multiple interfaces. This was essential for evolving the Collections API (e.g., List.forEach, Map.getOrDefault) without breaking existing implementations.", + "whyModernWins" : [ { + "icon" : "🔀", + "title" : "Multiple inheritance", + "desc" : "Classes can implement many interfaces with default methods, unlike single abstract class inheritance." + }, { + "icon" : "📦", + "title" : "API evolution", + "desc" : "Add new methods to interfaces without breaking existing implementations." + }, { + "icon" : "🧩", + "title" : "Composable behavior", + "desc" : "Mix and match capabilities from multiple interfaces freely." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 8 (March 2014)." + }, + "docs" : [ { + "title" : "Default Methods (Java Tutorial)", + "href" : "https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html" + } ] +}, { + "id" : 12, + "slug" : "diamond-operator", + "title" : "Diamond with anonymous classes", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 7/8", + "modernLabel" : "Java 9+", + "oldApproach" : "Repeat Type Args", + "modernApproach" : "Diamond <>", + "oldCode" : "Map> map =\n new HashMap>();\n// anonymous class: no diamond\nPredicate p =\n new Predicate() {\n public boolean test(String s) {..}\n };", + "modernCode" : "Map> map =\n new HashMap<>();\n// Java 9: diamond with anonymous classes\nPredicate p =\n new Predicate<>() {\n public boolean test(String s) {..}\n };", + "summary" : "Diamond operator now works with anonymous classes too.", + "explanation" : "Java 7 introduced <> but it didn't work with anonymous inner classes. Java 9 fixed this, so you never need to repeat type arguments on the right-hand side.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Consistent rules", + "desc" : "Diamond works everywhere — constructors and anonymous classes alike." + }, { + "icon" : "🧹", + "title" : "Less redundancy", + "desc" : "Type arguments are stated once on the left, never repeated." + }, { + "icon" : "🔧", + "title" : "DRY principle", + "desc" : "The compiler already knows the type — why write it twice?" + } ], + "support" : { + "state" : "available", + "description" : "Diamond with anonymous classes since JDK 9 (Sept 2017)." + }, + "docs" : [ { + "title" : "Diamond with Anonymous Classes (JEP 213)", + "href" : "https://openjdk.org/jeps/213" + } ] +}, { + "id" : 18, + "slug" : "exhaustive-switch", + "title" : "Exhaustive switch without default", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Mandatory default", + "modernApproach" : "Sealed Exhaustiveness", + "oldCode" : "// Must add default even though\n// all cases are covered\ndouble area(Shape s) {\n if (s instanceof Circle c)\n return Math.PI * c.r() * c.r();\n else if (s instanceof Rect r)\n return r.w() * r.h();\n else throw new IAE();\n}", + "modernCode" : "// sealed Shape permits Circle, Rect\ndouble area(Shape s) {\n return switch (s) {\n case Circle c ->\n Math.PI * c.r() * c.r();\n case Rect r ->\n r.w() * r.h();\n }; // no default needed!\n}", + "summary" : "Compiler verifies all sealed subtypes are covered — no default needed.", + "explanation" : "When switching over a sealed type, the compiler knows all possible subtypes and verifies every case is handled. If you add a new subtype, the compiler flags every switch that's now incomplete.", + "whyModernWins" : [ { + "icon" : "✅", + "title" : "Compile-time safety", + "desc" : "Add a new subtype and the compiler shows every place to update." + }, { + "icon" : "🚫", + "title" : "No dead code", + "desc" : "No unreachable default branch that masks bugs." + }, { + "icon" : "📐", + "title" : "Algebraic types", + "desc" : "Sealed + records + exhaustive switch = proper ADTs in Java." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Pattern Matching for switch (JEP 441)", + "href" : "https://openjdk.org/jeps/441" + }, { + "title" : "Sealed Classes (JEP 409)", + "href" : "https://openjdk.org/jeps/409" + } ] +}, { + "id" : 10, + "slug" : "flexible-constructor-bodies", + "title" : "Flexible constructor bodies", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25+", + "oldApproach" : "Validate After super()", + "modernApproach" : "Code Before super()", + "oldCode" : "class Square extends Shape {\n Square(double side) {\n super(side, side);\n // can't validate BEFORE super!\n if (side <= 0)\n throw new IAE(\"bad\");\n }\n}", + "modernCode" : "class Square extends Shape {\n Square(double side) {\n if (side <= 0)\n throw new IAE(\"bad\");\n super(side, side);\n }\n}", + "summary" : "Validate and compute values before calling super() or this().", + "explanation" : "Java 25 lifts the restriction that super() must be the first statement. You can now validate arguments, compute derived values, and set up state before delegating to the parent constructor.", + "whyModernWins" : [ { + "icon" : "🛡️", + "title" : "Fail fast", + "desc" : "Validate arguments before the superclass constructor runs." + }, { + "icon" : "🧮", + "title" : "Compute first", + "desc" : "Derive values and prepare data before calling super()." + }, { + "icon" : "🧹", + "title" : "No workarounds", + "desc" : "No more static helper methods or factory patterns to work around the restriction." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 25 LTS (JEP 513, Sept 2025)." + }, + "docs" : [ { + "title" : "Flexible Constructor Bodies (JEP 492)", + "href" : "https://openjdk.org/jeps/492" + } ] +}, { + "id" : 15, + "slug" : "guarded-patterns", + "title" : "Guarded patterns with when", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Nested if", + "modernApproach" : "when Clause", + "oldCode" : "if (shape instanceof Circle c) {\n if (c.radius() > 10) {\n return \"large circle\";\n } else {\n return \"small circle\";\n }\n} else {\n return \"not a circle\";\n}", + "modernCode" : "return switch (shape) {\n case Circle c\n when c.radius() > 10\n -> \"large circle\";\n case Circle c\n -> \"small circle\";\n default -> \"not a circle\";\n};", + "summary" : "Add conditions to pattern cases using when guards.", + "explanation" : "Guarded patterns let you refine a type match with an additional boolean condition. This keeps all the branching logic in the switch instead of nesting if statements inside cases.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Precise matching", + "desc" : "Combine type + condition in a single case label." + }, { + "icon" : "📐", + "title" : "Flat structure", + "desc" : "No nested if/else inside switch cases." + }, { + "icon" : "📖", + "title" : "Readable intent", + "desc" : "The when clause reads like natural language." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Pattern Matching for switch (JEP 441)", + "href" : "https://openjdk.org/jeps/441" + } ] +}, { + "id" : 92, + "slug" : "markdown-javadoc-comments", + "title" : "Markdown in Javadoc comments", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "23", + "oldLabel" : "Java 8", + "modernLabel" : "Java 23+", + "oldApproach" : "HTML-based Javadoc", + "modernApproach" : "Markdown Javadoc", + "oldCode" : "/**\n * Returns the {@code User} with\n * the given ID.\n *\n *

Example:\n *

{@code\n * var user = findUser(123);\n * }
\n *\n * @param id the user ID\n * @return the user\n */\npublic User findUser(int id) { ... }", + "modernCode" : "/// Returns the `User` with\n/// the given ID.\n///\n/// Example:\n/// ```java\n/// var user = findUser(123);\n/// ```\n///\n/// @param id the user ID\n/// @return the user\npublic User findUser(int id) { ... }", + "summary" : "Write Javadoc comments in Markdown instead of HTML for better readability.", + "explanation" : "Java 23 introduces /// Markdown-style Javadoc comments as an alternative to the traditional /** */ HTML-based format. Markdown syntax is more natural to write and read, with support for code blocks, emphasis, lists, and links. The compiler converts Markdown to HTML for javadoc output.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Natural syntax", + "desc" : "Use backticks for inline code and ``` for blocks instead of HTML tags." + }, { + "icon" : "✍️", + "title" : "Easier to write", + "desc" : "No need for {@code},
, 

tags — just write Markdown." + }, { + "icon" : "👁", + "title" : "Better in editors", + "desc" : "Markdown renders beautifully in modern IDEs and text editors." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 23 (Sept 2024)" + }, + "docs" : [ ] +}, { + "id" : 17, + "slug" : "module-import-declarations", + "title" : "Module import declarations", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25+", + "oldApproach" : "Many Imports", + "modernApproach" : "import module", + "oldCode" : "import java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;", + "modernCode" : "import module java.base;\n\n// All of java.util, java.io, java.nio\n// etc. available in one line", + "summary" : "Import all exported packages of a module with a single declaration.", + "explanation" : "Module import declarations let you import everything a module exports with one line. This is especially useful for java.base which covers collections, I/O, streams, and more.", + "whyModernWins" : [ { + "icon" : "🧹", + "title" : "One line", + "desc" : "Replace a wall of imports with a single module import." + }, { + "icon" : "📦", + "title" : "Module-aware", + "desc" : "Leverages the module system to import coherent sets of packages." + }, { + "icon" : "🚀", + "title" : "Quick starts", + "desc" : "Perfect for scripts and prototypes where import lists are tedious." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 25 LTS (JEP 511, Sept 2025)." + }, + "docs" : [ { + "title" : "Module Import Declarations (JEP 494)", + "href" : "https://openjdk.org/jeps/494" + } ] +}, { + "id" : 4, + "slug" : "pattern-matching-instanceof", + "title" : "Pattern matching for instanceof", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "instanceof + Cast", + "modernApproach" : "Pattern Variable", + "oldCode" : "if (obj instanceof String) {\n String s = (String) obj;\n System.out.println(s.length());\n}", + "modernCode" : "if (obj instanceof String s) {\n System.out.println(s.length());\n}", + "summary" : "Combine type check and cast in one step with pattern matching.", + "explanation" : "Pattern matching for instanceof eliminates the redundant cast after a type check. The variable is automatically scoped to where the pattern matches, making code safer and shorter.", + "whyModernWins" : [ { + "icon" : "🔄", + "title" : "No redundant cast", + "desc" : "Type check and variable binding happen in a single expression." + }, { + "icon" : "📏", + "title" : "Fewer lines", + "desc" : "One line instead of two — the cast line disappears entirely." + }, { + "icon" : "🛡️", + "title" : "Scope safety", + "desc" : "The pattern variable is only in scope where the type is guaranteed." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ { + "title" : "Pattern Matching for instanceof (JEP 394)", + "href" : "https://openjdk.org/jeps/394" + } ] +}, { + "id" : 14, + "slug" : "pattern-matching-switch", + "title" : "Pattern matching in switch", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "if-else Chain", + "modernApproach" : "Type Patterns", + "oldCode" : "String format(Object obj) {\n if (obj instanceof Integer i)\n return \"int: \" + i;\n else if (obj instanceof Double d)\n return \"double: \" + d;\n else if (obj instanceof String s)\n return \"str: \" + s;\n return \"unknown\";\n}", + "modernCode" : "String format(Object obj) {\n return switch (obj) {\n case Integer i -> \"int: \" + i;\n case Double d -> \"double: \" + d;\n case String s -> \"str: \" + s;\n default -> \"unknown\";\n };\n}", + "summary" : "Replace if-else instanceof chains with clean switch type patterns.", + "explanation" : "Pattern matching in switch lets you match on types directly, combining the type test, cast, and binding in one concise case label. The compiler checks completeness.", + "whyModernWins" : [ { + "icon" : "📐", + "title" : "Structured dispatch", + "desc" : "Switch makes the branching structure explicit and scannable." + }, { + "icon" : "🎯", + "title" : "Expression form", + "desc" : "Returns a value directly — no mutable variable needed." + }, { + "icon" : "✅", + "title" : "Exhaustiveness", + "desc" : "The compiler ensures all types are handled." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Pattern Matching for switch (JEP 441)", + "href" : "https://openjdk.org/jeps/441" + } ] +}, { + "id" : 16, + "slug" : "primitive-types-in-patterns", + "title" : "Primitive types in patterns", + "category" : "language", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25 (Preview)", + "oldApproach" : "Manual Range Checks", + "modernApproach" : "Primitive Patterns", + "oldCode" : "String classify(int code) {\n if (code >= 200 && code < 300)\n return \"success\";\n else if (code >= 400 && code < 500)\n return \"client error\";\n else\n return \"other\";\n}", + "modernCode" : "String classify(int code) {\n return switch (code) {\n case int c when c >= 200\n && c < 300 -> \"success\";\n case int c when c >= 400\n && c < 500 -> \"client error\";\n default -> \"other\";\n };\n}", + "summary" : "Pattern matching now works with primitive types, not just objects.", + "explanation" : "Java 25 extends pattern matching to primitive types. You can use int, long, double etc. in switch patterns with when guards, eliminating the need for boxing or manual range checks.", + "whyModernWins" : [ { + "icon" : "📦", + "title" : "No boxing", + "desc" : "Match primitives directly — no Integer wrapper needed." + }, { + "icon" : "🎯", + "title" : "Pattern consistency", + "desc" : "Same pattern syntax for objects and primitives." + }, { + "icon" : "⚡", + "title" : "Better performance", + "desc" : "Avoid autoboxing overhead in pattern matching." + } ], + "support" : { + "state" : "preview", + "description" : "Preview in JDK 25 (third preview, JEP 507). Requires --enable-preview." + }, + "docs" : [ { + "title" : "Primitive Types in Patterns (JEP 488)", + "href" : "https://openjdk.org/jeps/488" + } ] +}, { + "id" : 13, + "slug" : "private-interface-methods", + "title" : "Private interface methods", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Duplicated Logic", + "modernApproach" : "Private Methods", + "oldCode" : "interface Logger {\n default void logInfo(String msg) {\n System.out.println(\n \"[INFO] \" + timestamp() + msg);\n }\n default void logWarn(String msg) {\n System.out.println(\n \"[WARN] \" + timestamp() + msg);\n }\n}", + "modernCode" : "interface Logger {\n private String format(String lvl, String msg) {\n return \"[\" + lvl + \"] \" + timestamp() + msg;\n }\n default void logInfo(String msg) {\n System.out.println(format(\"INFO\", msg));\n }\n default void logWarn(String msg) {\n System.out.println(format(\"WARN\", msg));\n }\n}", + "summary" : "Extract shared logic in interfaces using private methods.", + "explanation" : "Java 9 allows private methods in interfaces, enabling you to share code between default methods without exposing implementation details to implementing classes.", + "whyModernWins" : [ { + "icon" : "🧩", + "title" : "Code reuse", + "desc" : "Share logic between default methods without duplication." + }, { + "icon" : "🔐", + "title" : "Encapsulation", + "desc" : "Implementation details stay hidden from implementing classes." + }, { + "icon" : "🧹", + "title" : "DRY interfaces", + "desc" : "No more copy-paste between default methods." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Private Interface Methods", + "href" : "https://openjdk.org/jeps/213" + } ] +}, { + "id" : 7, + "slug" : "record-patterns", + "title" : "Record patterns (destructuring)", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Manual Access", + "modernApproach" : "Destructuring", + "oldCode" : "if (obj instanceof Point) {\n Point p = (Point) obj;\n int x = p.getX();\n int y = p.getY();\n System.out.println(x + y);\n}", + "modernCode" : "if (obj instanceof Point(int x, int y)) {\n System.out.println(x + y);\n}", + "summary" : "Destructure records directly in patterns — extract fields in one step.", + "explanation" : "Record patterns let you decompose a record's components directly in instanceof and switch. Nested patterns are supported too, enabling deep matching without intermediate variables.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Direct extraction", + "desc" : "Access record components without calling accessors manually." + }, { + "icon" : "🪆", + "title" : "Nestable", + "desc" : "Patterns can nest — match inner records in a single expression." + }, { + "icon" : "📏", + "title" : "Compact code", + "desc" : "Five lines become two — less ceremony, same clarity." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Record Patterns (JEP 440)", + "href" : "https://openjdk.org/jeps/440" + } ] +}, { + "id" : 5, + "slug" : "records-for-data-classes", + "title" : "Records para classes de dados", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "POJO verboso", + "modernApproach" : "record", + "oldCode" : "public class Point {\n private final int x, y;\n public Point(int x, int y) { ... }\n public int getX() { return x; }\n public int getY() { return y; }\n // equals, hashCode, toString\n}", + "modernCode" : "public record Point(int x, int y) {}", + "summary" : "Uma linha substitui mais de 30 linhas de boilerplate para portadores de dados imutáveis.", + "explanation" : "Records geram automaticamente o construtor, acessores (x(), y()), equals(), hashCode() e toString(). São imutáveis por design e ideais para DTOs, objetos de valor e pattern matching.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Definição em uma linha", + "desc" : "Uma única linha substitui construtor, getters, equals, hashCode, toString." + }, { + "icon" : "🔒", + "title" : "Imutável por padrão", + "desc" : "Todos os campos são final — sem armadilhas de setters." + }, { + "icon" : "🧩", + "title" : "Compatível com patterns", + "desc" : "Records funcionam com padrões de desestruturação em switch e instanceof." + } ], + "support" : { + "state" : "available", + "description" : "Amplamente disponível desde o JDK 16 (março de 2021)" + }, + "docs" : [ { + "title" : "Records (JEP 395)", + "href" : "https://openjdk.org/jeps/395" + }, { + "title" : "Record class", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Record.html" + } ] +}, { + "id" : 6, + "slug" : "sealed-classes", + "title" : "Sealed classes for type hierarchies", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "Java 8", + "modernLabel" : "Java 17+", + "oldApproach" : "Open Hierarchy", + "modernApproach" : "sealed permits", + "oldCode" : "// Anyone can extend Shape\npublic abstract class Shape { }\npublic class Circle extends Shape { }\npublic class Rect extends Shape { }\n// unknown subclasses possible", + "modernCode" : "public sealed interface Shape\n permits Circle, Rect {}\npublic record Circle(double r)\n implements Shape {}\npublic record Rect(double w, double h)\n implements Shape {}", + "summary" : "Restrict which classes can extend a type — enabling exhaustive switches.", + "explanation" : "Sealed classes define a closed set of subtypes. The compiler knows all possible cases, enabling exhaustive pattern matching without a default branch. Combined with records, they model algebraic data types.", + "whyModernWins" : [ { + "icon" : "🔐", + "title" : "Controlled hierarchy", + "desc" : "Only permitted subtypes can extend — no surprise subclasses." + }, { + "icon" : "✅", + "title" : "Exhaustive matching", + "desc" : "The compiler verifies switch covers all cases, no default needed." + }, { + "icon" : "📐", + "title" : "Algebraic data types", + "desc" : "Model sum types naturally — sealed + records = ADTs in Java." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 17 LTS (Sept 2021)" + }, + "docs" : [ { + "title" : "Sealed Classes (JEP 409)", + "href" : "https://openjdk.org/jeps/409" + } ] +}, { + "id" : 94, + "slug" : "static-members-in-inner-classes", + "title" : "Static members in inner classes", + "category" : "language", + "difficulty" : "intermediate", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "Must use static nested class", + "modernApproach" : "Static members in inner classes", + "oldCode" : "class Library {\n // Must be static nested class\n static class Book {\n static int globalBookCount;\n\n Book() {\n globalBookCount++;\n }\n }\n}\n\n// Usage\nvar book = new Library.Book();", + "modernCode" : "class Library {\n // Can be inner class with statics\n class Book {\n static int globalBookCount;\n\n Book() {\n Book.globalBookCount++;\n }\n }\n}\n\n// Usage\nvar lib = new Library();\nvar book = lib.new Book();", + "summary" : "Define static members in inner classes without requiring static nested classes.", + "explanation" : "Before Java 16, only static nested classes could contain static members. Inner (non-static) classes couldn't have statics because they required an enclosing instance. Java 16 relaxes this restriction, allowing static fields, methods, and even nested types in inner classes.", + "whyModernWins" : [ { + "icon" : "🔓", + "title" : "More flexibility", + "desc" : "Inner classes can now have static members when needed." + }, { + "icon" : "🧩", + "title" : "Shared state", + "desc" : "Track shared state across instances of an inner class." + }, { + "icon" : "📐", + "title" : "Design freedom", + "desc" : "No need to promote to static nested class just for one static field." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ ] +}, { + "id" : 93, + "slug" : "static-methods-in-interfaces", + "title" : "Static methods in interfaces", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "8", + "oldLabel" : "Java 7", + "modernLabel" : "Java 8+", + "oldApproach" : "Utility classes", + "modernApproach" : "Interface static methods", + "oldCode" : "// Separate utility class needed\npublic class ValidatorUtils {\n public static boolean isBlank(\n String s) {\n return s == null ||\n s.trim().isEmpty();\n }\n}\n\n// Usage\nif (ValidatorUtils.isBlank(input)) { ... }", + "modernCode" : "public interface Validator {\n boolean validate(String s);\n\n static boolean isBlank(String s) {\n return s == null ||\n s.trim().isEmpty();\n }\n}\n\n// Usage\nif (Validator.isBlank(input)) { ... }", + "summary" : "Add static utility methods directly to interfaces instead of separate utility classes.", + "explanation" : "Before Java 8, utility methods related to an interface had to live in a separate class (e.g., Collections for Collection). Static methods in interfaces let you keep related utilities together. Common in modern APIs like Comparator.comparing(), Stream.of(), and List.of().", + "whyModernWins" : [ { + "icon" : "📦", + "title" : "Better organization", + "desc" : "Keep related utilities with the interface, not in a separate class." + }, { + "icon" : "🔍", + "title" : "Discoverability", + "desc" : "Factory and helper methods are found where you'd expect them." + }, { + "icon" : "🧩", + "title" : "API cohesion", + "desc" : "No need for separate *Utils or *Helper classes." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 8 (March 2014)" + }, + "docs" : [ ] +}, { + "id" : 3, + "slug" : "switch-expressions", + "title" : "Switch expressions", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "14", + "oldLabel" : "Java 8", + "modernLabel" : "Java 14+", + "oldApproach" : "Switch Statement", + "modernApproach" : "Switch Expression", + "oldCode" : "String msg;\nswitch (day) {\n case MONDAY:\n msg = \"Start\";\n break;\n case FRIDAY:\n msg = \"End\";\n break;\n default:\n msg = \"Mid\";\n}", + "modernCode" : "String msg = switch (day) {\n case MONDAY -> \"Start\";\n case FRIDAY -> \"End\";\n default -> \"Mid\";\n};", + "summary" : "Switch as an expression that returns a value — no break, no fall-through.", + "explanation" : "Switch expressions return a value directly, use arrow syntax to prevent fall-through bugs, and the compiler verifies exhaustiveness. This replaces the error-prone statement form.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Returns a value", + "desc" : "Assign the switch result directly — no temporary variable needed." + }, { + "icon" : "🛡️", + "title" : "No fall-through", + "desc" : "Arrow syntax eliminates accidental fall-through bugs from missing break." + }, { + "icon" : "✅", + "title" : "Exhaustiveness check", + "desc" : "The compiler ensures all cases are covered." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 14 (March 2020)" + }, + "docs" : [ { + "title" : "Switch Expressions (JEP 361)", + "href" : "https://openjdk.org/jeps/361" + } ] +}, { + "id" : 2, + "slug" : "text-blocks-for-multiline-strings", + "title" : "Text blocks for multiline strings", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "15", + "oldLabel" : "Java 8", + "modernLabel" : "Java 15+", + "oldApproach" : "String Concatenation", + "modernApproach" : "Text Blocks", + "oldCode" : "String json = \"{\\n\" +\n \" \\\"name\\\": \\\"Duke\\\",\\n\" +\n \" \\\"age\\\": 30\\n\" +\n \"}\";", + "modernCode" : "String json = \"\"\"\n {\n \"name\": \"Duke\",\n \"age\": 30\n }\"\"\";", + "summary" : "Write multiline strings naturally with triple-quote text blocks.", + "explanation" : "Text blocks let you write multiline strings exactly as they appear. No more escaping quotes or adding \\n. The compiler strips incidental indentation automatically.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Readable as-is", + "desc" : "JSON, SQL, and HTML look like real JSON, SQL, and HTML in your source." + }, { + "icon" : "🚫", + "title" : "No escape hell", + "desc" : "Embedded quotes don't need backslash escaping." + }, { + "icon" : "📐", + "title" : "Smart indentation", + "desc" : "Leading whitespace is trimmed automatically based on the closing delimiter position." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 15 (Sept 2020)" + }, + "docs" : [ { + "title" : "Text Blocks (JEP 378)", + "href" : "https://openjdk.org/jeps/378" + }, { + "title" : "Text Blocks Guide", + "href" : "https://docs.oracle.com/en/java/javase/25/language/text-blocks.html" + } ] +}, { + "id" : 1, + "slug" : "type-inference-with-var", + "title" : "Inferência de tipo com var", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "10", + "oldLabel" : "Java 8", + "modernLabel" : "Java 10+", + "oldApproach" : "Tipos explícitos", + "modernApproach" : "Palavra-chave var", + "oldCode" : "Map> map =\n new HashMap>();\nfor (Map.Entry> e\n : map.entrySet()) {\n // verbose type noise\n}", + "modernCode" : "var map = new HashMap>();\nfor (var entry : map.entrySet()) {\n // clean and readable\n}", + "summary" : "Use var para inferência de tipo em variáveis locais — menos ruído, mesma segurança.", + "explanation" : "Desde o Java 10, o compilador infere os tipos de variáveis locais a partir do lado direito da atribuição. Isso reduz o ruído visual sem sacrificar a segurança de tipos. Use var quando o tipo for óbvio pelo contexto.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Menos boilerplate", + "desc" : "Não é necessário repetir tipos genéricos complexos em ambos os lados da atribuição." + }, { + "icon" : "👁", + "title" : "Melhor legibilidade", + "desc" : "Foco nos nomes de variáveis e valores, não nas declarações de tipo." + }, { + "icon" : "🔒", + "title" : "Ainda seguro em tipos", + "desc" : "O compilador infere e impõe o tipo exato em tempo de compilação." + } ], + "support" : { + "state" : "available", + "description" : "Amplamente disponível desde o JDK 10 (março de 2018)" + }, + "docs" : [ { + "title" : "Local Variable Type Inference (JEP 286)", + "href" : "https://openjdk.org/jeps/286" + }, { + "title" : "Style Guidelines for var", + "href" : "https://openjdk.org/projects/amber/guides/lvti-style-guide" + } ] +}, { + "id" : 8, + "slug" : "unnamed-variables", + "title" : "Unnamed variables with _", + "category" : "language", + "difficulty" : "beginner", + "jdkVersion" : "22", + "oldLabel" : "Java 8", + "modernLabel" : "Java 22+", + "oldApproach" : "Unused Variable", + "modernApproach" : "_ Placeholder", + "oldCode" : "try {\n parse(input);\n} catch (Exception ignored) {\n log(\"parse failed\");\n}\nmap.forEach((key, value) -> {\n process(value); // key unused\n});", + "modernCode" : "try {\n parse(input);\n} catch (Exception _) {\n log(\"parse failed\");\n}\nmap.forEach((_, value) -> {\n process(value);\n});", + "summary" : "Use _ to signal intent when a variable is intentionally unused.", + "explanation" : "Unnamed variables communicate to readers and tools that a value is deliberately ignored. No more 'ignored' or 'unused' naming conventions, no more IDE warnings.", + "whyModernWins" : [ { + "icon" : "📢", + "title" : "Clear intent", + "desc" : "_ explicitly says 'this value is not needed here'." + }, { + "icon" : "🔇", + "title" : "No warnings", + "desc" : "IDEs and linters won't flag intentionally unused variables." + }, { + "icon" : "🧹", + "title" : "Cleaner lambdas", + "desc" : "Multi-param lambdas are cleaner when you only need some params." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 22 (JEP 456, March 2024)." + }, + "docs" : [ { + "title" : "Unnamed Variables & Patterns (JEP 456)", + "href" : "https://openjdk.org/jeps/456" + } ] +}, { + "id" : 26, + "slug" : "collectors-teeing", + "title" : "Collectors.teeing()", + "category" : "collections", + "difficulty" : "intermediate", + "jdkVersion" : "12", + "oldLabel" : "Java 8", + "modernLabel" : "Java 12+", + "oldApproach" : "Two Passes", + "modernApproach" : "teeing()", + "oldCode" : "long count = items.stream().count();\ndouble sum = items.stream()\n .mapToDouble(Item::price)\n .sum();\nvar result = new Stats(count, sum);", + "modernCode" : "var result = items.stream().collect(\n Collectors.teeing(\n Collectors.counting(),\n Collectors.summingDouble(Item::price),\n Stats::new\n )\n);", + "summary" : "Compute two aggregations in a single stream pass.", + "explanation" : "Collectors.teeing() sends each element to two downstream collectors and merges the results. This avoids streaming the data twice or using a mutable accumulator.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Single pass", + "desc" : "Process the stream once instead of twice." + }, { + "icon" : "🧩", + "title" : "Composable", + "desc" : "Combine any two collectors with a merger function." + }, { + "icon" : "🔒", + "title" : "Immutable result", + "desc" : "Merge into a record or value object directly." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 12 (March 2019)" + }, + "docs" : [ { + "title" : "Collectors.teeing()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Collectors.html#teeing(java.util.stream.Collector,java.util.stream.Collector,java.util.function.BiFunction)" + } ] +}, { + "id" : 22, + "slug" : "copying-collections-immutably", + "title" : "Copying collections immutably", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "10", + "oldLabel" : "Java 8", + "modernLabel" : "Java 10+", + "oldApproach" : "Manual Copy + Wrap", + "modernApproach" : "List.copyOf()", + "oldCode" : "List copy =\n Collections.unmodifiableList(\n new ArrayList<>(original)\n );", + "modernCode" : "List copy =\n List.copyOf(original);", + "summary" : "Create an immutable copy of any collection in one call.", + "explanation" : "List.copyOf(), Set.copyOf(), and Map.copyOf() create immutable snapshots of existing collections. If the source is already an immutable collection, no copy is made.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Smart copy", + "desc" : "Skips the copy if the source is already immutable." + }, { + "icon" : "📏", + "title" : "One call", + "desc" : "No manual ArrayList construction + wrapping." + }, { + "icon" : "🛡️", + "title" : "Defensive copy", + "desc" : "Changes to the original don't affect the copy." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 10 (March 2018)" + }, + "docs" : [ { + "title" : "List.copyOf()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/List.html#copyOf(java.util.Collection)" + } ] +}, { + "id" : 19, + "slug" : "immutable-list-creation", + "title" : "Criação de listas imutáveis", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Encapsulamento verboso", + "modernApproach" : "List.of()", + "oldCode" : "List list =\n Collections.unmodifiableList(\n new ArrayList<>(\n Arrays.asList(\"a\", \"b\", \"c\")\n )\n );", + "modernCode" : "List list =\n List.of(\"a\", \"b\", \"c\");", + "summary" : "Crie listas imutáveis em uma expressão limpa.", + "explanation" : "List.of() cria uma lista verdadeiramente imutável — sem encapsulamento, sem cópia defensiva. Rejeita elementos nulos (null-hostile) e é estruturalmente imutável. O modo antigo exigia três chamadas aninhadas.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Uma chamada", + "desc" : "Substitua três chamadas aninhadas por um único método fábrica." + }, { + "icon" : "🔒", + "title" : "Verdadeiramente imutável", + "desc" : "Não é apenas um wrapper — a lista em si é imutável." + }, { + "icon" : "🛡️", + "title" : "Seguro contra nulos", + "desc" : "Rejeita elementos nulos no momento da criação, falhando rapidamente." + } ], + "support" : { + "state" : "available", + "description" : "Amplamente disponível desde o JDK 9 (setembro de 2017)" + }, + "docs" : [ { + "title" : "List.of()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/List.html#of()" + }, { + "title" : "Collections Factory Methods (JEP 269)", + "href" : "https://openjdk.org/jeps/269" + } ] +}, { + "id" : 20, + "slug" : "immutable-map-creation", + "title" : "Immutable map creation", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Map Builder Pattern", + "modernApproach" : "Map.of()", + "oldCode" : "Map map = new HashMap<>();\nmap.put(\"a\", 1);\nmap.put(\"b\", 2);\nmap.put(\"c\", 3);\nmap = Collections.unmodifiableMap(map);", + "modernCode" : "Map map =\n Map.of(\"a\", 1, \"b\", 2, \"c\", 3);", + "summary" : "Create immutable maps inline without a builder.", + "explanation" : "Map.of() accepts key-value pairs inline and returns an immutable map. For more than 10 entries, use Map.ofEntries() with Map.entry() pairs.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Inline creation", + "desc" : "No temporary mutable map needed." + }, { + "icon" : "🔒", + "title" : "Immutable result", + "desc" : "The map cannot be modified after creation." + }, { + "icon" : "🚫", + "title" : "No null keys/values", + "desc" : "Null entries are rejected immediately." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Map.of()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Map.html#of()" + }, { + "title" : "Map.ofEntries()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Map.html#ofEntries(java.util.Map.Entry...)" + } ] +}, { + "id" : 21, + "slug" : "immutable-set-creation", + "title" : "Immutable set creation", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Verbose Wrapping", + "modernApproach" : "Set.of()", + "oldCode" : "Set set =\n Collections.unmodifiableSet(\n new HashSet<>(\n Arrays.asList(\"a\", \"b\", \"c\")\n )\n );", + "modernCode" : "Set set =\n Set.of(\"a\", \"b\", \"c\");", + "summary" : "Create immutable sets with a single factory call.", + "explanation" : "Set.of() creates a truly immutable set that rejects nulls and duplicate elements at creation time. No more wrapping mutable sets.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Concise", + "desc" : "One line instead of three nested calls." + }, { + "icon" : "🚫", + "title" : "Detects duplicates", + "desc" : "Throws if you accidentally pass duplicate elements." + }, { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "No add/remove possible after creation." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Set.of()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Set.html#of()" + }, { + "title" : "Collections Factory Methods (JEP 269)", + "href" : "https://openjdk.org/jeps/269" + } ] +}, { + "id" : 23, + "slug" : "map-entry-factory", + "title" : "Map.entry() factory", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "SimpleEntry", + "modernApproach" : "Map.entry()", + "oldCode" : "Map.Entry e =\n new AbstractMap.SimpleEntry<>(\n \"key\", 42\n );", + "modernCode" : "var e = Map.entry(\"key\", 42);", + "summary" : "Create map entries with a clean factory method.", + "explanation" : "Map.entry() replaces the verbose AbstractMap.SimpleEntry constructor. It returns an immutable entry, making it ideal for Map.ofEntries() and stream operations.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Concise", + "desc" : "One line instead of three with a clearer intent." + }, { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "The returned entry cannot be modified." + }, { + "icon" : "🧩", + "title" : "Composable", + "desc" : "Works perfectly with Map.ofEntries() for large maps." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Map.entry()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Map.html#entry(K,V)" + } ] +}, { + "id" : 96, + "slug" : "reverse-list-iteration", + "title" : "Reverse list iteration", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Manual ListIterator", + "modernApproach" : "reversed()", + "oldCode" : "for (ListIterator it =\n list.listIterator(list.size());\n it.hasPrevious(); ) {\n String element = it.previous();\n System.out.println(element);\n}", + "modernCode" : "for (String element : list.reversed()) {\n System.out.println(element);\n}", + "summary" : "Iterate over a list in reverse order with a clean for-each loop.", + "explanation" : "The reversed() method from SequencedCollection returns a reverse-ordered view of the list. This view is backed by the original list, so no copying occurs. The enhanced for loop syntax makes reverse iteration as readable as forward iteration.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Natural syntax", + "desc" : "Enhanced for loop instead of verbose ListIterator." + }, { + "icon" : "⚡", + "title" : "No copying", + "desc" : "reversed() returns a view — no performance overhead." + }, { + "icon" : "🧩", + "title" : "Consistent API", + "desc" : "Works on List, Deque, SortedSet uniformly." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ ] +}, { + "id" : 24, + "slug" : "sequenced-collections", + "title" : "Sequenced collections", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Index Arithmetic", + "modernApproach" : "getFirst/getLast", + "oldCode" : "// Get last element\nvar last = list.get(list.size() - 1);\n// Get first\nvar first = list.get(0);\n// Reverse iteration: manual", + "modernCode" : "var last = list.getLast();\nvar first = list.getFirst();\nvar reversed = list.reversed();", + "summary" : "Access first/last elements and reverse views with clean API methods.", + "explanation" : "SequencedCollection adds getFirst(), getLast(), reversed(), addFirst(), addLast() to List, Deque, SortedSet, and LinkedHashSet. No more size-1 arithmetic or manual reverse iteration.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Self-documenting", + "desc" : "getLast() is clearer than get(size()-1)." + }, { + "icon" : "🔄", + "title" : "Reversed view", + "desc" : "reversed() gives a view — no copying needed." + }, { + "icon" : "🧩", + "title" : "Uniform API", + "desc" : "Works the same on List, Deque, SortedSet." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Sequenced Collections (JEP 431)", + "href" : "https://openjdk.org/jeps/431" + }, { + "title" : "SequencedCollection", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/SequencedCollection.html" + } ] +}, { + "id" : 27, + "slug" : "stream-toarray-typed", + "title" : "Typed stream toArray", + "category" : "collections", + "difficulty" : "beginner", + "jdkVersion" : "8", + "oldLabel" : "Pre-Streams", + "modernLabel" : "Java 8+", + "oldApproach" : "Manual Array Copy", + "modernApproach" : "toArray(generator)", + "oldCode" : "List list = getNames();\nString[] arr = new String[list.size()];\nfor (int i = 0; i < list.size(); i++) {\n arr[i] = list.get(i);\n}", + "modernCode" : "String[] arr = getNames().stream()\n .filter(n -> n.length() > 3)\n .toArray(String[]::new);", + "summary" : "Convert streams to typed arrays with a method reference.", + "explanation" : "The toArray(IntFunction) method creates a properly typed array from a stream. The generator (String[]::new) tells the stream what type of array to create.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Type-safe", + "desc" : "No Object[] cast — the array type is correct." + }, { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "Works at the end of any stream pipeline." + }, { + "icon" : "📏", + "title" : "Concise", + "desc" : "One expression replaces the manual loop." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 8 (March 2014)" + }, + "docs" : [ { + "title" : "Stream.toArray()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#toArray(java.util.function.IntFunction)" + } ] +}, { + "id" : 28, + "slug" : "unmodifiable-collectors", + "title" : "Unmodifiable collectors", + "category" : "collections", + "difficulty" : "intermediate", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "collectingAndThen", + "modernApproach" : "stream.toList()", + "oldCode" : "List list = stream.collect(\n Collectors.collectingAndThen(\n Collectors.toList(),\n Collections::unmodifiableList\n )\n);", + "modernCode" : "List list = stream.toList();", + "summary" : "Collect directly to an unmodifiable list with stream.toList().", + "explanation" : "Java 10 added toUnmodifiableList(), toUnmodifiableSet(), and toUnmodifiableMap() to replace the verbose collectingAndThen wrapper. For lists specifically, Java 16's stream.toList() provides an even simpler alternative — no collect() call at all. Use toUnmodifiableSet() and toUnmodifiableMap() for other collection types.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Shortest yet", + "desc" : "stream.toList() needs no collect() or Collectors import at all." + }, { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "Result cannot be modified — no accidental mutations." + }, { + "icon" : "📖", + "title" : "Readable", + "desc" : "Reads naturally as the terminal step of any stream pipeline." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ { + "title" : "Stream.toList()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#toList()" + }, { + "title" : "Collectors.toUnmodifiableList()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Collectors.html#toUnmodifiableList()" + } ] +}, { + "id" : 35, + "slug" : "string-chars-stream", + "title" : "String chars as stream", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Manual Loop", + "modernApproach" : "chars() Stream", + "oldCode" : "for (int i = 0; i < str.length(); i++) {\n char c = str.charAt(i);\n if (Character.isDigit(c)) {\n process(c);\n }\n}", + "modernCode" : "str.chars()\n .filter(Character::isDigit)\n .forEach(c -> process((char) c));", + "summary" : "Process string characters as a stream pipeline.", + "explanation" : "String.chars() returns an IntStream of character values, enabling functional processing. For Unicode support, codePoints() handles supplementary characters correctly.", + "whyModernWins" : [ { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "Use filter, map, collect on character streams." + }, { + "icon" : "📐", + "title" : "Declarative", + "desc" : "Describe what to do, not how to loop." + }, { + "icon" : "🌐", + "title" : "Unicode-ready", + "desc" : "codePoints() correctly handles emoji and supplementary chars." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 8+ (improved in 9+)" + }, + "docs" : [ { + "title" : "String.chars()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#chars()" + }, { + "title" : "CharSequence.codePoints()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/CharSequence.html#codePoints()" + } ] +}, { + "id" : 33, + "slug" : "string-formatted", + "title" : "String.formatted()", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "15", + "oldLabel" : "Java 8", + "modernLabel" : "Java 15+", + "oldApproach" : "String.format()", + "modernApproach" : "formatted()", + "oldCode" : "String msg = String.format(\n \"Hello %s, you are %d\",\n name, age\n);", + "modernCode" : "String msg =\n \"Hello %s, you are %d\"\n .formatted(name, age);", + "summary" : "Call formatted() on the template string itself.", + "explanation" : "String.formatted() is an instance method equivalent to String.format() but called on the format string. It reads more naturally in a left-to-right flow.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Reads naturally", + "desc" : "Template.formatted(args) flows better than String.format(template, args)." + }, { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "Can be chained with other string methods." + }, { + "icon" : "📏", + "title" : "Less verbose", + "desc" : "Drops the redundant String.format() static call." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 15 (Sept 2020)" + }, + "docs" : [ { + "title" : "String.formatted()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#formatted(java.lang.Object...)" + } ] +}, { + "id" : 32, + "slug" : "string-indent-transform", + "title" : "String.indent() and transform()", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "12", + "oldLabel" : "Java 8", + "modernLabel" : "Java 12+", + "oldApproach" : "Manual Indentation", + "modernApproach" : "indent() / transform()", + "oldCode" : "String[] lines = text.split(\"\\n\");\nStringBuilder sb = new StringBuilder();\nfor (String line : lines) {\n sb.append(\" \").append(line)\n .append(\"\\n\");\n}\nString indented = sb.toString();", + "modernCode" : "String indented = text.indent(4);\n\nString result = text\n .transform(String::strip)\n .transform(s -> s.replace(\" \", \"-\"));", + "summary" : "Indent text and chain string transformations fluently.", + "explanation" : "indent(n) adds n spaces to each line. transform(fn) applies any function and returns the result, enabling fluent chaining of string operations.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Built-in", + "desc" : "Indentation is a common operation — now it's one call." + }, { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "transform() enables fluent pipelines on strings." + }, { + "icon" : "🧹", + "title" : "Clean code", + "desc" : "No manual line splitting and StringBuilder loops." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 12 (March 2019)" + }, + "docs" : [ { + "title" : "String.indent()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#indent(int)" + }, { + "title" : "String.transform()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#transform(java.util.function.Function)" + } ] +}, { + "id" : 29, + "slug" : "string-isblank", + "title" : "String.isBlank()", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "trim().isEmpty()", + "modernApproach" : "isBlank()", + "oldCode" : "boolean blank =\n str.trim().isEmpty();\n// or: str.trim().length() == 0", + "modernCode" : "boolean blank = str.isBlank();\n// handles Unicode whitespace too", + "summary" : "Check for blank strings with a single method call.", + "explanation" : "isBlank() returns true if the string is empty or contains only whitespace, including Unicode whitespace characters that trim() misses.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Self-documenting", + "desc" : "isBlank() says exactly what it checks." + }, { + "icon" : "🌐", + "title" : "Unicode-aware", + "desc" : "Handles all Unicode whitespace, not just ASCII." + }, { + "icon" : "⚡", + "title" : "No allocation", + "desc" : "No intermediate trimmed string is created." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "String.isBlank()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#isBlank()" + } ] +}, { + "id" : 86, + "slug" : "string-lines", + "title" : "String.lines() for line splitting", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "split(\"\\\\n\")", + "modernApproach" : "lines()", + "oldCode" : "String text = \"one\\ntwo\\nthree\";\nString[] lines = text.split(\"\\n\");\nfor (String line : lines) {\n System.out.println(line);\n}", + "modernCode" : "String text = \"one\\ntwo\\nthree\";\ntext.lines().forEach(System.out::println);", + "summary" : "Use String.lines() to split text into a stream of lines without regex overhead.", + "explanation" : "String.lines() returns a Stream of lines split by \\n, \\r, or \\r\\n. It is lazier and more efficient than split(), avoids regex compilation, and integrates naturally with the Stream API for further processing.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Lazy streaming", + "desc" : "Lines are produced on demand, not all at once like split()." + }, { + "icon" : "🔧", + "title" : "Universal line endings", + "desc" : "Handles \\n, \\r, and \\r\\n automatically without regex." + }, { + "icon" : "🔗", + "title" : "Stream integration", + "desc" : "Returns a Stream for direct use with filter, map, collect." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 11 (September 2018)." + }, + "docs" : [ { + "title" : "String.lines()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#lines()" + } ] +}, { + "id" : 31, + "slug" : "string-repeat", + "title" : "String.repeat()", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "StringBuilder Loop", + "modernApproach" : "repeat()", + "oldCode" : "StringBuilder sb = new StringBuilder();\nfor (int i = 0; i < 3; i++) {\n sb.append(\"abc\");\n}\nString result = sb.toString();", + "modernCode" : "String result = \"abc\".repeat(3);\n// \"abcabcabc\"", + "summary" : "Repeat a string n times without a loop.", + "explanation" : "String.repeat(int) returns the string concatenated with itself n times. Handles edge cases: repeat(0) returns empty string, repeat(1) returns the same string.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "One-liner", + "desc" : "Replace 5 lines of StringBuilder code with one call." + }, { + "icon" : "⚡", + "title" : "Optimized", + "desc" : "Internal implementation is optimized for large repeats." + }, { + "icon" : "📖", + "title" : "Clear intent", + "desc" : "repeat(3) immediately conveys the purpose." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "String.repeat()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#repeat(int)" + } ] +}, { + "id" : 30, + "slug" : "string-strip", + "title" : "String.strip() vs trim()", + "category" : "strings", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "trim()", + "modernApproach" : "strip()", + "oldCode" : "// trim() only removes ASCII whitespace\n// (chars <= U+0020)\nString clean = str.trim();", + "modernCode" : "// strip() removes all Unicode whitespace\nString clean = str.strip();\nString left = str.stripLeading();\nString right = str.stripTrailing();", + "summary" : "Use Unicode-aware stripping with strip(), stripLeading(), stripTrailing().", + "explanation" : "trim() only removes characters ≤ U+0020 (ASCII control chars and space). strip() uses Character.isWhitespace() which handles Unicode spaces like non-breaking space, ideographic space, etc.", + "whyModernWins" : [ { + "icon" : "🌐", + "title" : "Unicode-correct", + "desc" : "Handles all whitespace characters from every script." + }, { + "icon" : "🎯", + "title" : "Directional", + "desc" : "stripLeading() and stripTrailing() for one-sided trimming." + }, { + "icon" : "🛡️", + "title" : "Fewer bugs", + "desc" : "No surprise whitespace left behind in international text." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "String.strip()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#strip()" + } ] +}, { + "id" : 39, + "slug" : "collectors-flatmapping", + "title" : "Collectors.flatMapping()", + "category" : "streams", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Nested flatMap", + "modernApproach" : "flatMapping()", + "oldCode" : "// Flatten within a grouping collector\n// Required complex custom collector\nMap> tagsByDept =\n // no clean way in Java 8", + "modernCode" : "var tagsByDept = employees.stream()\n .collect(groupingBy(\n Emp::dept,\n flatMapping(\n e -> e.tags().stream(),\n toSet()\n )\n ));", + "summary" : "Use flatMapping() to flatten inside a grouping collector.", + "explanation" : "Collectors.flatMapping() applies a one-to-many mapping as a downstream collector. It's the collector equivalent of Stream.flatMap() — useful inside groupingBy or partitioningBy.", + "whyModernWins" : [ { + "icon" : "🧩", + "title" : "Composable", + "desc" : "Works as a downstream collector inside groupingBy." + }, { + "icon" : "📐", + "title" : "One pass", + "desc" : "Flatten and group in a single stream traversal." + }, { + "icon" : "🔗", + "title" : "Nestable", + "desc" : "Combine with other downstream collectors." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Collectors.flatMapping()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Collectors.html#flatMapping(java.util.function.Function,java.util.stream.Collector)" + } ] +}, { + "id" : 44, + "slug" : "optional-ifpresentorelse", + "title" : "Optional.ifPresentOrElse()", + "category" : "streams", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "if/else on Optional", + "modernApproach" : "ifPresentOrElse()", + "oldCode" : "Optional user = findUser(id);\nif (user.isPresent()) {\n greet(user.get());\n} else {\n handleMissing();\n}", + "modernCode" : "findUser(id).ifPresentOrElse(\n this::greet,\n this::handleMissing\n);", + "summary" : "Handle both present and empty cases of Optional in one call.", + "explanation" : "ifPresentOrElse() takes a Consumer for the present case and a Runnable for the empty case. It avoids the isPresent/get anti-pattern.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Single expression", + "desc" : "Both cases handled in one method call." + }, { + "icon" : "🚫", + "title" : "No get()", + "desc" : "Eliminates the dangerous isPresent() + get() pattern." + }, { + "icon" : "🔗", + "title" : "Fluent", + "desc" : "Chains naturally after findUser() or any Optional-returning method." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Optional.ifPresentOrElse()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Optional.html#ifPresentOrElse(java.util.function.Consumer,java.lang.Runnable)" + } ] +}, { + "id" : 45, + "slug" : "optional-or", + "title" : "Optional.or() fallback", + "category" : "streams", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Nested Fallback", + "modernApproach" : ".or() chain", + "oldCode" : "Optional cfg = primary();\nif (!cfg.isPresent()) {\n cfg = secondary();\n}\nif (!cfg.isPresent()) {\n cfg = defaults();\n}", + "modernCode" : "Optional cfg = primary()\n .or(this::secondary)\n .or(this::defaults);", + "summary" : "Chain Optional fallbacks without nested checks.", + "explanation" : "Optional.or() returns the original Optional if it has a value, otherwise evaluates the supplier to get an alternative Optional. Suppliers are lazy — only called when needed.", + "whyModernWins" : [ { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "Stack fallbacks in a readable pipeline." + }, { + "icon" : "⚡", + "title" : "Lazy evaluation", + "desc" : "Fallback suppliers only execute if needed." + }, { + "icon" : "📖", + "title" : "Declarative", + "desc" : "Reads as 'try primary, or secondary, or defaults'." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Optional.or()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Optional.html#or(java.util.function.Supplier)" + } ] +}, { + "id" : 87, + "slug" : "predicate-not", + "title" : "Predicate.not() for negation", + "category" : "streams", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "Lambda negation", + "modernApproach" : "Predicate.not()", + "oldCode" : "List nonEmpty = list.stream()\n .filter(s -> !s.isBlank())\n .collect(Collectors.toList());", + "modernCode" : "List nonEmpty = list.stream()\n .filter(Predicate.not(String::isBlank))\n .toList();", + "summary" : "Use Predicate.not() to negate method references cleanly instead of writing lambda wrappers.", + "explanation" : "Before Java 11, negating a method reference required wrapping it in a lambda. Predicate.not() lets you negate any predicate directly, keeping the code readable and consistent with method reference style throughout the stream pipeline.", + "whyModernWins" : [ { + "icon" : "👁", + "title" : "Cleaner negation", + "desc" : "No need to wrap method references in lambdas just to negate them." + }, { + "icon" : "🔗", + "title" : "Composable", + "desc" : "Works with any Predicate, enabling clean predicate chains." + }, { + "icon" : "📖", + "title" : "Reads naturally", + "desc" : "Predicate.not(String::isBlank) reads like English." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 11 (September 2018)." + }, + "docs" : [ { + "title" : "Predicate.not()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/function/Predicate.html#not(java.util.function.Predicate)" + } ] +}, { + "id" : 42, + "slug" : "stream-gatherers", + "title" : "Stream gatherers", + "category" : "streams", + "difficulty" : "advanced", + "jdkVersion" : "24", + "oldLabel" : "Java 8", + "modernLabel" : "Java 24+", + "oldApproach" : "Custom Collector", + "modernApproach" : "gather()", + "oldCode" : "// Sliding window: manual implementation\nList> windows = new ArrayList<>();\nfor (int i = 0; i <= list.size()-3; i++) {\n windows.add(\n list.subList(i, i + 3));\n}", + "modernCode" : "var windows = stream\n .gather(\n Gatherers.windowSliding(3)\n )\n .toList();", + "summary" : "Use gatherers for custom intermediate stream operations.", + "explanation" : "Gatherers are a new intermediate stream operation that can express complex transformations like sliding windows, fixed-size groups, and scan operations that were impossible with standard stream ops.", + "whyModernWins" : [ { + "icon" : "🧩", + "title" : "Composable", + "desc" : "Gatherers compose with other stream operations." + }, { + "icon" : "📦", + "title" : "Built-in operations", + "desc" : "windowFixed, windowSliding, fold, scan out of the box." + }, { + "icon" : "🔧", + "title" : "Extensible", + "desc" : "Write custom gatherers for any intermediate transformation." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 24 (JEP 485, March 2025)." + }, + "docs" : [ { + "title" : "Stream Gatherers (JEP 485)", + "href" : "https://openjdk.org/jeps/485" + }, { + "title" : "Gatherers", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Gatherers.html" + } ] +}, { + "id" : 37, + "slug" : "stream-iterate-predicate", + "title" : "Stream.iterate() with predicate", + "category" : "streams", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "iterate + limit", + "modernApproach" : "iterate(seed, pred, op)", + "oldCode" : "Stream.iterate(1, n -> n * 2)\n .limit(10)\n .forEach(System.out::println);\n// can't stop at a condition", + "modernCode" : "Stream.iterate(\n 1,\n n -> n < 1000,\n n -> n * 2\n).forEach(System.out::println);\n// stops when n >= 1000", + "summary" : "Use a predicate to stop iteration — like a for-loop in stream form.", + "explanation" : "The three-argument Stream.iterate(seed, hasNext, next) works like a for-loop: seed is the start, hasNext determines when to stop, and next produces the next value.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Natural termination", + "desc" : "Stop based on a condition, not an arbitrary limit." + }, { + "icon" : "📐", + "title" : "For-loop equivalent", + "desc" : "Same semantics as for(seed; hasNext; next)." + }, { + "icon" : "🛡️", + "title" : "No infinite stream risk", + "desc" : "The predicate guarantees termination." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Stream.iterate()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#iterate(T,java.util.function.Predicate,java.util.function.UnaryOperator)" + } ] +}, { + "id" : 41, + "slug" : "stream-mapmulti", + "title" : "Stream.mapMulti()", + "category" : "streams", + "difficulty" : "intermediate", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "flatMap + List", + "modernApproach" : "mapMulti()", + "oldCode" : "stream.flatMap(order ->\n order.items().stream()\n .map(item -> new OrderItem(\n order.id(), item)\n )\n);", + "modernCode" : "stream.mapMulti(\n (order, downstream) -> {\n for (var item : order.items())\n downstream.accept(\n new OrderItem(order.id(), item));\n }\n);", + "summary" : "Emit zero or more elements per input without creating intermediate streams.", + "explanation" : "mapMulti() is an imperative alternative to flatMap that avoids creating intermediate Stream objects for each element. It's more efficient when the mapping produces a small number of elements.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Less allocation", + "desc" : "No intermediate Stream created per element." + }, { + "icon" : "🎯", + "title" : "Imperative style", + "desc" : "Use loops and conditionals directly." + }, { + "icon" : "📐", + "title" : "Flexible", + "desc" : "Emit zero, one, or many elements with full control." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ { + "title" : "Stream.mapMulti()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#mapMulti(java.util.function.BiConsumer)" + } ] +}, { + "id" : 36, + "slug" : "stream-of-nullable", + "title" : "Stream.ofNullable()", + "category" : "streams", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Null Check", + "modernApproach" : "ofNullable()", + "oldCode" : "Stream s = val != null\n ? Stream.of(val)\n : Stream.empty();", + "modernCode" : "Stream s =\n Stream.ofNullable(val);", + "summary" : "Create a zero-or-one element stream from a nullable value.", + "explanation" : "Stream.ofNullable() returns a single-element stream if the value is non-null, or an empty stream if null. Eliminates the ternary null check pattern.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Concise", + "desc" : "One call replaces the ternary conditional." + }, { + "icon" : "🔗", + "title" : "Flatmap-friendly", + "desc" : "Perfect inside flatMap to skip null values." + }, { + "icon" : "🛡️", + "title" : "Null-safe", + "desc" : "No NPE risk — null becomes empty stream." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Stream.ofNullable()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#ofNullable(T)" + } ] +}, { + "id" : 38, + "slug" : "stream-takewhile-dropwhile", + "title" : "Stream takeWhile / dropWhile", + "category" : "streams", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Manual Loop", + "modernApproach" : "takeWhile/dropWhile", + "oldCode" : "List result = new ArrayList<>();\nfor (int n : sorted) {\n if (n >= 100) break;\n result.add(n);\n}\n// no stream equivalent in Java 8", + "modernCode" : "var result = sorted.stream()\n .takeWhile(n -> n < 100)\n .toList();\n// or: .dropWhile(n -> n < 10)", + "summary" : "Take or drop elements from a stream based on a predicate.", + "explanation" : "takeWhile() returns elements while the predicate is true and stops at the first false. dropWhile() skips elements while true and returns the rest. Both work best on ordered streams.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Short-circuit", + "desc" : "Stops processing as soon as the predicate fails." + }, { + "icon" : "🔗", + "title" : "Pipeline-friendly", + "desc" : "Chain with other stream operations naturally." + }, { + "icon" : "📖", + "title" : "Declarative", + "desc" : "takeWhile reads like English: 'take while less than 100'." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Stream.takeWhile()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#takeWhile(java.util.function.Predicate)" + }, { + "title" : "Stream.dropWhile()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#dropWhile(java.util.function.Predicate)" + } ] +}, { + "id" : 40, + "slug" : "stream-tolist", + "title" : "Stream.toList()", + "category" : "streams", + "difficulty" : "beginner", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "Collectors.toList()", + "modernApproach" : ".toList()", + "oldCode" : "List result = stream\n .filter(s -> s.length() > 3)\n .collect(Collectors.toList());", + "modernCode" : "List result = stream\n .filter(s -> s.length() > 3)\n .toList();", + "summary" : "Terminal toList() replaces the verbose collect(Collectors.toList()).", + "explanation" : "Stream.toList() returns an unmodifiable list. It's equivalent to .collect(Collectors.toUnmodifiableList()) but much shorter. Note: the result is immutable, unlike Collectors.toList().", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "7 chars vs 24", + "desc" : ".toList() replaces .collect(Collectors.toList())." + }, { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "The result list cannot be modified." + }, { + "icon" : "📖", + "title" : "Fluent", + "desc" : "Reads naturally at the end of a pipeline." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ { + "title" : "Stream.toList()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Stream.html#toList()" + } ] +}, { + "id" : 43, + "slug" : "virtual-thread-executor", + "title" : "Virtual thread executor", + "category" : "streams", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Fixed Thread Pool", + "modernApproach" : "Virtual Thread Executor", + "oldCode" : "ExecutorService exec =\n Executors.newFixedThreadPool(10);\ntry {\n futures = tasks.stream()\n .map(t -> exec.submit(t))\n .toList();\n} finally {\n exec.shutdown();\n}", + "modernCode" : "try (var exec = Executors\n .newVirtualThreadPerTaskExecutor()) {\n var futures = tasks.stream()\n .map(exec::submit)\n .toList();\n}", + "summary" : "Use virtual thread executors for unlimited lightweight concurrency.", + "explanation" : "The virtual thread executor creates a new virtual thread for each task. No pool sizing needed — virtual threads are cheap enough to create millions of them.", + "whyModernWins" : [ { + "icon" : "♾️", + "title" : "No sizing", + "desc" : "No pool size to tune — create as many threads as needed." + }, { + "icon" : "⚡", + "title" : "Lightweight", + "desc" : "Virtual threads use KB of memory, not MB." + }, { + "icon" : "🧹", + "title" : "Auto-closeable", + "desc" : "try-with-resources handles shutdown automatically." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Executors.newVirtualThreadPerTaskExecutor()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/Executors.html#newVirtualThreadPerTaskExecutor()" + }, { + "title" : "Virtual Threads (JEP 444)", + "href" : "https://openjdk.org/jeps/444" + } ] +}, { + "id" : 50, + "slug" : "completablefuture-chaining", + "title" : "CompletableFuture chaining", + "category" : "concurrency", + "difficulty" : "intermediate", + "jdkVersion" : "8", + "oldLabel" : "Pre-Java 8", + "modernLabel" : "Java 8+", + "oldApproach" : "Blocking Future.get()", + "modernApproach" : "CompletableFuture", + "oldCode" : "Future future =\n executor.submit(this::fetchData);\nString data = future.get(); // blocks\nString result = transform(data);", + "modernCode" : "CompletableFuture.supplyAsync(\n this::fetchData\n)\n.thenApply(this::transform)\n.thenAccept(System.out::println);", + "summary" : "Chain async operations without blocking, using CompletableFuture.", + "explanation" : "CompletableFuture enables non-blocking async pipelines. Chain operations with thenApply, thenCompose, thenAccept. Handle errors with exceptionally(). Combine multiple futures with allOf/anyOf.", + "whyModernWins" : [ { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "Compose async steps into a readable pipeline." + }, { + "icon" : "🚫", + "title" : "Non-blocking", + "desc" : "No thread sits idle waiting for results." + }, { + "icon" : "🛡️", + "title" : "Error handling", + "desc" : "exceptionally() and handle() for clean error recovery." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 8 (March 2014)" + }, + "docs" : [ { + "title" : "CompletableFuture", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/CompletableFuture.html" + } ] +}, { + "id" : 54, + "slug" : "concurrent-http-virtual", + "title" : "Concurrent HTTP with virtual threads", + "category" : "concurrency", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Thread Pool + URLConnection", + "modernApproach" : "Virtual Threads + HttpClient", + "oldCode" : "ExecutorService pool =\n Executors.newFixedThreadPool(10);\nList> futures =\n urls.stream()\n .map(u -> pool.submit(\n () -> fetchUrl(u)))\n .toList();\n// manual shutdown, blocking get()", + "modernCode" : "try (var exec = Executors\n .newVirtualThreadPerTaskExecutor()) {\n var results = urls.stream()\n .map(u -> exec.submit(\n () -> client.send(req(u),\n ofString()).body()))\n .toList().stream()\n .map(Future::join).toList();\n}", + "summary" : "Fetch many URLs concurrently with virtual threads and HttpClient.", + "explanation" : "Virtual threads make it practical to create a thread per HTTP request. Combined with HttpClient, this replaces complex async callback patterns with simple blocking code that scales.", + "whyModernWins" : [ { + "icon" : "♾️", + "title" : "Thread per request", + "desc" : "No pool sizing — one virtual thread per URL." + }, { + "icon" : "📖", + "title" : "Simple code", + "desc" : "Write straightforward blocking code." + }, { + "icon" : "⚡", + "title" : "High throughput", + "desc" : "Thousands of concurrent requests with minimal resources." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Virtual Threads (JEP 444)", + "href" : "https://openjdk.org/jeps/444" + }, { + "title" : "HttpClient", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.net.http/java/net/http/HttpClient.html" + } ] +}, { + "id" : 51, + "slug" : "executor-try-with-resources", + "title" : "ExecutorService auto-close", + "category" : "concurrency", + "difficulty" : "beginner", + "jdkVersion" : "19", + "oldLabel" : "Java 8", + "modernLabel" : "Java 19+", + "oldApproach" : "Manual Shutdown", + "modernApproach" : "try-with-resources", + "oldCode" : "ExecutorService exec =\n Executors.newCachedThreadPool();\ntry {\n exec.submit(task);\n} finally {\n exec.shutdown();\n exec.awaitTermination(\n 1, TimeUnit.MINUTES);\n}", + "modernCode" : "try (var exec =\n Executors.newCachedThreadPool()) {\n exec.submit(task);\n}\n// auto shutdown + await on close", + "summary" : "Use try-with-resources for automatic executor shutdown.", + "explanation" : "Since Java 19, ExecutorService implements AutoCloseable. The close() method calls shutdown() and waits for tasks to complete. No more manual try/finally shutdown patterns.", + "whyModernWins" : [ { + "icon" : "🧹", + "title" : "Auto cleanup", + "desc" : "Shutdown happens automatically when the block exits." + }, { + "icon" : "🛡️", + "title" : "No leaks", + "desc" : "Executor always shuts down, even if exceptions occur." + }, { + "icon" : "📖", + "title" : "Familiar pattern", + "desc" : "Same try-with-resources used for files, connections, etc." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 19 (Sept 2022)" + }, + "docs" : [ { + "title" : "ExecutorService", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/ExecutorService.html" + } ] +}, { + "id" : 55, + "slug" : "lock-free-lazy-init", + "title" : "Lock-free lazy initialization", + "category" : "concurrency", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25 (Preview)", + "oldApproach" : "synchronized + volatile", + "modernApproach" : "StableValue", + "oldCode" : "class Config {\n private static volatile Config inst;\n static Config get() {\n if (inst == null) {\n synchronized (Config.class) {\n if (inst == null)\n inst = load();\n }\n }\n return inst;\n }\n}", + "modernCode" : "class Config {\n private static final\n StableValue INST =\n StableValue.of(Config::load);\n\n static Config get() {\n return INST.get();\n }\n}", + "summary" : "Replace double-checked locking with StableValue for lazy singletons.", + "explanation" : "StableValue encapsulates the lazy initialization pattern with correct thread safety. The JVM can optimize the read path after initialization, potentially making it faster than volatile reads.", + "whyModernWins" : [ { + "icon" : "🧹", + "title" : "No boilerplate", + "desc" : "No volatile, synchronized, or double-null-check." + }, { + "icon" : "⚡", + "title" : "Faster reads", + "desc" : "JVM can constant-fold after initialization." + }, { + "icon" : "✅", + "title" : "Provably correct", + "desc" : "No subtle ordering bugs — the JVM handles it." + } ], + "support" : { + "state" : "preview", + "description" : "Preview in JDK 25 (JEP 502, StableValue). Requires --enable-preview." + }, + "docs" : [ { + "title" : "StableValue (JEP 502)", + "href" : "https://openjdk.org/jeps/502" + }, { + "title" : "StableValue", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/StableValue.html" + } ] +}, { + "id" : 53, + "slug" : "process-api", + "title" : "Modern Process API", + "category" : "concurrency", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Runtime.exec()", + "modernApproach" : "ProcessHandle", + "oldCode" : "Process p = Runtime.getRuntime()\n .exec(\"ls -la\");\nint code = p.waitFor();\n// no way to get PID\n// no easy process info", + "modernCode" : "ProcessHandle ph =\n ProcessHandle.current();\nlong pid = ph.pid();\nph.info().command()\n .ifPresent(System.out::println);\nph.children().forEach(\n c -> System.out.println(c.pid()));", + "summary" : "Inspect and manage OS processes with ProcessHandle.", + "explanation" : "ProcessHandle provides PIDs, process info (command, arguments, start time, CPU usage), parent/child relationships, and process destruction. No more undocumented Process internals.", + "whyModernWins" : [ { + "icon" : "🔍", + "title" : "Full info", + "desc" : "Access PID, command, arguments, start time, CPU usage." + }, { + "icon" : "🌳", + "title" : "Process tree", + "desc" : "Navigate parent, children, and descendants." + }, { + "icon" : "📊", + "title" : "Monitoring", + "desc" : "onExit() returns a CompletableFuture for async monitoring." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "ProcessHandle", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ProcessHandle.html" + }, { + "title" : "Process API (JEP 102)", + "href" : "https://openjdk.org/jeps/102" + } ] +}, { + "id" : 48, + "slug" : "scoped-values", + "title" : "Scoped values", + "category" : "concurrency", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25", + "oldApproach" : "ThreadLocal", + "modernApproach" : "ScopedValue", + "oldCode" : "static final ThreadLocal CURRENT =\n new ThreadLocal<>();\nvoid handle(Request req) {\n CURRENT.set(authenticate(req));\n try { process(); }\n finally { CURRENT.remove(); }\n}", + "modernCode" : "static final ScopedValue CURRENT =\n ScopedValue.newInstance();\nvoid handle(Request req) {\n ScopedValue.where(CURRENT,\n authenticate(req)\n ).run(this::process);\n}", + "summary" : "Share data across call stacks safely without ThreadLocal pitfalls.", + "explanation" : "ScopedValue provides immutable, inheritable, scope-limited context. Unlike ThreadLocal, scoped values are automatically cleaned up, work with virtual threads, and can't be mutated by callees.", + "whyModernWins" : [ { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "Callees can read but never modify the scoped value." + }, { + "icon" : "🧹", + "title" : "Auto cleanup", + "desc" : "No manual remove() — value is scoped to the block." + }, { + "icon" : "⚡", + "title" : "Virtual-thread safe", + "desc" : "Works efficiently with millions of virtual threads." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 25 LTS (JEP 506, Sept 2025)." + }, + "docs" : [ { + "title" : "Scoped Values (JEP 487)", + "href" : "https://openjdk.org/jeps/487" + }, { + "title" : "ScopedValue", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html" + } ] +}, { + "id" : 49, + "slug" : "stable-values", + "title" : "Stable values", + "category" : "concurrency", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25 (Preview)", + "oldApproach" : "Double-Checked Locking", + "modernApproach" : "StableValue", + "oldCode" : "private volatile Logger logger;\nLogger getLogger() {\n if (logger == null) {\n synchronized (this) {\n if (logger == null)\n logger = createLogger();\n }\n }\n return logger;\n}", + "modernCode" : "private final StableValue logger =\n StableValue.of(this::createLogger);\n\nLogger getLogger() {\n return logger.get();\n}", + "summary" : "Thread-safe lazy initialization without volatile or synchronized.", + "explanation" : "StableValue provides a lazily initialized, immutable value with built-in thread safety. No double-checked locking, no volatile fields, no synchronized blocks. The JVM can even optimize the read path after initialization.", + "whyModernWins" : [ { + "icon" : "🧹", + "title" : "Zero boilerplate", + "desc" : "No volatile, synchronized, or null checks." + }, { + "icon" : "⚡", + "title" : "JVM-optimized", + "desc" : "The JVM can fold the value after initialization." + }, { + "icon" : "🛡️", + "title" : "Guaranteed once", + "desc" : "The supplier runs exactly once, even under contention." + } ], + "support" : { + "state" : "preview", + "description" : "Preview in JDK 25 (JEP 502). Requires --enable-preview." + }, + "docs" : [ { + "title" : "Stable Values (JEP 502)", + "href" : "https://openjdk.org/jeps/502" + }, { + "title" : "StableValue", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/StableValue.html" + } ] +}, { + "id" : 47, + "slug" : "structured-concurrency", + "title" : "Structured concurrency", + "category" : "concurrency", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25 (Preview)", + "oldApproach" : "Manual Thread Lifecycle", + "modernApproach" : "StructuredTaskScope", + "oldCode" : "ExecutorService exec =\n Executors.newFixedThreadPool(2);\nFuture u = exec.submit(this::fetchUser);\nFuture o = exec.submit(this::fetchOrder);\ntry {\n return combine(u.get(), o.get());\n} finally { exec.shutdown(); }", + "modernCode" : "try (var scope = new StructuredTaskScope\n .ShutdownOnFailure()) {\n var u = scope.fork(this::fetchUser);\n var o = scope.fork(this::fetchOrder);\n scope.join().throwIfFailed();\n return combine(u.get(), o.get());\n}", + "summary" : "Manage concurrent task lifetimes as a single unit of work.", + "explanation" : "Structured concurrency treats a group of concurrent tasks as one operation. If any subtask fails, the others are cancelled. The scope ensures no thread leaks and gives clear parent-child relationships.", + "whyModernWins" : [ { + "icon" : "🛡️", + "title" : "No thread leaks", + "desc" : "All forked tasks complete before the scope closes." + }, { + "icon" : "⚡", + "title" : "Fast failure", + "desc" : "ShutdownOnFailure cancels siblings if one fails." + }, { + "icon" : "📐", + "title" : "Clear structure", + "desc" : "Task lifetime matches the lexical scope in code." + } ], + "support" : { + "state" : "preview", + "description" : "Preview in JDK 25 (fifth preview, JEP 505). Requires --enable-preview." + }, + "docs" : [ { + "title" : "Structured Concurrency (JEP 499)", + "href" : "https://openjdk.org/jeps/499" + }, { + "title" : "StructuredTaskScope", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html" + } ] +}, { + "id" : 52, + "slug" : "thread-sleep-duration", + "title" : "Thread.sleep with Duration", + "category" : "concurrency", + "difficulty" : "beginner", + "jdkVersion" : "19", + "oldLabel" : "Java 8", + "modernLabel" : "Java 19+", + "oldApproach" : "Milliseconds", + "modernApproach" : "Duration", + "oldCode" : "// What unit is 5000? ms? us?\nThread.sleep(5000);\n\n// 2.5 seconds: math required\nThread.sleep(2500);", + "modernCode" : "Thread.sleep(\n Duration.ofSeconds(5)\n);\nThread.sleep(\n Duration.ofMillis(2500)\n);", + "summary" : "Use Duration for self-documenting time values.", + "explanation" : "Thread.sleep(Duration) makes the time unit explicit. No more guessing whether 5000 means milliseconds or microseconds. Works with Duration.ofSeconds, ofMillis, ofMinutes, etc.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Self-documenting", + "desc" : "Duration.ofSeconds(5) is unambiguous." + }, { + "icon" : "🛡️", + "title" : "Unit-safe", + "desc" : "No accidentally passing microseconds as milliseconds." + }, { + "icon" : "🧩", + "title" : "Composable", + "desc" : "Duration math: plus(), multipliedBy(), etc." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 19 (Sept 2022)" + }, + "docs" : [ { + "title" : "Thread.sleep(Duration)", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.html#sleep(java.time.Duration)" + } ] +}, { + "id" : 46, + "slug" : "virtual-threads", + "title" : "Virtual threads", + "category" : "concurrency", + "difficulty" : "beginner", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Platform Threads", + "modernApproach" : "Virtual Threads", + "oldCode" : "Thread thread = new Thread(() -> {\n System.out.println(\"hello\");\n});\nthread.start();\nthread.join();", + "modernCode" : "Thread.startVirtualThread(() -> {\n System.out.println(\"hello\");\n}).join();", + "summary" : "Create millions of lightweight virtual threads instead of heavy OS threads.", + "explanation" : "Virtual threads are lightweight threads managed by the JVM, not the OS. You can create millions of them without tuning thread pools. They're ideal for I/O-bound tasks like HTTP calls and database queries.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Lightweight", + "desc" : "Virtual threads use KB of memory, platform threads use MB." + }, { + "icon" : "♾️", + "title" : "Scalable", + "desc" : "Create millions of threads — no pool sizing needed." + }, { + "icon" : "🧹", + "title" : "Simple model", + "desc" : "Write blocking code that scales like async code." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Virtual Threads (JEP 444)", + "href" : "https://openjdk.org/jeps/444" + }, { + "title" : "Thread.ofVirtual()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.html#ofVirtual()" + } ] +}, { + "id" : 63, + "slug" : "deserialization-filters", + "title" : "Deserialization filters", + "category" : "io", + "difficulty" : "advanced", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Accept Everything", + "modernApproach" : "ObjectInputFilter", + "oldCode" : "// Dangerous: accepts any class\nObjectInputStream ois =\n new ObjectInputStream(input);\nObject obj = ois.readObject();\n// deserialization attacks possible!", + "modernCode" : "ObjectInputFilter filter =\n ObjectInputFilter.Config\n .createFilter(\n \"com.myapp.*;!*\"\n );\nois.setObjectInputFilter(filter);\nObject obj = ois.readObject();", + "summary" : "Restrict which classes can be deserialized to prevent attacks.", + "explanation" : "ObjectInputFilter lets you allowlist/denylist classes, limit object graph depth, array sizes, and reference counts. This defends against deserialization vulnerabilities without external libraries.", + "whyModernWins" : [ { + "icon" : "🛡️", + "title" : "Security", + "desc" : "Prevent deserialization of unexpected/malicious classes." + }, { + "icon" : "📐", + "title" : "Fine-grained", + "desc" : "Control depth, array size, references, and class patterns." + }, { + "icon" : "🏗️", + "title" : "JVM-wide", + "desc" : "Set a global filter for all deserialization in the JVM." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "ObjectInputFilter", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/io/ObjectInputFilter.html" + }, { + "title" : "Deserialization Filtering Guide", + "href" : "https://docs.oracle.com/en/java/javase/25/core/serialization-filtering1.html" + } ] +}, { + "id" : 97, + "slug" : "file-memory-mapping", + "title" : "File memory mapping", + "category" : "io", + "difficulty" : "advanced", + "jdkVersion" : "22", + "oldLabel" : "Java 8", + "modernLabel" : "Java 22+", + "oldApproach" : "MappedByteBuffer", + "modernApproach" : "MemorySegment with Arena", + "oldCode" : "try (FileChannel channel =\n FileChannel.open(path,\n StandardOpenOption.READ,\n StandardOpenOption.WRITE)) {\n MappedByteBuffer buffer =\n channel.map(\n FileChannel.MapMode.READ_WRITE,\n 0, (int) channel.size());\n // Limited to 2GB\n // Freed by GC, no control\n}", + "modernCode" : "FileChannel channel =\n FileChannel.open(path,\n StandardOpenOption.READ,\n StandardOpenOption.WRITE);\ntry (Arena arena = Arena.ofShared()) {\n MemorySegment segment =\n channel.map(\n FileChannel.MapMode.READ_WRITE,\n 0, channel.size(), arena);\n // No size limit\n // ...\n} // Deterministic cleanup", + "summary" : "Map files larger than 2GB with deterministic cleanup using MemorySegment.", + "explanation" : "The Foreign Function & Memory API (JEP 454) introduces MemorySegment for safe and efficient memory access. Unlike MappedByteBuffer, MemorySegment supports files larger than 2GB (Integer.MAX_VALUE), provides deterministic cleanup via Arena, and offers better performance with modern hardware.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "No size limit", + "desc" : "Map files larger than 2GB without workarounds." + }, { + "icon" : "🔒", + "title" : "Deterministic cleanup", + "desc" : "Arena ensures memory is freed at scope exit, not GC time." + }, { + "icon" : "⚡", + "title" : "Better performance", + "desc" : "Aligned with modern memory models and hardware." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 22 (March 2024)" + }, + "docs" : [ ] +}, { + "id" : 62, + "slug" : "files-mismatch", + "title" : "Files.mismatch()", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "12", + "oldLabel" : "Java 8", + "modernLabel" : "Java 12+", + "oldApproach" : "Manual Byte Compare", + "modernApproach" : "Files.mismatch()", + "oldCode" : "// Compare two files byte by byte\nbyte[] f1 = Files.readAllBytes(path1);\nbyte[] f2 = Files.readAllBytes(path2);\nboolean equal = Arrays.equals(f1, f2);\n// loads both files entirely into memory", + "modernCode" : "long pos = Files.mismatch(path1, path2);\n// -1 if identical\n// otherwise: position of first difference", + "summary" : "Compare two files efficiently without loading them into memory.", + "explanation" : "Files.mismatch() returns the position of the first byte that differs, or -1 if the files are identical. It reads lazily and short-circuits on the first difference.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Memory-efficient", + "desc" : "Doesn't load entire files into byte arrays." + }, { + "icon" : "🎯", + "title" : "Pinpoints difference", + "desc" : "Returns the exact byte position of the first mismatch." + }, { + "icon" : "📏", + "title" : "One call", + "desc" : "No manual byte array comparison logic." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 12 (March 2019)" + }, + "docs" : [ { + "title" : "Files.mismatch()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/file/Files.html#mismatch(java.nio.file.Path,java.nio.file.Path)" + } ] +}, { + "id" : 56, + "slug" : "http-client", + "title" : "Modern HTTP client", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "HttpURLConnection", + "modernApproach" : "HttpClient", + "oldCode" : "URL url = new URL(\"https://api.com/data\");\nHttpURLConnection con =\n (HttpURLConnection) url.openConnection();\ncon.setRequestMethod(\"GET\");\nBufferedReader in = new BufferedReader(\n new InputStreamReader(con.getInputStream()));\n// read lines, close streams...", + "modernCode" : "var client = HttpClient.newHttpClient();\nvar request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.com/data\"))\n .build();\nvar response = client.send(\n request, BodyHandlers.ofString());\nString body = response.body();", + "summary" : "Use the built-in HttpClient for clean, modern HTTP requests.", + "explanation" : "HttpClient supports HTTP/1.1 and HTTP/2, async requests, WebSocket, custom executors, and connection pooling. No more casting URLConnection or manually reading InputStreams.", + "whyModernWins" : [ { + "icon" : "📐", + "title" : "Builder API", + "desc" : "Fluent builder for requests, headers, and timeouts." + }, { + "icon" : "🔄", + "title" : "HTTP/2 support", + "desc" : "Built-in HTTP/2 with multiplexing and server push." + }, { + "icon" : "⚡", + "title" : "Async ready", + "desc" : "sendAsync() returns CompletableFuture." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "HttpClient (JEP 321)", + "href" : "https://openjdk.org/jeps/321" + }, { + "title" : "HttpClient", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.net.http/java/net/http/HttpClient.html" + } ] +}, { + "id" : 59, + "slug" : "inputstream-transferto", + "title" : "InputStream.transferTo()", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Manual Copy Loop", + "modernApproach" : "transferTo()", + "oldCode" : "byte[] buf = new byte[8192];\nint n;\nwhile ((n = input.read(buf)) != -1) {\n output.write(buf, 0, n);\n}", + "modernCode" : "input.transferTo(output);", + "summary" : "Copy an InputStream to an OutputStream in one call.", + "explanation" : "transferTo() reads all bytes from the input stream and writes them to the output stream. No buffer management, no loop. It uses an optimized internal buffer.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "One line", + "desc" : "Replace the entire read/write loop with one method call." + }, { + "icon" : "⚡", + "title" : "Optimized", + "desc" : "Internal buffer size is tuned for performance." + }, { + "icon" : "🛡️", + "title" : "No bugs", + "desc" : "No off-by-one errors in buffer management." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "InputStream.transferTo()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/io/InputStream.html#transferTo(java.io.OutputStream)" + } ] +}, { + "id" : 90, + "slug" : "io-class-console-io", + "title" : "IO class for console I/O", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25+", + "oldApproach" : "System.out / Scanner", + "modernApproach" : "IO class", + "oldCode" : "import java.util.Scanner;\n\nScanner sc = new Scanner(System.in);\nSystem.out.print(\"Name: \");\nString name = sc.nextLine();\nSystem.out.println(\"Hello, \" + name);\nsc.close();", + "modernCode" : "String name = IO.readln(\"Name: \");\nIO.println(\"Hello, \" + name);", + "summary" : "The new IO class provides simple, concise methods for console input and output.", + "explanation" : "Java 25 introduces the IO class (java.io.IO) as part of the implicitly declared classes feature. It provides static methods like println(), print(), readln(), and read() that replace the verbose combination of System.out and Scanner. IO.readln(prompt) handles both prompting and reading in a single call. The class is automatically available in compact source files and can be used in traditional classes via import.", + "whyModernWins" : [ { + "icon" : "✨", + "title" : "Dramatically simpler", + "desc" : "Two methods replace seven lines of Scanner setup, prompting, reading, and cleanup." + }, { + "icon" : "🔒", + "title" : "No resource leaks", + "desc" : "No Scanner to close — IO methods handle resource management internally." + }, { + "icon" : "🎓", + "title" : "Beginner-friendly", + "desc" : "New developers can do console I/O without learning Scanner, System.out, or import statements." + } ], + "support" : { + "state" : "preview", + "description" : "Preview in JDK 25 as part of implicitly declared classes (JEP 495)" + }, + "docs" : [ { + "title" : "Simple Source Files (JEP 495)", + "href" : "https://openjdk.org/jeps/495" + }, { + "title" : "IO", + "href" : "https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/io/IO.html" + } ] +}, { + "id" : 60, + "slug" : "path-of", + "title" : "Path.of() factory", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "Paths.get()", + "modernApproach" : "Path.of()", + "oldCode" : "Path path = Paths.get(\"src\", \"main\",\n \"java\", \"App.java\");", + "modernCode" : "Path path = Path.of(\"src\", \"main\",\n \"java\", \"App.java\");", + "summary" : "Use Path.of() — the modern factory method on the Path interface.", + "explanation" : "Path.of() is a factory method added directly to the Path interface, replacing the separate Paths utility class. It's more discoverable and consistent with List.of(), Map.of(), etc.", + "whyModernWins" : [ { + "icon" : "📐", + "title" : "Consistent API", + "desc" : "Follows the .of() factory pattern like List.of(), Set.of()." + }, { + "icon" : "📖", + "title" : "Discoverable", + "desc" : "Found on the Path type itself, not a separate Paths class." + }, { + "icon" : "🧹", + "title" : "One less class", + "desc" : "No need to import the Paths utility class." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "Path.of()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/file/Path.html#of(java.lang.String,java.lang.String...)" + } ] +}, { + "id" : 57, + "slug" : "reading-files", + "title" : "Reading files", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "BufferedReader", + "modernApproach" : "Files.readString()", + "oldCode" : "StringBuilder sb = new StringBuilder();\ntry (BufferedReader br =\n new BufferedReader(\n new FileReader(\"data.txt\"))) {\n String line;\n while ((line = br.readLine()) != null)\n sb.append(line).append(\"\\n\");\n}\nString content = sb.toString();", + "modernCode" : "String content =\n Files.readString(Path.of(\"data.txt\"));", + "summary" : "Read an entire file into a String with one line.", + "explanation" : "Files.readString() reads a file's entire content into a String. It handles encoding (UTF-8 by default) and resource cleanup. For large files, use Files.lines() for lazy streaming.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "One line", + "desc" : "Replace 8 lines of BufferedReader boilerplate." + }, { + "icon" : "🧹", + "title" : "Auto cleanup", + "desc" : "File handle is closed automatically." + }, { + "icon" : "🌐", + "title" : "UTF-8 default", + "desc" : "Correct encoding by default — no charset confusion." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "Files.readString()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/file/Files.html#readString(java.nio.file.Path)" + }, { + "title" : "Files.readAllLines()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/file/Files.html#readAllLines(java.nio.file.Path)" + } ] +}, { + "id" : 61, + "slug" : "try-with-resources-effectively-final", + "title" : "Try-with-resources improvement", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Re-declare Variable", + "modernApproach" : "Effectively Final", + "oldCode" : "Connection conn = getConnection();\n// Must re-declare in try\ntry (Connection c = conn) {\n use(c);\n}", + "modernCode" : "Connection conn = getConnection();\n// Use existing variable directly\ntry (conn) {\n use(conn);\n}", + "summary" : "Use existing effectively-final variables directly in try-with-resources.", + "explanation" : "Java 9 allows effectively-final variables to be used directly in try-with-resources without re-declaration. This is cleaner when the resource was created outside the try block.", + "whyModernWins" : [ { + "icon" : "🧹", + "title" : "No re-declaration", + "desc" : "Use the existing variable name directly." + }, { + "icon" : "📖", + "title" : "Less confusion", + "desc" : "No separate variable name inside the try block." + }, { + "icon" : "📏", + "title" : "Concise", + "desc" : "Fewer lines, same resource safety." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "The try-with-resources Statement", + "href" : "https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html" + } ] +}, { + "id" : 58, + "slug" : "writing-files", + "title" : "Writing files", + "category" : "io", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "FileWriter + BufferedWriter", + "modernApproach" : "Files.writeString()", + "oldCode" : "try (FileWriter fw =\n new FileWriter(\"out.txt\");\n BufferedWriter bw =\n new BufferedWriter(fw)) {\n bw.write(content);\n}", + "modernCode" : "Files.writeString(\n Path.of(\"out.txt\"),\n content\n);", + "summary" : "Write a String to a file with one line.", + "explanation" : "Files.writeString() writes content to a file with UTF-8 encoding by default. Options can be passed for appending, creating, etc.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "One line", + "desc" : "No writer wrapping or try-with-resources needed." + }, { + "icon" : "🛡️", + "title" : "Safe defaults", + "desc" : "UTF-8 encoding, proper file handle cleanup." + }, { + "icon" : "🔧", + "title" : "Options", + "desc" : "Pass OpenOption flags for append, create, etc." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "Files.writeString()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/file/Files.html#writeString(java.nio.file.Path,java.lang.CharSequence,java.nio.file.OpenOption...)" + } ] +}, { + "id" : 64, + "slug" : "helpful-npe", + "title" : "Helpful NullPointerExceptions", + "category" : "errors", + "difficulty" : "beginner", + "jdkVersion" : "14", + "oldLabel" : "Java 8", + "modernLabel" : "Java 14+", + "oldApproach" : "Cryptic NPE", + "modernApproach" : "Detailed NPE", + "oldCode" : "// Old NPE message:\n// \"NullPointerException\"\n// at MyApp.main(MyApp.java:42)\n// Which variable was null?!", + "modernCode" : "// Modern NPE message:\n// Cannot invoke \"String.length()\"\n// because \"user.address().city()\"\n// is null\n// Exact variable identified!", + "summary" : "JVM automatically tells you exactly which variable was null.", + "explanation" : "Helpful NPEs describe which expression was null and what operation failed. This is enabled by default since Java 14 — no code change needed, just upgrade the JDK.", + "whyModernWins" : [ { + "icon" : "🔍", + "title" : "Exact variable", + "desc" : "The message names the null variable in the chain." + }, { + "icon" : "⚡", + "title" : "Faster debugging", + "desc" : "No more guessing which of 5 chained calls was null." + }, { + "icon" : "🆓", + "title" : "Free upgrade", + "desc" : "No code changes — just run on JDK 14+." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 14 (March 2020)" + }, + "docs" : [ { + "title" : "Helpful NullPointerExceptions (JEP 358)", + "href" : "https://openjdk.org/jeps/358" + } ] +}, { + "id" : 67, + "slug" : "multi-catch", + "title" : "Multi-catch exception handling", + "category" : "errors", + "difficulty" : "beginner", + "jdkVersion" : "7", + "oldLabel" : "Pre-Java 7", + "modernLabel" : "Java 7+", + "oldApproach" : "Separate Catch Blocks", + "modernApproach" : "Multi-catch", + "oldCode" : "try {\n process();\n} catch (IOException e) {\n log(e);\n} catch (SQLException e) {\n log(e);\n} catch (ParseException e) {\n log(e);\n}", + "modernCode" : "try {\n process();\n} catch (IOException\n | SQLException\n | ParseException e) {\n log(e);\n}", + "summary" : "Catch multiple exception types in a single catch block.", + "explanation" : "Multi-catch handles multiple exception types with the same code. The exception variable is effectively final, so you can rethrow it without wrapping.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "DRY", + "desc" : "Same handling logic written once instead of three times." + }, { + "icon" : "🔄", + "title" : "Rethrowable", + "desc" : "The caught exception can be rethrown with its precise type." + }, { + "icon" : "📖", + "title" : "Scannable", + "desc" : "All handled types are visible in one place." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 7 (July 2011)" + }, + "docs" : [ { + "title" : "The catch Blocks", + "href" : "https://docs.oracle.com/javase/tutorial/essential/exceptions/catch.html" + } ] +}, { + "id" : 68, + "slug" : "null-in-switch", + "title" : "Null case in switch", + "category" : "errors", + "difficulty" : "beginner", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Guard Before Switch", + "modernApproach" : "case null", + "oldCode" : "// Must check before switch\nif (status == null) {\n return \"unknown\";\n}\nreturn switch (status) {\n case ACTIVE -> \"active\";\n case PAUSED -> \"paused\";\n default -> \"other\";\n};", + "modernCode" : "return switch (status) {\n case null -> \"unknown\";\n case ACTIVE -> \"active\";\n case PAUSED -> \"paused\";\n default -> \"other\";\n};", + "summary" : "Handle null directly as a switch case — no separate guard needed.", + "explanation" : "Pattern matching switch can match null as a case label. This eliminates the need for a null check before the switch and makes null handling explicit and visible.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Explicit", + "desc" : "null handling is visible right in the switch." + }, { + "icon" : "🛡️", + "title" : "No NPE", + "desc" : "Switch on a null value won't throw NullPointerException." + }, { + "icon" : "📐", + "title" : "All-in-one", + "desc" : "All cases including null in a single switch expression." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Pattern Matching for switch (JEP 441)", + "href" : "https://openjdk.org/jeps/441" + } ] +}, { + "id" : 65, + "slug" : "optional-chaining", + "title" : "Optional chaining", + "category" : "errors", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Nested Null Checks", + "modernApproach" : "Optional Pipeline", + "oldCode" : "String city = null;\nif (user != null) {\n Address addr = user.getAddress();\n if (addr != null) {\n city = addr.getCity();\n }\n}\nif (city == null) city = \"Unknown\";", + "modernCode" : "String city = Optional.ofNullable(user)\n .map(User::address)\n .map(Address::city)\n .orElse(\"Unknown\");", + "summary" : "Replace nested null checks with an Optional pipeline.", + "explanation" : "Optional.map() chains through nullable values, short-circuiting on the first null. orElse() provides the default. This eliminates pyramid-of-doom null checking.", + "whyModernWins" : [ { + "icon" : "🔗", + "title" : "Chainable", + "desc" : "Each .map() step handles null transparently." + }, { + "icon" : "📖", + "title" : "Linear flow", + "desc" : "Read left-to-right instead of nested if-blocks." + }, { + "icon" : "🛡️", + "title" : "NPE-proof", + "desc" : "null is handled at each step — no crash possible." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 8+ (improved in 9+)" + }, + "docs" : [ { + "title" : "Optional", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Optional.html" + } ] +}, { + "id" : 88, + "slug" : "optional-orelsethrow", + "title" : "Optional.orElseThrow() without supplier", + "category" : "errors", + "difficulty" : "beginner", + "jdkVersion" : "10", + "oldLabel" : "Java 8", + "modernLabel" : "Java 10+", + "oldApproach" : "get() or orElseThrow(supplier)", + "modernApproach" : "orElseThrow()", + "oldCode" : "// Risky: get() throws if empty, no clear intent\nString value = optional.get();\n\n// Verbose: supplier just for NoSuchElementException\nString value = optional\n .orElseThrow(NoSuchElementException::new);", + "modernCode" : "// Clear intent: throws NoSuchElementException if empty\nString value = optional.orElseThrow();", + "summary" : "Use Optional.orElseThrow() as a clearer, intent-revealing alternative to get().", + "explanation" : "Optional.get() is widely considered a code smell because it hides the possibility of failure. The no-arg orElseThrow(), added in Java 10, does exactly the same thing but makes the intent explicit: the developer expects a value and wants an exception if absent.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Self-documenting", + "desc" : "orElseThrow() clearly signals that absence is unexpected." + }, { + "icon" : "🔒", + "title" : "Avoids get()", + "desc" : "Static analysis tools flag get() as risky; orElseThrow() is idiomatic." + }, { + "icon" : "⚡", + "title" : "Less boilerplate", + "desc" : "No need to pass a supplier for the default NoSuchElementException." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 10 (March 2018)." + }, + "docs" : [ { + "title" : "Optional.orElseThrow()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Optional.html#orElseThrow()" + } ] +}, { + "id" : 69, + "slug" : "record-based-errors", + "title" : "Record-based error responses", + "category" : "errors", + "difficulty" : "intermediate", + "jdkVersion" : "16", + "oldLabel" : "Java 8", + "modernLabel" : "Java 16+", + "oldApproach" : "Map or Verbose Class", + "modernApproach" : "Error Records", + "oldCode" : "// Verbose error class\npublic class ErrorResponse {\n private final int code;\n private final String message;\n // constructor, getters, equals,\n // hashCode, toString...\n}", + "modernCode" : "public record ApiError(\n int code,\n String message,\n Instant timestamp\n) {\n public ApiError(int code, String msg) {\n this(code, msg, Instant.now());\n }\n}", + "summary" : "Use records for concise, immutable error response types.", + "explanation" : "Records are perfect for error responses — they're immutable, have built-in equals/hashCode for comparison, and toString for logging. Custom constructors add validation or defaults.", + "whyModernWins" : [ { + "icon" : "📏", + "title" : "Concise", + "desc" : "Define error types in 3 lines instead of 30." + }, { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "Error data can't be accidentally modified after creation." + }, { + "icon" : "📋", + "title" : "Auto toString", + "desc" : "Perfect for logging — shows all fields automatically." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 16 (March 2021)" + }, + "docs" : [ { + "title" : "Records (JEP 395)", + "href" : "https://openjdk.org/jeps/395" + }, { + "title" : "Record class", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Record.html" + } ] +}, { + "id" : 66, + "slug" : "require-nonnull-else", + "title" : "Objects.requireNonNullElse()", + "category" : "errors", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Ternary Null Check", + "modernApproach" : "requireNonNullElse()", + "oldCode" : "String name = input != null\n ? input\n : \"default\";\n// easy to get the order wrong", + "modernCode" : "String name = Objects\n .requireNonNullElse(\n input, \"default\"\n );", + "summary" : "Get a non-null value with a clear default, no ternary needed.", + "explanation" : "requireNonNullElse returns the first argument if non-null, otherwise the second. The default itself cannot be null — it throws NPE if both are null, catching bugs early.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Clear intent", + "desc" : "Method name describes exactly what it does." + }, { + "icon" : "🛡️", + "title" : "Null-safe default", + "desc" : "The default value is also checked for null." + }, { + "icon" : "📏", + "title" : "Readable", + "desc" : "Better than ternary for simple null-or-default logic." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Objects.requireNonNullElse()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Objects.html#requireNonNullElse(T,T)" + }, { + "title" : "Objects.requireNonNullElseGet()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Objects.html#requireNonNullElseGet(T,java.util.function.Supplier)" + } ] +}, { + "id" : 72, + "slug" : "date-formatting", + "title" : "Date formatting", + "category" : "datetime", + "difficulty" : "beginner", + "jdkVersion" : "8", + "oldLabel" : "Pre-Java 8", + "modernLabel" : "Java 8+", + "oldApproach" : "SimpleDateFormat", + "modernApproach" : "DateTimeFormatter", + "oldCode" : "// Not thread-safe!\nSimpleDateFormat sdf =\n new SimpleDateFormat(\"yyyy-MM-dd\");\nString formatted = sdf.format(date);\n// Must synchronize for concurrent use", + "modernCode" : "DateTimeFormatter fmt =\n DateTimeFormatter.ofPattern(\n \"uuuu-MM-dd\");\nString formatted =\n LocalDate.now().format(fmt);\n// Thread-safe, immutable", + "summary" : "Format dates with thread-safe, immutable DateTimeFormatter.", + "explanation" : "DateTimeFormatter is immutable and thread-safe, unlike SimpleDateFormat. It can be stored as a constant and shared. Predefined formatters like ISO_LOCAL_DATE are available for common formats.", + "whyModernWins" : [ { + "icon" : "🛡️", + "title" : "Thread-safe", + "desc" : "Share formatters across threads without synchronization." + }, { + "icon" : "📋", + "title" : "Built-in formats", + "desc" : "ISO_LOCAL_DATE, ISO_INSTANT, etc. for standard formats." + }, { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "Store as static final constants safely." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 8 (March 2014)" + }, + "docs" : [ { + "title" : "DateTimeFormatter", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/format/DateTimeFormatter.html" + } ] +}, { + "id" : 71, + "slug" : "duration-and-period", + "title" : "Duration and Period", + "category" : "datetime", + "difficulty" : "beginner", + "jdkVersion" : "8", + "oldLabel" : "Pre-Java 8", + "modernLabel" : "Java 8+", + "oldApproach" : "Millisecond Math", + "modernApproach" : "Duration / Period", + "oldCode" : "// How many days between two dates?\nlong diff = date2.getTime()\n - date1.getTime();\nlong days = diff\n / (1000 * 60 * 60 * 24);\n// ignores DST, leap seconds", + "modernCode" : "long days = ChronoUnit.DAYS\n .between(date1, date2);\nPeriod period = Period.between(\n date1, date2);\nDuration elapsed = Duration.between(\n time1, time2);", + "summary" : "Calculate time differences with type-safe Duration and Period.", + "explanation" : "Duration is for time-based amounts (hours, minutes, seconds). Period is for date-based amounts (years, months, days). ChronoUnit.between() for simple differences. All handle edge cases correctly.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Type-safe", + "desc" : "Duration for time, Period for dates — no confusion." + }, { + "icon" : "🛡️", + "title" : "Correct math", + "desc" : "Handles DST transitions, leap years, and leap seconds." + }, { + "icon" : "📖", + "title" : "Readable", + "desc" : "ChronoUnit.DAYS.between() reads like English." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 8 (March 2014)" + }, + "docs" : [ { + "title" : "Duration", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/Duration.html" + }, { + "title" : "Period", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/Period.html" + } ] +}, { + "id" : 75, + "slug" : "hex-format", + "title" : "HexFormat", + "category" : "datetime", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "Java 8", + "modernLabel" : "Java 17+", + "oldApproach" : "Manual Hex Conversion", + "modernApproach" : "HexFormat", + "oldCode" : "// Pad to 2 digits, uppercase\nString hex = String.format(\n \"%02X\", byteValue);\n// Parse hex string\nint val = Integer.parseInt(\n \"FF\", 16);", + "modernCode" : "HexFormat hex = HexFormat.of()\n .withUpperCase();\nString s = hex.toHexDigits(\n byteValue);\nbyte[] bytes =\n hex.parseHex(\"48656C6C6F\");", + "summary" : "Convert between hex strings and byte arrays with HexFormat.", + "explanation" : "HexFormat provides bidirectional hex encoding/decoding for bytes, ints, and arrays. Configure delimiters, prefix, suffix, and case. No more manual formatting or parsing.", + "whyModernWins" : [ { + "icon" : "📐", + "title" : "Bidirectional", + "desc" : "Convert bytes→hex and hex→bytes with one API." + }, { + "icon" : "🔧", + "title" : "Configurable", + "desc" : "Delimiters, prefix, suffix, upper/lower case." + }, { + "icon" : "📦", + "title" : "Array support", + "desc" : "Encode/decode entire byte arrays at once." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 17 LTS (Sept 2021)" + }, + "docs" : [ { + "title" : "HexFormat", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/HexFormat.html" + } ] +}, { + "id" : 73, + "slug" : "instant-precision", + "title" : "Instant with nanosecond precision", + "category" : "datetime", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Milliseconds", + "modernApproach" : "Nanoseconds", + "oldCode" : "// Millisecond precision only\nlong millis =\n System.currentTimeMillis();\n// 1708012345678", + "modernCode" : "// Microsecond/nanosecond precision\nInstant now = Instant.now();\n// 2025-02-15T20:12:25.678901234Z\nlong nanos = now.getNano();", + "summary" : "Get timestamps with microsecond or nanosecond precision.", + "explanation" : "Java 9 improved the clock resolution so Instant.now() captures microsecond precision on most platforms (nanosecond on some). The old currentTimeMillis() only gives milliseconds.", + "whyModernWins" : [ { + "icon" : "🎯", + "title" : "Higher precision", + "desc" : "Microsecond/nanosecond vs millisecond timestamps." + }, { + "icon" : "📐", + "title" : "Type-safe", + "desc" : "Instant carries its precision — no ambiguous longs." + }, { + "icon" : "🌐", + "title" : "UTC-based", + "desc" : "Instant is always in UTC — no timezone confusion." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "Instant", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/Instant.html" + }, { + "title" : "Clock", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/Clock.html" + } ] +}, { + "id" : 70, + "slug" : "java-time-basics", + "title" : "java.time API basics", + "category" : "datetime", + "difficulty" : "beginner", + "jdkVersion" : "8", + "oldLabel" : "Pre-Java 8", + "modernLabel" : "Java 8+", + "oldApproach" : "Date + Calendar", + "modernApproach" : "java.time.*", + "oldCode" : "// Mutable, confusing, zero-indexed months\nCalendar cal = Calendar.getInstance();\ncal.set(2025, 0, 15); // January = 0!\nDate date = cal.getTime();\n// not thread-safe", + "modernCode" : "LocalDate date = LocalDate.of(\n 2025, Month.JANUARY, 15);\nLocalTime time = LocalTime.of(14, 30);\nInstant now = Instant.now();\n// immutable, thread-safe", + "summary" : "Use immutable, clear date/time types instead of Date and Calendar.", + "explanation" : "java.time provides LocalDate, LocalTime, LocalDateTime, Instant, ZonedDateTime — all immutable and thread-safe. Months are 1-indexed. No more Calendar.JANUARY = 0 confusion.", + "whyModernWins" : [ { + "icon" : "🔒", + "title" : "Immutable", + "desc" : "Date/time values can't be accidentally modified." + }, { + "icon" : "📖", + "title" : "Clear API", + "desc" : "Month.JANUARY, not 0. DayOfWeek.MONDAY, not 2." + }, { + "icon" : "🛡️", + "title" : "Thread-safe", + "desc" : "No synchronization needed — share freely across threads." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 8 (March 2014)" + }, + "docs" : [ { + "title" : "LocalDate", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/LocalDate.html" + }, { + "title" : "LocalTime", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/LocalTime.html" + }, { + "title" : "LocalDateTime", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/LocalDateTime.html" + } ] +}, { + "id" : 74, + "slug" : "math-clamp", + "title" : "Math.clamp()", + "category" : "datetime", + "difficulty" : "beginner", + "jdkVersion" : "21", + "oldLabel" : "Java 8", + "modernLabel" : "Java 21+", + "oldApproach" : "Nested min/max", + "modernApproach" : "Math.clamp()", + "oldCode" : "// Clamp value between min and max\nint clamped =\n Math.min(Math.max(value, 0), 100);\n// or: min and max order confusion", + "modernCode" : "int clamped =\n Math.clamp(value, 0, 100);\n// value constrained to [0, 100]", + "summary" : "Clamp a value between bounds with a single clear call.", + "explanation" : "Math.clamp(value, min, max) constrains a value to the range [min, max]. Clearer than nested Math.min/Math.max and available for int, long, float, and double.", + "whyModernWins" : [ { + "icon" : "📖", + "title" : "Self-documenting", + "desc" : "clamp(value, min, max) is unambiguous." + }, { + "icon" : "🛡️", + "title" : "Less error-prone", + "desc" : "No more swapping min/max order by accident." + }, { + "icon" : "🎯", + "title" : "All numeric types", + "desc" : "Works with int, long, float, and double." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 21 LTS (Sept 2023)" + }, + "docs" : [ { + "title" : "Math.clamp()", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Math.html#clamp(long,int,int)" + } ] +}, { + "id" : 77, + "slug" : "key-derivation-functions", + "title" : "Key Derivation Functions", + "category" : "security", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25", + "oldApproach" : "Manual PBKDF2", + "modernApproach" : "KDF API", + "oldCode" : "SecretKeyFactory factory =\n SecretKeyFactory.getInstance(\n \"PBKDF2WithHmacSHA256\");\nKeySpec spec = new PBEKeySpec(\n password, salt, 10000, 256);\nSecretKey key =\n factory.generateSecret(spec);", + "modernCode" : "KDF kdf = KDF.getInstance(\"HKDF-SHA256\");\nSecretKey key = kdf.deriveKey(\n \"AES\",\n KDF.HKDFParameterSpec\n .ofExtract()\n .addIKM(inputKey)\n .addSalt(salt)\n .thenExpand(info, 32)\n .build()\n);", + "summary" : "Derive cryptographic keys using the standard KDF API.", + "explanation" : "The KDF API provides a standard interface for key derivation functions including HKDF. It replaces the awkward SecretKeyFactory + PBEKeySpec pattern with a clean builder API.", + "whyModernWins" : [ { + "icon" : "📐", + "title" : "Clean API", + "desc" : "Builder pattern instead of awkward KeySpec constructors." + }, { + "icon" : "🔧", + "title" : "HKDF support", + "desc" : "Modern HKDF algorithm alongside PBKDF2." + }, { + "icon" : "🛡️", + "title" : "Standard", + "desc" : "Unified API for all key derivation algorithms." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 25 LTS (JEP 510, Sept 2025)." + }, + "docs" : [ { + "title" : "Key Derivation Functions (JEP 478)", + "href" : "https://openjdk.org/jeps/478" + } ] +}, { + "id" : 76, + "slug" : "pem-encoding", + "title" : "PEM encoding/decoding", + "category" : "security", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25 (Preview)", + "oldApproach" : "Manual Base64 + Headers", + "modernApproach" : "PEM API", + "oldCode" : "String pem = \"-----BEGIN CERTIFICATE-----\\n\"\n + Base64.getMimeEncoder()\n .encodeToString(\n cert.getEncoded())\n + \"\\n-----END CERTIFICATE-----\";", + "modernCode" : "// Encode to PEM\nString pem = PEMEncoder.of()\n .encodeToString(cert);\n// Decode from PEM\nvar cert = PEMDecoder.of()\n .decode(pemString);", + "summary" : "Encode and decode PEM-formatted cryptographic objects natively.", + "explanation" : "The PEM API provides standard encoding/decoding for certificates, keys, and other cryptographic objects in PEM format. No more manual Base64 wrapping with BEGIN/END headers.", + "whyModernWins" : [ { + "icon" : "🧹", + "title" : "No manual Base64", + "desc" : "PEM headers, line wrapping, and Base64 handled automatically." + }, { + "icon" : "🔄", + "title" : "Bidirectional", + "desc" : "Encode to PEM and decode from PEM with one API." + }, { + "icon" : "🛡️", + "title" : "Standard format", + "desc" : "Produces RFC 7468-compliant PEM output." + } ], + "support" : { + "state" : "preview", + "description" : "Preview in JDK 25 (JEP 470). Requires --enable-preview." + }, + "docs" : [ { + "title" : "PEM Encodings of Certificates (JEP 470)", + "href" : "https://openjdk.org/jeps/470" + } ] +}, { + "id" : 89, + "slug" : "random-generator", + "title" : "RandomGenerator interface", + "category" : "security", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "Java 8", + "modernLabel" : "Java 17+", + "oldApproach" : "new Random() / ThreadLocalRandom", + "modernApproach" : "RandomGenerator factory", + "oldCode" : "// Hard-coded to one algorithm\nRandom rng = new Random();\nint value = rng.nextInt(100);\n\n// Or thread-local, but still locked in\nint value = ThreadLocalRandom.current()\n .nextInt(100);", + "modernCode" : "// Algorithm-agnostic via factory\nvar rng = RandomGenerator.of(\"L64X128MixRandom\");\nint value = rng.nextInt(100);\n\n// Or get a splittable generator\nvar rng = RandomGeneratorFactory\n .of(\"L64X128MixRandom\").create();", + "summary" : "Use the RandomGenerator interface to choose random number algorithms by name without coupling to a specific class.", + "explanation" : "JDK 17 introduced RandomGenerator as a common interface for all RNG implementations. Instead of hard-coding new Random() or ThreadLocalRandom, you can select algorithms by name via a factory, making it easy to swap between algorithms optimized for different use cases (speed, statistical quality, splittability).", + "whyModernWins" : [ { + "icon" : "🔧", + "title" : "Algorithm-agnostic", + "desc" : "Choose the best RNG algorithm by name without changing code structure." + }, { + "icon" : "⚡", + "title" : "Better algorithms", + "desc" : "Access to modern LXM generators with superior statistical properties." + }, { + "icon" : "🔗", + "title" : "Unified API", + "desc" : "One interface covers Random, ThreadLocalRandom, SplittableRandom, and more." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 17 (September 2021, JEP 356)." + }, + "docs" : [ { + "title" : "RandomGenerator", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/random/RandomGenerator.html" + }, { + "title" : "New Random Generator API (JEP 356)", + "href" : "https://openjdk.org/jeps/356" + } ] +}, { + "id" : 78, + "slug" : "strong-random", + "title" : "Strong random generation", + "category" : "security", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "new SecureRandom()", + "modernApproach" : "getInstanceStrong()", + "oldCode" : "// Default algorithm — may not be\n// the strongest available\nSecureRandom random =\n new SecureRandom();\nbyte[] bytes = new byte[32];\nrandom.nextBytes(bytes);", + "modernCode" : "// Platform's strongest algorithm\nSecureRandom random =\n SecureRandom.getInstanceStrong();\nbyte[] bytes = new byte[32];\nrandom.nextBytes(bytes);", + "summary" : "Get the platform's strongest SecureRandom implementation.", + "explanation" : "getInstanceStrong() returns the SecureRandom implementation configured as the strongest on the platform. This is controlled by the securerandom.strongAlgorithms security property.", + "whyModernWins" : [ { + "icon" : "🛡️", + "title" : "Strongest available", + "desc" : "Automatically selects the best algorithm for the platform." + }, { + "icon" : "📖", + "title" : "Explicit intent", + "desc" : "Clearly communicates that strong randomness is required." + }, { + "icon" : "🔧", + "title" : "Configurable", + "desc" : "Administrators can change the strong algorithm via security properties." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "SecureRandom", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/security/SecureRandom.html" + } ] +}, { + "id" : 79, + "slug" : "tls-default", + "title" : "TLS 1.3 by default", + "category" : "security", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "Manual TLS Config", + "modernApproach" : "TLS 1.3 Default", + "oldCode" : "SSLContext ctx =\n SSLContext.getInstance(\"TLSv1.2\");\nctx.init(null, trustManagers, null);\nSSLSocketFactory factory =\n ctx.getSocketFactory();\n// Must specify protocol version", + "modernCode" : "// TLS 1.3 is the default!\nvar client = HttpClient.newBuilder()\n .sslContext(SSLContext.getDefault())\n .build();\n// Already using TLS 1.3", + "summary" : "TLS 1.3 is enabled by default — no explicit protocol configuration needed.", + "explanation" : "Java 11 added TLS 1.3 support and made it the preferred protocol. The HttpClient uses it automatically. No more manually specifying protocol versions for secure connections.", + "whyModernWins" : [ { + "icon" : "🛡️", + "title" : "More secure", + "desc" : "TLS 1.3 removes obsolete cipher suites and handshake patterns." + }, { + "icon" : "⚡", + "title" : "Faster handshake", + "desc" : "TLS 1.3 completes in one round trip vs two." + }, { + "icon" : "🆓", + "title" : "Zero config", + "desc" : "Secure by default — no explicit protocol selection needed." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "SSLContext", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/javax/net/ssl/SSLContext.html" + }, { + "title" : "Java Security Guide", + "href" : "https://docs.oracle.com/en/java/javase/25/security/java-security-overview1.html" + } ] +}, { + "id" : 85, + "slug" : "aot-class-preloading", + "title" : "AOT class preloading", + "category" : "tooling", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25", + "oldApproach" : "Cold Start Every Time", + "modernApproach" : "AOT Cache", + "oldCode" : "// Every startup:\n// - Load 10,000+ classes\n// - Verify bytecode\n// - JIT compile hot paths\n// Startup: 2-5 seconds", + "modernCode" : "// Training run:\n$ java -XX:AOTCacheOutput=app.aot \\\n -cp app.jar com.App\n// Production:\n$ java -XX:AOTCache=app.aot \\\n -cp app.jar com.App", + "summary" : "Cache class loading and compilation for instant startup.", + "explanation" : "AOT class preloading caches loaded and linked classes from a training run. On subsequent starts, classes are loaded from the cache, skipping verification and linking. Combined with AOT compilation, this dramatically reduces startup time.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Faster startup", + "desc" : "Skip class loading, verification, and linking." + }, { + "icon" : "📦", + "title" : "Cached state", + "desc" : "Training run captures the ideal class state." + }, { + "icon" : "🔧", + "title" : "No code changes", + "desc" : "Works with existing applications — just add JVM flags." + } ], + "support" : { + "state" : "available", + "description" : "Available as a standard feature in JDK 25 LTS (JEPs 514/515, Sept 2025)." + }, + "docs" : [ { + "title" : "Ahead-of-Time Command-Line Ergonomics (JEP 514)", + "href" : "https://openjdk.org/jeps/514" + }, { + "title" : "AOT Cache (JEP 515)", + "href" : "https://openjdk.org/jeps/515" + } ] +}, { + "id" : 91, + "slug" : "built-in-http-server", + "title" : "Built-in HTTP server", + "category" : "tooling", + "difficulty" : "beginner", + "jdkVersion" : "18", + "oldLabel" : "Java 8", + "modernLabel" : "Java 18+", + "oldApproach" : "External Server / Framework", + "modernApproach" : "jwebserver CLI", + "oldCode" : "// Install and configure a web server\n// (Apache, Nginx, or embedded Jetty)\n\n// Or write boilerplate with com.sun.net.httpserver\nHttpServer server = HttpServer.create(\n new InetSocketAddress(8080), 0);\nserver.createContext(\"/\", exchange -> { ... });\nserver.start();", + "modernCode" : "// Terminal: serve current directory\n$ jwebserver\n\n// Or use the API (JDK 18+)\nvar server = SimpleFileServer.createFileServer(\n new InetSocketAddress(8080),\n Path.of(\".\"),\n OutputLevel.VERBOSE);\nserver.start();", + "summary" : "Java 18 includes a built-in minimal HTTP server for prototyping and file serving.", + "explanation" : "JDK 18 added a simple, zero-dependency HTTP file server accessible via the jwebserver command-line tool or the SimpleFileServer API. It serves static files from a given directory with no configuration needed. The CLI tool is ideal for quick prototyping, testing, and ad-hoc file sharing — no external dependencies or frameworks required. The API allows programmatic use with customizable handlers and output levels.", + "whyModernWins" : [ { + "icon" : "🚀", + "title" : "Zero setup", + "desc" : "Run jwebserver in any directory — no installation, config, or dependencies needed." + }, { + "icon" : "📦", + "title" : "Built into the JDK", + "desc" : "Ships with every JDK 18+ installation, always available on any machine with Java." + }, { + "icon" : "🧪", + "title" : "Great for prototyping", + "desc" : "Serve static files instantly for testing HTML, APIs, or front-end development." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 18 (March 2022)" + }, + "docs" : [ { + "title" : "Simple Web Server (JEP 408)", + "href" : "https://openjdk.org/jeps/408" + }, { + "title" : "SimpleFileServer", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/api/jdk.httpserver/com/sun/net/httpserver/SimpleFileServer.html" + }, { + "title" : "jwebserver Tool", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/specs/man/jwebserver.html" + } ] +}, { + "id" : 84, + "slug" : "compact-object-headers", + "title" : "Compact object headers", + "category" : "tooling", + "difficulty" : "advanced", + "jdkVersion" : "25", + "oldLabel" : "Java 8", + "modernLabel" : "Java 25", + "oldApproach" : "128-bit Headers", + "modernApproach" : "64-bit Headers", + "oldCode" : "// Default: 128-bit object header\n// = 16 bytes overhead per object\n// A boolean field object = 32 bytes!\n// Mark word (64) + Klass pointer (64)", + "modernCode" : "// -XX:+UseCompactObjectHeaders\n// 64-bit object header\n// = 8 bytes overhead per object\n// 50% less header memory\n// More objects fit in cache", + "summary" : "Cut object header size in half for better memory density and cache usage.", + "explanation" : "Compact object headers reduce the per-object overhead from 128 bits to 64 bits on 64-bit platforms. This saves memory and improves cache utilization, especially for applications with many small objects.", + "whyModernWins" : [ { + "icon" : "📦", + "title" : "50% smaller headers", + "desc" : "8 bytes instead of 16 per object." + }, { + "icon" : "⚡", + "title" : "Better cache usage", + "desc" : "More objects fit in CPU cache lines." + }, { + "icon" : "📊", + "title" : "Higher density", + "desc" : "Fit more objects in the same heap size." + } ], + "support" : { + "state" : "available", + "description" : "Finalized in JDK 25 LTS (JEP 519, Sept 2025)." + }, + "docs" : [ { + "title" : "Compact Object Headers (JEP 519)", + "href" : "https://openjdk.org/jeps/519" + } ] +}, { + "id" : 83, + "slug" : "jfr-profiling", + "title" : "JFR for profiling", + "category" : "tooling", + "difficulty" : "intermediate", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "External Profiler", + "modernApproach" : "Java Flight Recorder", + "oldCode" : "// Install VisualVM / YourKit / JProfiler\n// Attach to running process\n// Configure sampling\n// Export and analyze\n// External tool required", + "modernCode" : "// Start with profiling enabled\n$ java -XX:StartFlightRecording=\n filename=rec.jfr MyApp\n\n// Or attach to running app:\n$ jcmd JFR.start", + "summary" : "Profile any Java app with the built-in Flight Recorder — no external tools.", + "explanation" : "Java Flight Recorder (JFR) is a low-overhead profiling tool built into the JVM. It captures events for CPU, memory, GC, I/O, threads, and custom events with minimal performance impact (~1%).", + "whyModernWins" : [ { + "icon" : "🆓", + "title" : "Built-in", + "desc" : "No external profiler to install or license." + }, { + "icon" : "⚡", + "title" : "Low overhead", + "desc" : "~1% performance impact — safe for production." + }, { + "icon" : "📊", + "title" : "Rich events", + "desc" : "CPU, memory, GC, threads, I/O, locks, and custom events." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9/11 (open-sourced in 11)" + }, + "docs" : [ { + "title" : "JDK Flight Recorder", + "href" : "https://docs.oracle.com/en/java/javase/25/docs/specs/man/jfr.html" + }, { + "title" : "Flight Recorder API (JEP 328)", + "href" : "https://openjdk.org/jeps/328" + } ] +}, { + "id" : 80, + "slug" : "jshell-prototyping", + "title" : "JShell for prototyping", + "category" : "tooling", + "difficulty" : "beginner", + "jdkVersion" : "9", + "oldLabel" : "Java 8", + "modernLabel" : "Java 9+", + "oldApproach" : "Create File + Compile + Run", + "modernApproach" : "jshell REPL", + "oldCode" : "// 1. Create Test.java\n// 2. javac Test.java\n// 3. java Test\n// Just to test one expression!", + "modernCode" : "$ jshell\njshell> \"hello\".chars().count()\n$1 ==> 5\njshell> List.of(1,2,3).reversed()\n$2 ==> [3, 2, 1]", + "summary" : "Try Java expressions interactively without creating files.", + "explanation" : "JShell is a Read-Eval-Print Loop for Java. Test expressions, experiment with APIs, and prototype code without creating files, compiling, or writing a main method. Tab completion and inline docs included.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "Instant feedback", + "desc" : "Type an expression, see the result immediately." + }, { + "icon" : "📝", + "title" : "No files needed", + "desc" : "No .java files, no compilation step." + }, { + "icon" : "🔍", + "title" : "API exploration", + "desc" : "Tab completion helps discover methods and parameters." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 9 (Sept 2017)" + }, + "docs" : [ { + "title" : "JShell Tool", + "href" : "https://docs.oracle.com/en/java/javase/25/jshell/introduction-jshell.html" + }, { + "title" : "JShell (JEP 222)", + "href" : "https://openjdk.org/jeps/222" + } ] +}, { + "id" : 111, + "slug" : "junit6-with-jspecify", + "title" : "JUnit 6 with JSpecify null safety", + "category" : "tooling", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "JUnit 5", + "modernLabel" : "JUnit 6", + "oldApproach" : "Unannotated API", + "modernApproach" : "@NullMarked API", + "oldCode" : "import org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass UserServiceTest {\n\n // JUnit 5: no null contracts on the API\n // Can assertEquals() accept null? Check source...\n // Does fail(String) allow null message? Unknown.\n\n @Test\n void findUser_found() {\n // Is result nullable? API doesn't say\n User result = service.findById(\"u1\");\n assertNotNull(result);\n assertEquals(\"Alice\", result.name());\n }\n\n @Test\n void findUser_notFound() {\n // Hope this returns null, not throws...\n assertNull(service.findById(\"missing\"));\n }\n}", + "modernCode" : "import org.junit.jupiter.api.Test;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@NullMarked // all refs non-null unless @Nullable\nclass UserServiceTest {\n\n // JUnit 6 API is @NullMarked:\n // assertNull(@Nullable Object actual)\n // assertEquals(@Nullable Object, @Nullable Object)\n // fail(@Nullable String message)\n\n @Test\n void findUser_found() {\n // IDE warns: findById returns @Nullable User\n @Nullable User result = service.findById(\"u1\");\n assertNotNull(result); // narrows type to non-null\n assertEquals(\"Alice\", result.name()); // safe\n }\n\n @Test\n void findUser_notFound() {\n @Nullable User result = service.findById(\"missing\");\n assertNull(result); // IDE confirms null expectation\n }\n}", + "summary" : "JUnit 6 adopts JSpecify @NullMarked, making null contracts explicit across its assertion API.", + "explanation" : "JUnit 5 shipped without standardized nullability annotations, leaving developers to guess whether assertion parameters or return values could be null. JUnit 6 adopts JSpecify across its entire module: the @NullMarked annotation makes all unannotated types non-null by default, and @Nullable marks the exceptions. The Assertions class explicitly annotates parameters such as assertNull(@Nullable Object actual) and fail(@Nullable String message), so IDEs and static analyzers like NullAway and Error Prone can catch null misuse at compile time instead of at runtime.", + "whyModernWins" : [ { + "icon" : "📜", + "title" : "Explicit contracts", + "desc" : "@NullMarked on the JUnit 6 module documents null semantics directly in the API — no source-reading required." + }, { + "icon" : "🛡️", + "title" : "Compile-time safety", + "desc" : "IDEs and analyzers warn when null is passed where non-null is expected, catching bugs before tests run." + }, { + "icon" : "🌐", + "title" : "Ecosystem standard", + "desc" : "JSpecify is adopted by Spring, Guava, and others — consistent null semantics across your whole stack." + } ], + "support" : { + "state" : "available", + "description" : "Available since JUnit 6.0 (October 2025, requires Java 17+)" + }, + "docs" : [ { + "title" : "JUnit 6 Assertions API", + "href" : "https://docs.junit.org/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html" + }, { + "title" : "JSpecify Nullness User Guide", + "href" : "https://jspecify.dev/docs/user-guide/" + }, { + "title" : "Upgrading to JUnit 6.0", + "href" : "https://github.com/junit-team/junit-framework/wiki/Upgrading-to-JUnit-6.0/Core-Principles" + } ] +}, { + "id" : 82, + "slug" : "multi-file-source", + "title" : "Multi-file source launcher", + "category" : "tooling", + "difficulty" : "intermediate", + "jdkVersion" : "22", + "oldLabel" : "Java 8", + "modernLabel" : "Java 22+", + "oldApproach" : "Compile All First", + "modernApproach" : "Source Launcher", + "oldCode" : "$ javac *.java\n$ java Main\n// Must compile all files first\n// Need a build tool for dependencies", + "modernCode" : "$ java Main.java\n// Automatically finds and compiles\n// other source files referenced\n// by Main.java", + "summary" : "Launch multi-file programs without an explicit compile step.", + "explanation" : "Java 22+ can automatically compile referenced source files when launching from a .java file. This makes small multi-file programs as easy to run as scripts, without needing Maven or Gradle.", + "whyModernWins" : [ { + "icon" : "🚀", + "title" : "Zero setup", + "desc" : "No build tool needed for small multi-file programs." + }, { + "icon" : "🔗", + "title" : "Auto-resolve", + "desc" : "Referenced classes are found and compiled automatically." + }, { + "icon" : "📝", + "title" : "Script-like", + "desc" : "Run multi-file programs like scripts." + } ], + "support" : { + "state" : "available", + "description" : "Available since JDK 22 (March 2024)" + }, + "docs" : [ { + "title" : "Launch Multi-File Source-Code Programs (JEP 458)", + "href" : "https://openjdk.org/jeps/458" + } ] +}, { + "id" : 81, + "slug" : "single-file-execution", + "title" : "Single-file execution", + "category" : "tooling", + "difficulty" : "beginner", + "jdkVersion" : "11", + "oldLabel" : "Java 8", + "modernLabel" : "Java 11+", + "oldApproach" : "Two-Step Compile", + "modernApproach" : "Direct Launch", + "oldCode" : "$ javac HelloWorld.java\n$ java HelloWorld\n// Two steps every time", + "modernCode" : "$ java HelloWorld.java\n// Compiles and runs in one step\n// Also works with shebangs:\n#!/usr/bin/java --source 25", + "summary" : "Run single-file Java programs directly without javac.", + "explanation" : "The Java launcher can compile and run a single source file in one command. Combined with shebang support on Unix, Java files can work as scripts. No separate compilation step needed.", + "whyModernWins" : [ { + "icon" : "⚡", + "title" : "One command", + "desc" : "java File.java compiles and runs in one step." + }, { + "icon" : "📝", + "title" : "Script-like", + "desc" : "Add a shebang line to make .java files executable scripts." + }, { + "icon" : "🎓", + "title" : "Learning-friendly", + "desc" : "Newcomers run code immediately without learning build tools." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since JDK 11 (Sept 2018)" + }, + "docs" : [ { + "title" : "Launch Single-File Source-Code Programs (JEP 330)", + "href" : "https://openjdk.org/jeps/330" + } ] +}, { + "id" : 102, + "slug" : "ejb-timer-vs-jakarta-scheduler", + "title" : "EJB Timer vs Jakarta Scheduler", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 10+", + "oldApproach" : "EJB TimerService", + "modernApproach" : "ManagedScheduledExecutorService", + "oldCode" : "@Stateless\npublic class ReportGenerator {\n @Resource\n TimerService timerService;\n\n @PostConstruct\n public void init() {\n timerService.createCalendarTimer(\n new ScheduleExpression()\n .hour(\"2\").minute(\"0\"));\n }\n\n @Timeout\n public void generateReport(Timer timer) {\n // runs every day at 02:00\n buildDailyReport();\n }\n}", + "modernCode" : "@ApplicationScoped\npublic class ReportGenerator {\n @Resource\n ManagedScheduledExecutorService scheduler;\n\n @PostConstruct\n public void init() {\n scheduler.scheduleAtFixedRate(\n this::generateReport,\n 0, 24, TimeUnit.HOURS);\n }\n\n public void generateReport() {\n buildDailyReport();\n }\n}", + "summary" : "Replace heavyweight EJB timers with Jakarta Concurrency's ManagedScheduledExecutorService for simpler scheduling.", + "explanation" : "EJB timers require a @Stateless or @Singleton bean with a @Timeout callback and XML or annotation-based schedule expressions. Jakarta Concurrency provides ManagedScheduledExecutorService, which uses the familiar java.util.concurrent scheduling API. The result is less boilerplate, easier unit testing, and no EJB container dependency.", + "whyModernWins" : [ { + "icon" : "🪶", + "title" : "Reduced boilerplate", + "desc" : "No @Timeout callback or ScheduleExpression — use the standard ScheduledExecutorService API." + }, { + "icon" : "🧪", + "title" : "Better testability", + "desc" : "Plain methods and executor mocks make unit testing straightforward without EJB container." + }, { + "icon" : "☁️", + "title" : "Cloud-native friendly", + "desc" : "Managed executors integrate with container lifecycle and work in lightweight runtimes." + } ], + "support" : { + "state" : "available", + "description" : "Available since Jakarta EE 10 / Concurrency 3.0" + }, + "docs" : [ { + "title" : "Jakarta Concurrency Specification", + "href" : "https://jakarta.ee/specifications/concurrency/" + }, { + "title" : "Jakarta Concurrency 3.0 API", + "href" : "https://jakarta.ee/specifications/concurrency/3.0/apidocs/" + } ] +}, { + "id" : 99, + "slug" : "ejb-vs-cdi", + "title" : "EJB versus CDI", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "EJB", + "modernApproach" : "CDI Bean", + "oldCode" : "@Stateless\npublic class OrderEJB {\n @EJB\n private InventoryEJB inventory;\n\n public void placeOrder(Order order) {\n // container-managed transaction\n inventory.reserve(order.getItem());\n }\n}", + "modernCode" : "@ApplicationScoped\npublic class OrderService {\n @Inject\n private InventoryService inventory;\n\n @Transactional\n public void placeOrder(Order order) {\n inventory.reserve(order.getItem());\n }\n}", + "summary" : "Replace heavyweight EJBs with lightweight CDI beans for dependency injection and transactions.", + "explanation" : "CDI (Contexts and Dependency Injection) provides the same dependency injection and transaction management as EJBs, but as plain Java classes with no container-specific interfaces or superclasses. Scopes like @ApplicationScoped and @RequestScoped control lifecycle, and @Transactional replaces mandatory EJB transaction semantics.", + "whyModernWins" : [ { + "icon" : "🪶", + "title" : "Lightweight", + "desc" : "CDI beans are plain Java classes with no EJB-specific interfaces or descriptors." + }, { + "icon" : "💉", + "title" : "Unified injection", + "desc" : "@Inject works for every managed bean, JAX-RS resources, and Jakarta EE components alike." + }, { + "icon" : "🧪", + "title" : "Easy unit testing", + "desc" : "Plain classes without EJB proxy overhead are straightforward to instantiate and mock." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta CDI Specification", + "href" : "https://jakarta.ee/specifications/cdi/" + }, { + "title" : "Jakarta Transactions — @Transactional", + "href" : "https://jakarta.ee/specifications/transactions/" + } ] +}, { + "id" : 109, + "slug" : "jdbc-resultset-vs-jpa-criteria", + "title" : "JDBC ResultSet Mapping vs JPA Criteria API", + "category" : "enterprise", + "difficulty" : "advanced", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "JDBC ResultSet", + "modernApproach" : "JPA Criteria API", + "oldCode" : "String sql = \"SELECT * FROM users\"\n + \" WHERE status = ? AND age > ?\";\ntry (Connection con = ds.getConnection();\n PreparedStatement ps =\n con.prepareStatement(sql)) {\n ps.setString(1, status);\n ps.setInt(2, minAge);\n ResultSet rs = ps.executeQuery();\n List users = new ArrayList<>();\n while (rs.next()) {\n User u = new User();\n u.setId(rs.getLong(\"id\"));\n u.setName(rs.getString(\"name\"));\n u.setAge(rs.getInt(\"age\"));\n users.add(u);\n }\n}", + "modernCode" : "@PersistenceContext\nEntityManager em;\n\npublic List findActiveAboveAge(\n String status, int minAge) {\n CriteriaBuilder cb = em.getCriteriaBuilder();\n CriteriaQuery cq =\n cb.createQuery(User.class);\n Root root = cq.from(User.class);\n cq.select(root).where(\n cb.equal(root.get(\"status\"), status),\n cb.greaterThan(root.get(\"age\"), minAge));\n return em.createQuery(cq).getResultList();\n}", + "summary" : "Replace manual JDBC ResultSet mapping with JPA's type-safe Criteria API for dynamic queries.", + "explanation" : "Raw JDBC requires building SQL strings, setting parameters by index, and mapping each ResultSet column manually — a process that is error-prone and breaks silently when columns change. The JPA Criteria API builds queries programmatically using a type-safe builder pattern. Column names are validated against the entity model, result mapping is automatic, and complex dynamic queries compose cleanly without string concatenation.", + "whyModernWins" : [ { + "icon" : "🔒", + "title" : "Type-safe queries", + "desc" : "The Criteria builder catches field name and type mismatches at compile time." + }, { + "icon" : "🗺️", + "title" : "Automatic mapping", + "desc" : "JPA maps result rows to entity objects — no manual column-by-column extraction." + }, { + "icon" : "🧩", + "title" : "Composable predicates", + "desc" : "Dynamic where-clauses build cleanly with and(), or(), and reusable Predicate objects." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta Persistence Specification", + "href" : "https://jakarta.ee/specifications/persistence/" + }, { + "title" : "Jakarta Persistence 3.1 — Criteria API", + "href" : "https://jakarta.ee/specifications/persistence/3.1/apidocs/" + } ] +}, { + "id" : 112, + "slug" : "jdbc-vs-jooq", + "title" : "JDBC versus jOOQ", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Raw JDBC", + "modernLabel" : "jOOQ", + "oldApproach" : "Raw JDBC", + "modernApproach" : "jOOQ SQL DSL", + "oldCode" : "String sql = \"SELECT id, name, email FROM users \"\n + \"WHERE department = ? AND salary > ?\";\ntry (Connection con = ds.getConnection();\n PreparedStatement ps =\n con.prepareStatement(sql)) {\n ps.setString(1, department);\n ps.setBigDecimal(2, minSalary);\n ResultSet rs = ps.executeQuery();\n List result = new ArrayList<>();\n while (rs.next()) {\n result.add(new User(\n rs.getLong(\"id\"),\n rs.getString(\"name\"),\n rs.getString(\"email\")));\n }\n return result;\n}", + "modernCode" : "DSLContext dsl = DSL.using(ds, SQLDialect.POSTGRES);\n\nreturn dsl\n .select(USERS.ID, USERS.NAME, USERS.EMAIL)\n .from(USERS)\n .where(USERS.DEPARTMENT.eq(department)\n .and(USERS.SALARY.gt(minSalary)))\n .fetchInto(User.class);", + "summary" : "Replace raw JDBC string-based SQL with jOOQ's type-safe, fluent SQL DSL.", + "explanation" : "jOOQ (Java Object Oriented Querying) generates Java code from your database schema, turning table and column names into type-safe Java constants. The fluent DSL mirrors SQL syntax so queries are readable and composable. All parameters are bound automatically, eliminating SQL injection risk. Unlike JPA/JPQL, jOOQ embraces SQL fully — window functions, CTEs, RETURNING clauses, and vendor-specific extensions are all first-class.", + "whyModernWins" : [ { + "icon" : "🔒", + "title" : "Type-safe columns", + "desc" : "Column names are generated Java constants — typos and type mismatches become compiler errors instead of runtime failures." + }, { + "icon" : "📖", + "title" : "SQL fluency", + "desc" : "The jOOQ DSL mirrors SQL syntax closely, so complex JOINs, subqueries, and CTEs stay readable." + }, { + "icon" : "🛡️", + "title" : "Injection-free by design", + "desc" : "Parameters are always bound safely — no string concatenation means no SQL injection risk." + } ], + "support" : { + "state" : "available", + "description" : "jOOQ open-source edition supports all major open-source databases; older commercial databases require a paid license" + }, + "docs" : [ { + "title" : "jOOQ — Getting Started", + "href" : "https://www.jooq.org/doc/latest/manual/getting-started/" + }, { + "title" : "jOOQ — DSL API Reference", + "href" : "https://www.jooq.org/javadoc/latest/" + } ] +}, { + "id" : 100, + "slug" : "jdbc-vs-jpa", + "title" : "JDBC versus JPA", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "JDBC", + "modernApproach" : "JPA EntityManager", + "oldCode" : "String sql = \"SELECT * FROM users WHERE id = ?\";\ntry (Connection con = dataSource.getConnection();\n PreparedStatement ps =\n con.prepareStatement(sql)) {\n ps.setLong(1, id);\n ResultSet rs = ps.executeQuery();\n if (rs.next()) {\n User u = new User();\n u.setId(rs.getLong(\"id\"));\n u.setName(rs.getString(\"name\"));\n }\n}", + "modernCode" : "@PersistenceContext\nEntityManager em;\n\npublic User findUser(Long id) {\n return em.find(User.class, id);\n}\n\npublic List findByName(String name) {\n return em.createQuery(\n \"SELECT u FROM User u WHERE u.name = :name\",\n User.class)\n .setParameter(\"name\", name)\n .getResultList();\n}", + "summary" : "Replace verbose JDBC boilerplate with JPA's object-relational mapping and EntityManager.", + "explanation" : "JPA (Jakarta Persistence API) maps Java objects to database rows, eliminating manual ResultSet processing and SQL string concatenation. EntityManager provides find(), persist(), and JPQL queries so you work with domain objects instead of raw SQL, while the container manages connection pooling and transactions.", + "whyModernWins" : [ { + "icon" : "🗺️", + "title" : "Object mapping", + "desc" : "Entities are plain annotated classes — no manual ResultSet-to-object translation." + }, { + "icon" : "🔒", + "title" : "Type-safe queries", + "desc" : "JPQL operates on entity types and fields rather than raw table and column strings." + }, { + "icon" : "⚡", + "title" : "Built-in caching", + "desc" : "First- and second-level caches reduce database round-trips automatically." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta Persistence Specification", + "href" : "https://jakarta.ee/specifications/persistence/" + }, { + "title" : "Jakarta Persistence 3.1 API", + "href" : "https://jakarta.ee/specifications/persistence/3.1/apidocs/" + } ] +}, { + "id" : 103, + "slug" : "jndi-lookup-vs-cdi-injection", + "title" : "JNDI Lookup vs CDI Injection", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "JNDI Lookup", + "modernApproach" : "CDI @Inject", + "oldCode" : "public class OrderService {\n private DataSource ds;\n\n public void init() throws NamingException {\n InitialContext ctx = new InitialContext();\n ds = (DataSource) ctx.lookup(\n \"java:comp/env/jdbc/OrderDB\");\n }\n\n public List findAll()\n throws SQLException {\n try (Connection con = ds.getConnection()) {\n // query orders\n }\n }\n}", + "modernCode" : "@ApplicationScoped\npublic class OrderService {\n @Inject\n @Resource(name = \"jdbc/OrderDB\")\n DataSource ds;\n\n public List findAll()\n throws SQLException {\n try (Connection con = ds.getConnection()) {\n // query orders\n }\n }\n}", + "summary" : "Replace fragile JNDI string lookups with type-safe CDI injection for container-managed resources.", + "explanation" : "The traditional JNDI pattern forces you to use string-based resource names, handle NamingException, and manage an InitialContext. CDI injection with @Inject (or @Resource for container resources) lets the container wire dependencies automatically. Typos become compile-time errors, and classes are easier to test because dependencies can be injected directly.", + "whyModernWins" : [ { + "icon" : "🔒", + "title" : "Type-safe wiring", + "desc" : "Injection errors are caught at deployment time, not at runtime via string lookups." + }, { + "icon" : "🗑️", + "title" : "No boilerplate", + "desc" : "Eliminates InitialContext creation, JNDI name strings, and NamingException handling." + }, { + "icon" : "🧪", + "title" : "Testable", + "desc" : "Dependencies are injected fields, easily replaced with mocks in unit tests." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta CDI Specification", + "href" : "https://jakarta.ee/specifications/cdi/" + }, { + "title" : "Jakarta Annotations — @Resource", + "href" : "https://jakarta.ee/specifications/annotations/" + } ] +}, { + "id" : 101, + "slug" : "jpa-vs-jakarta-data", + "title" : "JPA versus Jakarta Data", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "21", + "oldLabel" : "Jakarta EE 8+", + "modernLabel" : "Jakarta EE 11+", + "oldApproach" : "JPA EntityManager", + "modernApproach" : "Jakarta Data Repository", + "oldCode" : "@PersistenceContext\nEntityManager em;\n\npublic User findById(Long id) {\n return em.find(User.class, id);\n}\n\npublic List findByName(String name) {\n return em.createQuery(\n \"SELECT u FROM User u WHERE u.name = :name\",\n User.class)\n .setParameter(\"name\", name)\n .getResultList();\n}\n\npublic void save(User user) {\n em.persist(user);\n}", + "modernCode" : "@Repository\npublic interface Users extends CrudRepository {\n List findByName(String name);\n}", + "summary" : "Declare a repository interface and let Jakarta Data generate the DAO implementation automatically.", + "explanation" : "Jakarta Data (Jakarta EE 11) turns data access into a pure interface declaration. You annotate an interface with @Repository and extend a built-in repository type such as CrudRepository. The runtime generates the implementation — including derived queries from method names like findByName — so there is no EntityManager boilerplate, no JPQL strings, and no hand-written save/find methods.", + "whyModernWins" : [ { + "icon" : "🪄", + "title" : "Zero boilerplate", + "desc" : "Declare the interface; the container generates the full DAO implementation at deploy time." + }, { + "icon" : "🔍", + "title" : "Derived queries", + "desc" : "Method names like findByNameAndStatus are parsed automatically — no JPQL or SQL needed." + }, { + "icon" : "🔌", + "title" : "Portable", + "desc" : "Any Jakarta EE 11 compliant runtime provides the repository implementation with no vendor lock-in." + } ], + "support" : { + "state" : "available", + "description" : "Available since Jakarta EE 11 / Java 21 (2024)" + }, + "docs" : [ { + "title" : "Jakarta Data 1.0 Specification", + "href" : "https://jakarta.ee/specifications/data/1.0/" + }, { + "title" : "Jakarta Data 1.0 API", + "href" : "https://jakarta.ee/specifications/data/1.0/apidocs/" + } ] +}, { + "id" : 107, + "slug" : "jsf-managed-bean-vs-cdi-named", + "title" : "JSF Managed Bean vs CDI Named Bean", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 10+", + "oldApproach" : "@ManagedBean", + "modernApproach" : "@Named + CDI", + "oldCode" : "@ManagedBean\n@SessionScoped\npublic class UserBean implements Serializable {\n @ManagedProperty(\"#{userService}\")\n private UserService userService;\n\n private String name;\n\n public String getName() { return name; }\n public void setName(String name) {\n this.name = name;\n }\n\n public void setUserService(UserService svc) {\n this.userService = svc;\n }\n}", + "modernCode" : "@Named\n@SessionScoped\npublic class UserBean implements Serializable {\n @Inject\n private UserService userService;\n\n private String name;\n\n public String getName() { return name; }\n public void setName(String name) {\n this.name = name;\n }\n}", + "summary" : "Replace deprecated JSF @ManagedBean with CDI @Named for a unified dependency injection model.", + "explanation" : "JSF's @ManagedBean and @ManagedProperty were deprecated in Jakarta Faces 2.3 and removed in Jakarta EE 10. The CDI-based replacement uses @Named to expose the bean to EL expressions and @Inject for dependency wiring. This unifies the bean model: JSF pages, JAX-RS resources, and EJBs all share the same CDI container.", + "whyModernWins" : [ { + "icon" : "🔗", + "title" : "Unified model", + "desc" : "One CDI container manages all beans — JSF, REST, and service layers share the same injection." + }, { + "icon" : "🗑️", + "title" : "Less boilerplate", + "desc" : "@Inject replaces @ManagedProperty and its required setter method." + }, { + "icon" : "🔮", + "title" : "Future-proof", + "desc" : "@ManagedBean is removed in Jakarta EE 10; @Named is the supported replacement." + } ], + "support" : { + "state" : "available", + "description" : "CDI @Named available since Java EE 6; @ManagedBean removed in Jakarta EE 10" + }, + "docs" : [ { + "title" : "Jakarta Faces Specification", + "href" : "https://jakarta.ee/specifications/faces/" + }, { + "title" : "Jakarta CDI Specification", + "href" : "https://jakarta.ee/specifications/cdi/" + } ] +}, { + "id" : 104, + "slug" : "manual-transaction-vs-declarative", + "title" : "Manual JPA Transaction vs Declarative @Transactional", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "Manual Transaction", + "modernApproach" : "@Transactional", + "oldCode" : "@PersistenceContext\nEntityManager em;\n\npublic void transferFunds(Long from, Long to,\n BigDecimal amount) {\n EntityTransaction tx = em.getTransaction();\n tx.begin();\n try {\n Account src = em.find(Account.class, from);\n Account dst = em.find(Account.class, to);\n src.debit(amount);\n dst.credit(amount);\n tx.commit();\n } catch (Exception e) {\n tx.rollback();\n throw e;\n }\n}", + "modernCode" : "@ApplicationScoped\npublic class AccountService {\n @PersistenceContext\n EntityManager em;\n\n @Transactional\n public void transferFunds(Long from, Long to,\n BigDecimal amount) {\n Account src = em.find(Account.class, from);\n Account dst = em.find(Account.class, to);\n src.debit(amount);\n dst.credit(amount);\n }\n}", + "summary" : "Replace verbose begin/commit/rollback blocks with a single @Transactional annotation.", + "explanation" : "Manual transaction management requires explicit begin(), commit(), and rollback() calls wrapped in try-catch blocks — every service method repeats this boilerplate. The @Transactional annotation delegates lifecycle management to the container: it begins a transaction before the method, commits on success, and rolls back on RuntimeException automatically.", + "whyModernWins" : [ { + "icon" : "🗑️", + "title" : "No boilerplate", + "desc" : "One annotation replaces repetitive begin/commit/rollback try-catch blocks." + }, { + "icon" : "🛡️", + "title" : "Safer rollback", + "desc" : "The container guarantees rollback on unchecked exceptions — no risk of forgetting the catch block." + }, { + "icon" : "📐", + "title" : "Declarative control", + "desc" : "Propagation, isolation, and rollback rules are expressed as annotation attributes." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta Transactions Specification", + "href" : "https://jakarta.ee/specifications/transactions/" + }, { + "title" : "Jakarta Transactions 2.0 API", + "href" : "https://jakarta.ee/specifications/transactions/2.0/apidocs/" + } ] +}, { + "id" : 106, + "slug" : "mdb-vs-reactive-messaging", + "title" : "Message-Driven Bean vs Reactive Messaging", + "category" : "enterprise", + "difficulty" : "advanced", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "MicroProfile 4+", + "oldApproach" : "Message-Driven Bean", + "modernApproach" : "Reactive Messaging", + "oldCode" : "@MessageDriven(activationConfig = {\n @ActivationConfigProperty(\n propertyName = \"destinationType\",\n propertyValue = \"jakarta.jms.Queue\"),\n @ActivationConfigProperty(\n propertyName = \"destination\",\n propertyValue = \"java:/jms/OrderQueue\")\n})\npublic class OrderMDB implements MessageListener {\n @Override\n public void onMessage(Message message) {\n TextMessage txt = (TextMessage) message;\n processOrder(txt.getText());\n }\n}", + "modernCode" : "@ApplicationScoped\npublic class OrderProcessor {\n @Incoming(\"orders\")\n public void process(Order order) {\n // automatically deserialized from\n // the \"orders\" channel\n fulfillOrder(order);\n }\n}", + "summary" : "Replace JMS Message-Driven Beans with MicroProfile Reactive Messaging for simpler event processing.", + "explanation" : "Message-Driven Beans require implementing MessageListener, configuring activation properties, and manually deserializing JMS messages. MicroProfile Reactive Messaging uses a simple @Incoming annotation on a method that receives typed objects directly. The channel configuration is externalised, making the code broker-agnostic and far easier to test.", + "whyModernWins" : [ { + "icon" : "🪶", + "title" : "Minimal code", + "desc" : "A single @Incoming method replaces the MDB class, MessageListener interface, and activation config." + }, { + "icon" : "🔌", + "title" : "Broker-agnostic", + "desc" : "Swap Kafka, AMQP, or JMS connectors via configuration without changing application code." + }, { + "icon" : "☁️", + "title" : "Cloud-native fit", + "desc" : "Reactive streams backpressure and lightweight runtime make it ideal for containerised deployments." + } ], + "support" : { + "state" : "available", + "description" : "Available since MicroProfile 4.0 / SmallRye Reactive Messaging" + }, + "docs" : [ { + "title" : "MicroProfile Reactive Messaging Specification", + "href" : "https://download.eclipse.org/microprofile/microprofile-reactive-messaging-3.0/microprofile-reactive-messaging-spec-3.0.html" + }, { + "title" : "SmallRye Reactive Messaging Documentation", + "href" : "https://smallrye.io/smallrye-reactive-messaging/" + } ] +}, { + "id" : 98, + "slug" : "servlet-vs-jaxrs", + "title" : "Servlet versus JAX-RS", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "HttpServlet", + "modernApproach" : "JAX-RS Resource", + "oldCode" : "@WebServlet(\"/users\")\npublic class UserServlet extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest req,\n HttpServletResponse res)\n throws ServletException, IOException {\n String id = req.getParameter(\"id\");\n res.setContentType(\"application/json\");\n res.getWriter().write(\"{\\\"id\\\":\\\"\" + id + \"\\\"}\");\n }\n}", + "modernCode" : "@Path(\"/users\")\npublic class UserResource {\n @GET\n @Produces(MediaType.APPLICATION_JSON)\n public Response getUser(\n @QueryParam(\"id\") String id) {\n return Response.ok(new User(id)).build();\n }\n}", + "summary" : "Replace verbose HttpServlet boilerplate with declarative JAX-RS resource classes.", + "explanation" : "JAX-RS (Jakarta RESTful Web Services) lets you expose REST endpoints using simple annotations like @GET, @Path, and @Produces. No more manual parsing of request parameters or setting content types on the response — the runtime handles marshalling and routing automatically.", + "whyModernWins" : [ { + "icon" : "📐", + "title" : "Declarative routing", + "desc" : "Annotations define HTTP method, path, and content type instead of imperative if/else dispatch." + }, { + "icon" : "🔄", + "title" : "Automatic marshalling", + "desc" : "Return POJOs directly; the runtime serialises them to JSON or XML based on @Produces." + }, { + "icon" : "🧪", + "title" : "Easier testing", + "desc" : "Resource classes are plain Java objects, testable without a servlet container." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta RESTful Web Services Specification", + "href" : "https://jakarta.ee/specifications/restful-ws/" + }, { + "title" : "Jakarta REST 3.1 API", + "href" : "https://jakarta.ee/specifications/restful-ws/3.1/apidocs/" + } ] +}, { + "id" : 108, + "slug" : "singleton-ejb-vs-cdi-application-scoped", + "title" : "Singleton EJB vs CDI @ApplicationScoped", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "@Singleton EJB", + "modernApproach" : "@ApplicationScoped CDI", + "oldCode" : "@Singleton\n@Startup\n@ConcurrencyManagement(\n ConcurrencyManagementType.CONTAINER)\npublic class ConfigCache {\n private Map cache;\n\n @PostConstruct\n public void load() {\n cache = loadFromDatabase();\n }\n\n @Lock(LockType.READ)\n public String get(String key) {\n return cache.get(key);\n }\n\n @Lock(LockType.WRITE)\n public void refresh() {\n cache = loadFromDatabase();\n }\n}", + "modernCode" : "@ApplicationScoped\npublic class ConfigCache {\n private volatile Map cache;\n\n @PostConstruct\n public void load() {\n cache = loadFromDatabase();\n }\n\n public String get(String key) {\n return cache.get(key);\n }\n\n public void refresh() {\n cache = loadFromDatabase();\n }\n}", + "summary" : "Replace Singleton EJBs with CDI @ApplicationScoped beans for simpler shared-state management.", + "explanation" : "Singleton EJBs bundle concurrency management (@Lock, @ConcurrencyManagement) and eager initialisation (@Startup) into the EJB container. A CDI @ApplicationScoped bean achieves the same single-instance lifecycle with far less ceremony. When concurrency control is needed, standard java.util.concurrent utilities give you finer-grained control than the EJB lock annotations.", + "whyModernWins" : [ { + "icon" : "🪶", + "title" : "Less annotation noise", + "desc" : "No @ConcurrencyManagement, @Lock, or @Startup — just a single @ApplicationScoped annotation." + }, { + "icon" : "🔧", + "title" : "Flexible concurrency", + "desc" : "Use java.util.concurrent locks or volatile for exactly the thread-safety you need." + }, { + "icon" : "🧪", + "title" : "Easy testing", + "desc" : "Plain CDI beans can be instantiated directly in tests without an EJB container." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta CDI Specification", + "href" : "https://jakarta.ee/specifications/cdi/" + }, { + "title" : "Jakarta Enterprise Beans Specification", + "href" : "https://jakarta.ee/specifications/enterprise-beans/" + } ] +}, { + "id" : 105, + "slug" : "soap-vs-jakarta-rest", + "title" : "SOAP Web Services vs Jakarta REST", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "11", + "oldLabel" : "Java EE", + "modernLabel" : "Jakarta EE 8+", + "oldApproach" : "JAX-WS / SOAP", + "modernApproach" : "Jakarta REST / JSON", + "oldCode" : "@WebService\npublic class UserWebService {\n @WebMethod\n public UserResponse getUser(\n @WebParam(name = \"id\") String id) {\n User user = findUser(id);\n UserResponse res = new UserResponse();\n res.setId(user.getId());\n res.setName(user.getName());\n return res;\n }\n}", + "modernCode" : "@Path(\"/users\")\n@Produces(MediaType.APPLICATION_JSON)\npublic class UserResource {\n @Inject\n UserService userService;\n\n @GET\n @Path(\"/{id}\")\n public User getUser(@PathParam(\"id\") String id) {\n return userService.findById(id);\n }\n}", + "summary" : "Replace heavyweight SOAP/WSDL endpoints with clean Jakarta REST resources returning JSON.", + "explanation" : "SOAP-based web services rely on WSDL contracts, XML marshalling, and JAX-WS annotations that add significant overhead. Jakarta REST (formerly JAX-RS) uses intuitive annotations like @GET, @Path, and @Produces to expose RESTful JSON APIs. The programming model is simpler, the payloads are smaller, and the approach aligns with how modern microservices communicate.", + "whyModernWins" : [ { + "icon" : "🪶", + "title" : "Lighter payloads", + "desc" : "JSON is more compact than SOAP XML envelopes, reducing bandwidth and parsing overhead." + }, { + "icon" : "📐", + "title" : "Simple annotations", + "desc" : "@GET, @Path, and @Produces replace WSDL, @WebService, and @WebMethod ceremony." + }, { + "icon" : "🔌", + "title" : "Microservice-ready", + "desc" : "REST/JSON is the standard for service-to-service communication in cloud-native architectures." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Jakarta EE 8 / Java 11" + }, + "docs" : [ { + "title" : "Jakarta RESTful Web Services Specification", + "href" : "https://jakarta.ee/specifications/restful-ws/" + }, { + "title" : "Jakarta JSON Binding Specification", + "href" : "https://jakarta.ee/specifications/jsonb/" + } ] +}, { + "id" : 110, + "slug" : "spring-api-versioning", + "title" : "Spring Framework 7 API Versioning", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "Spring Boot 2/3", + "modernLabel" : "Spring Framework 7+", + "oldApproach" : "Manual URL Path Versioning", + "modernApproach" : "Native API Versioning", + "oldCode" : "// Version 1 controller\n@RestController\n@RequestMapping(\"/api/v1/products\")\npublic class ProductControllerV1 {\n @GetMapping(\"/{id}\")\n public ProductDtoV1 getProduct(\n @PathVariable Long id) {\n return service.getV1(id);\n }\n}\n\n// Version 2 — duplicated structure\n@RestController\n@RequestMapping(\"/api/v2/products\")\npublic class ProductControllerV2 {\n @GetMapping(\"/{id}\")\n public ProductDtoV2 getProduct(\n @PathVariable Long id) {\n return service.getV2(id);\n }\n}", + "modernCode" : "// Configure versioning once\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n @Override\n public void configureApiVersioning(\n ApiVersionConfigurer config) {\n config.useRequestHeader(\"X-API-Version\");\n }\n}\n\n// Single controller, version per method\n@RestController\n@RequestMapping(\"/api/products\")\npublic class ProductController {\n @GetMapping(value = \"/{id}\", version = \"1\")\n public ProductDtoV1 getV1(@PathVariable Long id) {\n return service.getV1(id);\n }\n\n @GetMapping(value = \"/{id}\", version = \"2\")\n public ProductDtoV2 getV2(@PathVariable Long id) {\n return service.getV2(id);\n }\n}", + "summary" : "Replace duplicated version-prefixed controllers with Spring Framework 7's native API versioning support.", + "explanation" : "Before Spring Framework 7, API versioning required separate controller classes per version (e.g., /api/v1/products, /api/v2/products), duplicating request mappings and scattering version logic across many files. Spring Framework 7 introduces native versioning through a new version attribute on @RequestMapping and related annotations, plus a configureApiVersioning hook in WebMvcConfigurer. The version can be resolved from a request header, a URL path segment, or a query parameter — all controlled in one place.", + "whyModernWins" : [ { + "icon" : "🗂️", + "title" : "No controller duplication", + "desc" : "All versions live in one controller class; only the individual handler methods carry a version attribute." + }, { + "icon" : "⚙️", + "title" : "Centralised version strategy", + "desc" : "Switch from header to URL or query-param versioning in a single configureApiVersioning call." + }, { + "icon" : "📈", + "title" : "Incremental evolution", + "desc" : "Add a new version to one method without touching unrelated endpoints or creating new controller files." + } ], + "support" : { + "state" : "available", + "description" : "Available since Spring Framework 7.0 (requires Java 17+)" + }, + "docs" : [ { + "title" : "Spring Framework 7.0 — API Versioning", + "href" : "https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-requestmapping.html#mvc-ann-requestmapping-version" + }, { + "title" : "Spring Framework 7.0 Migration Guide", + "href" : "https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-7.0-Migration-Guide" + } ] +}, { + "id" : 110, + "slug" : "spring-null-safety-jspecify", + "title" : "Spring Null Safety with JSpecify", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "Spring 5/6", + "modernLabel" : "Spring 7", + "oldApproach" : "Spring @NonNull/@Nullable", + "modernApproach" : "JSpecify @NullMarked", + "oldCode" : "import org.springframework.lang.NonNull;\nimport org.springframework.lang.Nullable;\n\npublic class UserService {\n\n @Nullable\n public User findById(@NonNull String id) {\n return repository.findById(id).orElse(null);\n }\n\n @NonNull\n public List findAll() {\n return repository.findAll();\n }\n\n @NonNull\n public User save(@NonNull User user) {\n return repository.save(user);\n }\n}", + "modernCode" : "import org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\n@NullMarked\npublic class UserService {\n\n public @Nullable User findById(String id) {\n return repository.findById(id).orElse(null);\n }\n\n public List findAll() {\n return repository.findAll();\n }\n\n public User save(User user) {\n return repository.save(user);\n }\n}", + "summary" : "Spring 7 adopts JSpecify annotations, making non-null the default and reducing annotation noise.", + "explanation" : "Spring 5 and 6 introduced their own null safety annotations in the `org.springframework.lang` package. While useful, these were framework-specific and required annotating every non-null element explicitly. Spring 7 migrates to JSpecify, a cross-ecosystem standard for null safety. The `@NullMarked` annotation at the class or package level declares that all unannotated types are non-null by default. Only actual nullable types need the `@Nullable` annotation, dramatically reducing verbosity. JSpecify annotations are recognized by major static analysis tools such as NullAway, Error Prone, and IntelliJ IDEA, bringing richer tooling support beyond what Spring-specific annotations provided.", + "whyModernWins" : [ { + "icon" : "✂️", + "title" : "Non-null by default", + "desc" : "@NullMarked makes all unannotated types non-null, so only nullable exceptions need annotation." + }, { + "icon" : "🌐", + "title" : "Ecosystem standard", + "desc" : "JSpecify annotations are a cross-framework standard recognized by NullAway, Error Prone, and IDEs." + }, { + "icon" : "🔍", + "title" : "Richer tooling", + "desc" : "Modern static analyzers understand JSpecify's null model and report violations at compile time." + } ], + "support" : { + "state" : "available", + "description" : "Available since Spring Framework 7.0 (requires Java 17+)" + }, + "docs" : [ { + "title" : "Spring Framework 7 — Null Safety", + "href" : "https://docs.spring.io/spring-framework/reference/core/null-safety.html" + }, { + "title" : "JSpecify Specification", + "href" : "https://jspecify.dev/docs/spec" + } ] +}, { + "id" : 110, + "slug" : "spring-xml-config-vs-annotations", + "title" : "Spring XML Bean Config vs Annotation-Driven", + "category" : "enterprise", + "difficulty" : "intermediate", + "jdkVersion" : "17", + "oldLabel" : "Spring (XML)", + "modernLabel" : "Spring Boot 3+", + "oldApproach" : "XML Bean Definitions", + "modernApproach" : "Annotation-Driven Beans", + "oldCode" : "\n\n\n \n \n \n\n \n \n \n\n", + "modernCode" : "@SpringBootApplication\npublic class Application {\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n}\n\n@Repository\npublic class UserRepository {\n private final JdbcTemplate jdbc;\n\n public UserRepository(JdbcTemplate jdbc) {\n this.jdbc = jdbc;\n }\n}\n\n@Service\npublic class UserService {\n private final UserRepository repository;\n\n public UserService(UserRepository repository) {\n this.repository = repository;\n }\n}", + "summary" : "Replace verbose Spring XML bean definitions with concise annotation-driven configuration in Spring Boot.", + "explanation" : "Traditional Spring applications wired beans through XML configuration files, declaring each class and its dependencies as verbose elements. While annotation support existed since Spring 2.5, XML remained the dominant approach until Spring Boot introduced auto-configuration. Spring Boot detects beans annotated with @Component, @Service, @Repository, and @Controller via classpath scanning, satisfies dependencies through constructor injection automatically, and configures infrastructure like DataSource from the classpath — eliminating all XML wiring files.", + "whyModernWins" : [ { + "icon" : "🚫", + "title" : "No XML", + "desc" : "@SpringBootApplication triggers component scanning and auto-configuration, eliminating all XML wiring files." + }, { + "icon" : "💉", + "title" : "Constructor injection", + "desc" : "Spring injects dependencies through constructors automatically, making beans easier to test and reason about." + }, { + "icon" : "⚡", + "title" : "Auto-configuration", + "desc" : "Spring Boot configures DataSource, JPA, and other infrastructure from the classpath with zero boilerplate." + } ], + "support" : { + "state" : "available", + "description" : "Widely available since Spring Boot 1.0 (April 2014); Spring Boot 3 requires Java 17+" + }, + "docs" : [ { + "title" : "Spring Framework — Annotation-based Container Configuration", + "href" : "https://docs.spring.io/spring-framework/reference/core/beans/annotation-config.html" + }, { + "title" : "Spring Boot — Auto-configuration", + "href" : "https://docs.spring.io/spring-boot/reference/using/auto-configuration.html" + } ] +} ] diff --git a/site/pt-BR/datetime/date-formatting.html b/site/pt-BR/datetime/date-formatting.html new file mode 100644 index 0000000..775781d --- /dev/null +++ b/site/pt-BR/datetime/date-formatting.html @@ -0,0 +1,376 @@ + + + + + + Date formatting | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +
+ + +
+
+ Date/Time + beginner +
+

Date formatting

+

Format dates with thread-safe, immutable DateTimeFormatter.

+
+ +
+ +
+
+
+ ✕ Pre-Java 8 + +
+
+
// Not thread-safe!
+SimpleDateFormat sdf =
+    new SimpleDateFormat("yyyy-MM-dd");
+String formatted = sdf.format(date);
+// Must synchronize for concurrent use
+
+
+
+
+ ✓ Java 8+ + +
+
+
DateTimeFormatter fmt =
+    DateTimeFormatter.ofPattern(
+        "uuuu-MM-dd");
+String formatted =
+    LocalDate.now().format(fmt);
+// Thread-safe, immutable
+
+
+
+
+ +
+ +
+
+
🛡️
+

Thread-safe

+

Share formatters across threads without synchronization.

+
+
+
📋
+

Built-in formats

+

ISO_LOCAL_DATE, ISO_INSTANT, etc. for standard formats.

+
+
+
🔒
+

Immutable

+

Store as static final constants safely.

+
+
+
+ +
+
+
Abordagem Antiga
+
SimpleDateFormat
+
+
+
Abordagem Moderna
+
DateTimeFormatter
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Date formatting
+ Disponível +

Widely available since JDK 8 (March 2014)

+
+
+ +
+ +

DateTimeFormatter is immutable and thread-safe, unlike SimpleDateFormat. It can be stored as a constant and shared. Predefined formatters like ISO_LOCAL_DATE are available for common formats.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/datetime/duration-and-period.html b/site/pt-BR/datetime/duration-and-period.html new file mode 100644 index 0000000..b5e9e24 --- /dev/null +++ b/site/pt-BR/datetime/duration-and-period.html @@ -0,0 +1,376 @@ + + + + + + Duration and Period | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Date/Time + beginner +
+

Duration and Period

+

Calculate time differences with type-safe Duration and Period.

+
+ +
+ +
+
+
+ ✕ Pre-Java 8 + +
+
+
// How many days between two dates?
+long diff = date2.getTime()
+    - date1.getTime();
+long days = diff
+    / (1000 * 60 * 60 * 24);
+// ignores DST, leap seconds
+
+
+
+
+ ✓ Java 8+ + +
+
+
long days = ChronoUnit.DAYS
+    .between(date1, date2);
+Period period = Period.between(
+    date1, date2);
+Duration elapsed = Duration.between(
+    time1, time2);
+
+
+
+
+ +
+ +
+
+
🎯
+

Type-safe

+

Duration for time, Period for dates — no confusion.

+
+
+
🛡️
+

Correct math

+

Handles DST transitions, leap years, and leap seconds.

+
+
+
📖
+

Readable

+

ChronoUnit.DAYS.between() reads like English.

+
+
+
+ +
+
+
Abordagem Antiga
+
Millisecond Math
+
+
+
Abordagem Moderna
+
Duration / Period
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Duration and Period
+ Disponível +

Widely available since JDK 8 (March 2014)

+
+
+ +
+ +

Duration is for time-based amounts (hours, minutes, seconds). Period is for date-based amounts (years, months, days). ChronoUnit.between() for simple differences. All handle edge cases correctly.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/datetime/hex-format.html b/site/pt-BR/datetime/hex-format.html new file mode 100644 index 0000000..344c5b1 --- /dev/null +++ b/site/pt-BR/datetime/hex-format.html @@ -0,0 +1,378 @@ + + + + + + HexFormat | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Date/Time + intermediate +
+

HexFormat

+

Convert between hex strings and byte arrays with HexFormat.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Pad to 2 digits, uppercase
+String hex = String.format(
+    "%02X", byteValue);
+// Parse hex string
+int val = Integer.parseInt(
+    "FF", 16);
+
+
+
+
+ ✓ Java 17+ + +
+
+
HexFormat hex = HexFormat.of()
+    .withUpperCase();
+String s = hex.toHexDigits(
+    byteValue);
+byte[] bytes =
+    hex.parseHex("48656C6C6F");
+
+
+
+
+ +
+ +
+
+
📐
+

Bidirectional

+

Convert bytes→hex and hex→bytes with one API.

+
+
+
🔧
+

Configurable

+

Delimiters, prefix, suffix, upper/lower case.

+
+
+
📦
+

Array support

+

Encode/decode entire byte arrays at once.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Hex Conversion
+
+
+
Abordagem Moderna
+
HexFormat
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
HexFormat
+ Disponível +

Widely available since JDK 17 LTS (Sept 2021)

+
+
+ +
+ +

HexFormat provides bidirectional hex encoding/decoding for bytes, ints, and arrays. Configure delimiters, prefix, suffix, and case. No more manual formatting or parsing.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/datetime/instant-precision.html b/site/pt-BR/datetime/instant-precision.html new file mode 100644 index 0000000..61001ca --- /dev/null +++ b/site/pt-BR/datetime/instant-precision.html @@ -0,0 +1,378 @@ + + + + + + Instant with nanosecond precision | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Date/Time + intermediate +
+

Instant with nanosecond precision

+

Get timestamps with microsecond or nanosecond precision.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Millisecond precision only
+long millis =
+    System.currentTimeMillis();
+// 1708012345678
+
+
+
+
+ ✓ Java 9+ + +
+
+
// Microsecond/nanosecond precision
+Instant now = Instant.now();
+// 2025-02-15T20:12:25.678901234Z
+long nanos = now.getNano();
+
+
+
+
+ +
+ +
+
+
🎯
+

Higher precision

+

Microsecond/nanosecond vs millisecond timestamps.

+
+
+
📐
+

Type-safe

+

Instant carries its precision — no ambiguous longs.

+
+
+
🌐
+

UTC-based

+

Instant is always in UTC — no timezone confusion.

+
+
+
+ +
+
+
Abordagem Antiga
+
Milliseconds
+
+
+
Abordagem Moderna
+
Nanoseconds
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Instant with nanosecond precision
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Java 9 improved the clock resolution so Instant.now() captures microsecond precision on most platforms (nanosecond on some). The old currentTimeMillis() only gives milliseconds.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/datetime/java-time-basics.html b/site/pt-BR/datetime/java-time-basics.html new file mode 100644 index 0000000..97d8273 --- /dev/null +++ b/site/pt-BR/datetime/java-time-basics.html @@ -0,0 +1,378 @@ + + + + + + java.time API basics | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Date/Time + beginner +
+

java.time API basics

+

Use immutable, clear date/time types instead of Date and Calendar.

+
+ +
+ +
+
+
+ ✕ Pre-Java 8 + +
+
+
// Mutable, confusing, zero-indexed months
+Calendar cal = Calendar.getInstance();
+cal.set(2025, 0, 15); // January = 0!
+Date date = cal.getTime();
+// not thread-safe
+
+
+
+
+ ✓ Java 8+ + +
+
+
LocalDate date = LocalDate.of(
+    2025, Month.JANUARY, 15);
+LocalTime time = LocalTime.of(14, 30);
+Instant now = Instant.now();
+// immutable, thread-safe
+
+
+
+
+ +
+ +
+
+
🔒
+

Immutable

+

Date/time values can't be accidentally modified.

+
+
+
📖
+

Clear API

+

Month.JANUARY, not 0. DayOfWeek.MONDAY, not 2.

+
+
+
🛡️
+

Thread-safe

+

No synchronization needed — share freely across threads.

+
+
+
+ +
+
+
Abordagem Antiga
+
Date + Calendar
+
+
+
Abordagem Moderna
+
java.time.*
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
java.time API basics
+ Disponível +

Widely available since JDK 8 (March 2014)

+
+
+ +
+ +

java.time provides LocalDate, LocalTime, LocalDateTime, Instant, ZonedDateTime — all immutable and thread-safe. Months are 1-indexed. No more Calendar.JANUARY = 0 confusion.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/datetime/math-clamp.html b/site/pt-BR/datetime/math-clamp.html new file mode 100644 index 0000000..a4bd03b --- /dev/null +++ b/site/pt-BR/datetime/math-clamp.html @@ -0,0 +1,374 @@ + + + + + + Math.clamp() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Date/Time + beginner +
+

Math.clamp()

+

Clamp a value between bounds with a single clear call.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Clamp value between min and max
+int clamped =
+    Math.min(Math.max(value, 0), 100);
+// or: min and max order confusion
+
+
+
+
+ ✓ Java 21+ + +
+
+
int clamped =
+    Math.clamp(value, 0, 100);
+// value constrained to [0, 100]
+
+
+
+
+ +
+ +
+
+
📖
+

Self-documenting

+

clamp(value, min, max) is unambiguous.

+
+
+
🛡️
+

Less error-prone

+

No more swapping min/max order by accident.

+
+
+
🎯
+

All numeric types

+

Works with int, long, float, and double.

+
+
+
+ +
+
+
Abordagem Antiga
+
Nested min/max
+
+
+
Abordagem Moderna
+
Math.clamp()
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Math.clamp()
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Math.clamp(value, min, max) constrains a value to the range [min, max]. Clearer than nested Math.min/Math.max and available for int, long, float, and double.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/ejb-timer-vs-jakarta-scheduler.html b/site/pt-BR/enterprise/ejb-timer-vs-jakarta-scheduler.html new file mode 100644 index 0000000..af18326 --- /dev/null +++ b/site/pt-BR/enterprise/ejb-timer-vs-jakarta-scheduler.html @@ -0,0 +1,412 @@ + + + + + + EJB Timer vs Jakarta Scheduler | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

EJB Timer vs Jakarta Scheduler

+

Replace heavyweight EJB timers with Jakarta Concurrency's ManagedScheduledExecutorService for simpler scheduling.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@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();
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 10+ + +
+
+
@ApplicationScoped
+public class ReportGenerator {
+    @Resource
+    ManagedScheduledExecutorService scheduler;
+
+    @PostConstruct
+    public void init() {
+        scheduler.scheduleAtFixedRate(
+            this::generateReport,
+            0, 24, TimeUnit.HOURS);
+    }
+
+    public void generateReport() {
+        buildDailyReport();
+    }
+}
+
+
+
+
+ +
+ +
+
+
🪶
+

Reduced boilerplate

+

No @Timeout callback or ScheduleExpression — use the standard ScheduledExecutorService API.

+
+
+
🧪
+

Better testability

+

Plain methods and executor mocks make unit testing straightforward without EJB container.

+
+
+
☁️
+

Cloud-native friendly

+

Managed executors integrate with container lifecycle and work in lightweight runtimes.

+
+
+
+ +
+
+
Abordagem Antiga
+
EJB TimerService
+
+
+
Abordagem Moderna
+
ManagedScheduledExecutorService
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
EJB Timer vs Jakarta Scheduler
+ Disponível +

Available since Jakarta EE 10 / Concurrency 3.0

+
+
+ +
+ +

EJB timers require a @Stateless or @Singleton bean with a @Timeout callback and XML or annotation-based schedule expressions. Jakarta Concurrency provides ManagedScheduledExecutorService, which uses the familiar java.util.concurrent scheduling API. The result is less boilerplate, easier unit testing, and no EJB container dependency.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/ejb-vs-cdi.html b/site/pt-BR/enterprise/ejb-vs-cdi.html new file mode 100644 index 0000000..84eeac7 --- /dev/null +++ b/site/pt-BR/enterprise/ejb-vs-cdi.html @@ -0,0 +1,410 @@ + + + + + + EJB versus CDI | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

EJB versus CDI

+

Replace heavyweight EJBs with lightweight CDI beans for dependency injection and transactions.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@Stateless
+public class OrderEJB {
+    @EJB
+    private InventoryEJB inventory;
+
+    public void placeOrder(Order order) {
+        // container-managed transaction
+        inventory.reserve(order.getItem());
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@ApplicationScoped
+public class OrderService {
+    @Inject
+    private InventoryService inventory;
+
+    @Transactional
+    public void placeOrder(Order order) {
+        inventory.reserve(order.getItem());
+    }
+}
+
+
+
+
+ +
+ +
+
+
🪶
+

Lightweight

+

CDI beans are plain Java classes with no EJB-specific interfaces or descriptors.

+
+
+
💉
+

Unified injection

+

@Inject works for every managed bean, JAX-RS resources, and Jakarta EE components alike.

+
+
+
🧪
+

Easy unit testing

+

Plain classes without EJB proxy overhead are straightforward to instantiate and mock.

+
+
+
+ +
+
+
Abordagem Antiga
+
EJB
+
+
+
Abordagem Moderna
+
CDI Bean
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
EJB versus CDI
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

CDI (Contexts and Dependency Injection) provides the same dependency injection and transaction management as EJBs, but as plain Java classes with no container-specific interfaces or superclasses. Scopes like @ApplicationScoped and @RequestScoped control lifecycle, and @Transactional replaces mandatory EJB transaction semantics.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/jdbc-resultset-vs-jpa-criteria.html b/site/pt-BR/enterprise/jdbc-resultset-vs-jpa-criteria.html new file mode 100644 index 0000000..7d5562a --- /dev/null +++ b/site/pt-BR/enterprise/jdbc-resultset-vs-jpa-criteria.html @@ -0,0 +1,447 @@ + + + + + + JDBC ResultSet Mapping vs JPA Criteria API | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + advanced +
+

JDBC ResultSet Mapping vs JPA Criteria API

+

Replace manual JDBC ResultSet mapping with JPA's type-safe Criteria API for dynamic queries.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
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<User> users = new ArrayList<>();
+    while (rs.next()) {
+        User u = new User();
+        u.setId(rs.getLong("id"));
+        u.setName(rs.getString("name"));
+        u.setAge(rs.getInt("age"));
+        users.add(u);
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@PersistenceContext
+EntityManager em;
+
+public List<User> findActiveAboveAge(
+        String status, int minAge) {
+    CriteriaBuilder cb = em.getCriteriaBuilder();
+    CriteriaQuery<User> cq =
+        cb.createQuery(User.class);
+    Root<User> root = cq.from(User.class);
+    cq.select(root).where(
+        cb.equal(root.get("status"), status),
+        cb.greaterThan(root.get("age"), minAge));
+    return em.createQuery(cq).getResultList();
+}
+
+
+
+
+ +
+ +
+
+
🔒
+

Type-safe queries

+

The Criteria builder catches field name and type mismatches at compile time.

+
+
+
🗺️
+

Automatic mapping

+

JPA maps result rows to entity objects — no manual column-by-column extraction.

+
+
+
🧩
+

Composable predicates

+

Dynamic where-clauses build cleanly with and(), or(), and reusable Predicate objects.

+
+
+
+ +
+
+
Abordagem Antiga
+
JDBC ResultSet
+
+
+
Abordagem Moderna
+
JPA Criteria API
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
JDBC ResultSet Mapping vs JPA Criteria API
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

Raw JDBC requires building SQL strings, setting parameters by index, and mapping each ResultSet column manually — a process that is error-prone and breaks silently when columns change. The JPA Criteria API builds queries programmatically using a type-safe builder pattern. Column names are validated against the entity model, result mapping is automatic, and complex dynamic queries compose cleanly without string concatenation.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/jdbc-vs-jooq.html b/site/pt-BR/enterprise/jdbc-vs-jooq.html new file mode 100644 index 0000000..c017c82 --- /dev/null +++ b/site/pt-BR/enterprise/jdbc-vs-jooq.html @@ -0,0 +1,450 @@ + + + + + + JDBC versus jOOQ | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

JDBC versus jOOQ

+

Replace raw JDBC string-based SQL with jOOQ's type-safe, fluent SQL DSL.

+
+ +
+ +
+
+
+ ✕ Raw JDBC + +
+
+
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<User> result = new ArrayList<>();
+    while (rs.next()) {
+        result.add(new User(
+            rs.getLong("id"),
+            rs.getString("name"),
+            rs.getString("email")));
+    }
+    return result;
+}
+
+
+
+
+ ✓ jOOQ + +
+
+
DSLContext dsl = DSL.using(ds, SQLDialect.POSTGRES);
+
+return dsl
+    .select(USERS.ID, USERS.NAME, USERS.EMAIL)
+    .from(USERS)
+    .where(USERS.DEPARTMENT.eq(department)
+        .and(USERS.SALARY.gt(minSalary)))
+    .fetchInto(User.class);
+
+
+
+
+ +
+ +
+
+
🔒
+

Type-safe columns

+

Column names are generated Java constants — typos and type mismatches become compiler errors instead of runtime failures.

+
+
+
📖
+

SQL fluency

+

The jOOQ DSL mirrors SQL syntax closely, so complex JOINs, subqueries, and CTEs stay readable.

+
+
+
🛡️
+

Injection-free by design

+

Parameters are always bound safely — no string concatenation means no SQL injection risk.

+
+
+
+ +
+
+
Abordagem Antiga
+
Raw JDBC
+
+
+
Abordagem Moderna
+
jOOQ SQL DSL
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JDBC versus jOOQ
+ Disponível +

jOOQ open-source edition supports all major open-source databases; older commercial databases require a paid license

+
+
+ +
+ +

jOOQ (Java Object Oriented Querying) generates Java code from your database schema, turning table and column names into type-safe Java constants. The fluent DSL mirrors SQL syntax so queries are readable and composable. All parameters are bound automatically, eliminating SQL injection risk. Unlike JPA/JPQL, jOOQ embraces SQL fully — window functions, CTEs, RETURNING clauses, and vendor-specific extensions are all first-class.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/jdbc-vs-jpa.html b/site/pt-BR/enterprise/jdbc-vs-jpa.html new file mode 100644 index 0000000..ec86f69 --- /dev/null +++ b/site/pt-BR/enterprise/jdbc-vs-jpa.html @@ -0,0 +1,412 @@ + + + + + + JDBC versus JPA | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

JDBC versus JPA

+

Replace verbose JDBC boilerplate with JPA's object-relational mapping and EntityManager.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
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"));
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@PersistenceContext
+EntityManager em;
+
+public User findUser(Long id) {
+    return em.find(User.class, id);
+}
+
+public List<User> findByName(String name) {
+    return em.createQuery(
+        "SELECT u FROM User u WHERE u.name = :name",
+        User.class)
+        .setParameter("name", name)
+        .getResultList();
+}
+
+
+
+
+ +
+ +
+
+
🗺️
+

Object mapping

+

Entities are plain annotated classes — no manual ResultSet-to-object translation.

+
+
+
🔒
+

Type-safe queries

+

JPQL operates on entity types and fields rather than raw table and column strings.

+
+
+
+

Built-in caching

+

First- and second-level caches reduce database round-trips automatically.

+
+
+
+ +
+
+
Abordagem Antiga
+
JDBC
+
+
+
Abordagem Moderna
+
JPA EntityManager
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JDBC versus JPA
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

JPA (Jakarta Persistence API) maps Java objects to database rows, eliminating manual ResultSet processing and SQL string concatenation. EntityManager provides find(), persist(), and JPQL queries so you work with domain objects instead of raw SQL, while the container manages connection pooling and transactions.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/jndi-lookup-vs-cdi-injection.html b/site/pt-BR/enterprise/jndi-lookup-vs-cdi-injection.html new file mode 100644 index 0000000..02e2d37 --- /dev/null +++ b/site/pt-BR/enterprise/jndi-lookup-vs-cdi-injection.html @@ -0,0 +1,450 @@ + + + + + + JNDI Lookup vs CDI Injection | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

JNDI Lookup vs CDI Injection

+

Replace fragile JNDI string lookups with type-safe CDI injection for container-managed resources.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
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<Order> findAll()
+            throws SQLException {
+        try (Connection con = ds.getConnection()) {
+            // query orders
+        }
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@ApplicationScoped
+public class OrderService {
+    @Inject
+    @Resource(name = "jdbc/OrderDB")
+    DataSource ds;
+
+    public List<Order> findAll()
+            throws SQLException {
+        try (Connection con = ds.getConnection()) {
+            // query orders
+        }
+    }
+}
+
+
+
+
+ +
+ +
+
+
🔒
+

Type-safe wiring

+

Injection errors are caught at deployment time, not at runtime via string lookups.

+
+
+
🗑️
+

No boilerplate

+

Eliminates InitialContext creation, JNDI name strings, and NamingException handling.

+
+
+
🧪
+

Testable

+

Dependencies are injected fields, easily replaced with mocks in unit tests.

+
+
+
+ +
+
+
Abordagem Antiga
+
JNDI Lookup
+
+
+
Abordagem Moderna
+
CDI @Inject
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JNDI Lookup vs CDI Injection
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

The traditional JNDI pattern forces you to use string-based resource names, handle NamingException, and manage an InitialContext. CDI injection with @Inject (or @Resource for container resources) lets the container wire dependencies automatically. Typos become compile-time errors, and classes are easier to test because dependencies can be injected directly.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/jpa-vs-jakarta-data.html b/site/pt-BR/enterprise/jpa-vs-jakarta-data.html new file mode 100644 index 0000000..6a6b84d --- /dev/null +++ b/site/pt-BR/enterprise/jpa-vs-jakarta-data.html @@ -0,0 +1,424 @@ + + + + + + JPA versus Jakarta Data | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

JPA versus Jakarta Data

+

Declare a repository interface and let Jakarta Data generate the DAO implementation automatically.

+
+ +
+ +
+
+
+ ✕ Jakarta EE 8+ + +
+
+
@PersistenceContext
+EntityManager em;
+
+public User findById(Long id) {
+    return em.find(User.class, id);
+}
+
+public List<User> findByName(String name) {
+    return em.createQuery(
+        "SELECT u FROM User u WHERE u.name = :name",
+        User.class)
+        .setParameter("name", name)
+        .getResultList();
+}
+
+public void save(User user) {
+    em.persist(user);
+}
+
+
+
+
+ ✓ Jakarta EE 11+ + +
+
+
@Repository
+public interface Users extends CrudRepository<User, Long> {
+    List<User> findByName(String name);
+}
+
+
+
+
+ +
+ +
+
+
🪄
+

Zero boilerplate

+

Declare the interface; the container generates the full DAO implementation at deploy time.

+
+
+
🔍
+

Derived queries

+

Method names like findByNameAndStatus are parsed automatically — no JPQL or SQL needed.

+
+
+
🔌
+

Portable

+

Any Jakarta EE 11 compliant runtime provides the repository implementation with no vendor lock-in.

+
+
+
+ +
+
+
Abordagem Antiga
+
JPA EntityManager
+
+
+
Abordagem Moderna
+
Jakarta Data Repository
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JPA versus Jakarta Data
+ Disponível +

Available since Jakarta EE 11 / Java 21 (2024)

+
+
+ +
+ +

Jakarta Data (Jakarta EE 11) turns data access into a pure interface declaration. You annotate an interface with @Repository and extend a built-in repository type such as CrudRepository. The runtime generates the implementation — including derived queries from method names like findByName — so there is no EntityManager boilerplate, no JPQL strings, and no hand-written save/find methods.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/jsf-managed-bean-vs-cdi-named.html b/site/pt-BR/enterprise/jsf-managed-bean-vs-cdi-named.html new file mode 100644 index 0000000..2b7831d --- /dev/null +++ b/site/pt-BR/enterprise/jsf-managed-bean-vs-cdi-named.html @@ -0,0 +1,435 @@ + + + + + + JSF Managed Bean vs CDI Named Bean | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

JSF Managed Bean vs CDI Named Bean

+

Replace deprecated JSF @ManagedBean with CDI @Named for a unified dependency injection model.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@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;
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 10+ + +
+
+
@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;
+    }
+}
+
+
+
+
+ +
+ +
+
+
🔗
+

Unified model

+

One CDI container manages all beans — JSF, REST, and service layers share the same injection.

+
+
+
🗑️
+

Less boilerplate

+

@Inject replaces @ManagedProperty and its required setter method.

+
+
+
🔮
+

Future-proof

+

@ManagedBean is removed in Jakarta EE 10; @Named is the supported replacement.

+
+
+
+ +
+
+
Abordagem Antiga
+
@ManagedBean
+
+
+
Abordagem Moderna
+
@Named + CDI
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JSF Managed Bean vs CDI Named Bean
+ Disponível +

CDI @Named available since Java EE 6; @ManagedBean removed in Jakarta EE 10

+
+
+ +
+ +

JSF's @ManagedBean and @ManagedProperty were deprecated in Jakarta Faces 2.3 and removed in Jakarta EE 10. The CDI-based replacement uses @Named to expose the bean to EL expressions and @Inject for dependency wiring. This unifies the bean model: JSF pages, JAX-RS resources, and EJBs all share the same CDI container.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/manual-transaction-vs-declarative.html b/site/pt-BR/enterprise/manual-transaction-vs-declarative.html new file mode 100644 index 0000000..a3a17df --- /dev/null +++ b/site/pt-BR/enterprise/manual-transaction-vs-declarative.html @@ -0,0 +1,436 @@ + + + + + + Manual JPA Transaction vs Declarative @Transactional | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

Manual JPA Transaction vs Declarative @Transactional

+

Replace verbose begin/commit/rollback blocks with a single @Transactional annotation.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@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;
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@ApplicationScoped
+public class AccountService {
+    @PersistenceContext
+    EntityManager em;
+
+    @Transactional
+    public void transferFunds(Long from, Long to,
+                              BigDecimal amount) {
+        Account src = em.find(Account.class, from);
+        Account dst = em.find(Account.class, to);
+        src.debit(amount);
+        dst.credit(amount);
+    }
+}
+
+
+
+
+ +
+ +
+
+
🗑️
+

No boilerplate

+

One annotation replaces repetitive begin/commit/rollback try-catch blocks.

+
+
+
🛡️
+

Safer rollback

+

The container guarantees rollback on unchecked exceptions — no risk of forgetting the catch block.

+
+
+
📐
+

Declarative control

+

Propagation, isolation, and rollback rules are expressed as annotation attributes.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Transaction
+
+
+
Abordagem Moderna
+
@Transactional
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Manual JPA Transaction vs Declarative @Transactional
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

Manual transaction management requires explicit begin(), commit(), and rollback() calls wrapped in try-catch blocks — every service method repeats this boilerplate. The @Transactional annotation delegates lifecycle management to the container: it begins a transaction before the method, commits on success, and rolls back on RuntimeException automatically.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/mdb-vs-reactive-messaging.html b/site/pt-BR/enterprise/mdb-vs-reactive-messaging.html new file mode 100644 index 0000000..a45a055 --- /dev/null +++ b/site/pt-BR/enterprise/mdb-vs-reactive-messaging.html @@ -0,0 +1,402 @@ + + + + + + Message-Driven Bean vs Reactive Messaging | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + advanced +
+

Message-Driven Bean vs Reactive Messaging

+

Replace JMS Message-Driven Beans with MicroProfile Reactive Messaging for simpler event processing.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@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());
+    }
+}
+
+
+
+
+ ✓ MicroProfile 4+ + +
+
+
@ApplicationScoped
+public class OrderProcessor {
+    @Incoming("orders")
+    public void process(Order order) {
+        // automatically deserialized from
+        // the "orders" channel
+        fulfillOrder(order);
+    }
+}
+
+
+
+
+ +
+ +
+
+
🪶
+

Minimal code

+

A single @Incoming method replaces the MDB class, MessageListener interface, and activation config.

+
+
+
🔌
+

Broker-agnostic

+

Swap Kafka, AMQP, or JMS connectors via configuration without changing application code.

+
+
+
☁️
+

Cloud-native fit

+

Reactive streams backpressure and lightweight runtime make it ideal for containerised deployments.

+
+
+
+ +
+
+
Abordagem Antiga
+
Message-Driven Bean
+
+
+
Abordagem Moderna
+
Reactive Messaging
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Message-Driven Bean vs Reactive Messaging
+ Disponível +

Available since MicroProfile 4.0 / SmallRye Reactive Messaging

+
+
+ +
+ +

Message-Driven Beans require implementing MessageListener, configuring activation properties, and manually deserializing JMS messages. MicroProfile Reactive Messaging uses a simple @Incoming annotation on a method that receives typed objects directly. The channel configuration is externalised, making the code broker-agnostic and far easier to test.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/servlet-vs-jaxrs.html b/site/pt-BR/enterprise/servlet-vs-jaxrs.html new file mode 100644 index 0000000..edc1158 --- /dev/null +++ b/site/pt-BR/enterprise/servlet-vs-jaxrs.html @@ -0,0 +1,416 @@ + + + + + + Servlet versus JAX-RS | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

Servlet versus JAX-RS

+

Replace verbose HttpServlet boilerplate with declarative JAX-RS resource classes.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@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 + "\"}");
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@Path("/users")
+public class UserResource {
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getUser(
+            @QueryParam("id") String id) {
+        return Response.ok(new User(id)).build();
+    }
+}
+
+
+
+
+ +
+ +
+
+
📐
+

Declarative routing

+

Annotations define HTTP method, path, and content type instead of imperative if/else dispatch.

+
+
+
🔄
+

Automatic marshalling

+

Return POJOs directly; the runtime serialises them to JSON or XML based on @Produces.

+
+
+
🧪
+

Easier testing

+

Resource classes are plain Java objects, testable without a servlet container.

+
+
+
+ +
+
+
Abordagem Antiga
+
HttpServlet
+
+
+
Abordagem Moderna
+
JAX-RS Resource
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Servlet versus JAX-RS
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

JAX-RS (Jakarta RESTful Web Services) lets you expose REST endpoints using simple annotations like @GET, @Path, and @Produces. No more manual parsing of request parameters or setting content types on the response — the runtime handles marshalling and routing automatically.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/singleton-ejb-vs-cdi-application-scoped.html b/site/pt-BR/enterprise/singleton-ejb-vs-cdi-application-scoped.html new file mode 100644 index 0000000..0fbc9d2 --- /dev/null +++ b/site/pt-BR/enterprise/singleton-ejb-vs-cdi-application-scoped.html @@ -0,0 +1,445 @@ + + + + + + Singleton EJB vs CDI @ApplicationScoped | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

Singleton EJB vs CDI @ApplicationScoped

+

Replace Singleton EJBs with CDI @ApplicationScoped beans for simpler shared-state management.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@Singleton
+@Startup
+@ConcurrencyManagement(
+    ConcurrencyManagementType.CONTAINER)
+public class ConfigCache {
+    private Map<String, String> cache;
+
+    @PostConstruct
+    public void load() {
+        cache = loadFromDatabase();
+    }
+
+    @Lock(LockType.READ)
+    public String get(String key) {
+        return cache.get(key);
+    }
+
+    @Lock(LockType.WRITE)
+    public void refresh() {
+        cache = loadFromDatabase();
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@ApplicationScoped
+public class ConfigCache {
+    private volatile Map<String, String> cache;
+
+    @PostConstruct
+    public void load() {
+        cache = loadFromDatabase();
+    }
+
+    public String get(String key) {
+        return cache.get(key);
+    }
+
+    public void refresh() {
+        cache = loadFromDatabase();
+    }
+}
+
+
+
+
+ +
+ +
+
+
🪶
+

Less annotation noise

+

No @ConcurrencyManagement, @Lock, or @Startup — just a single @ApplicationScoped annotation.

+
+
+
🔧
+

Flexible concurrency

+

Use java.util.concurrent locks or volatile for exactly the thread-safety you need.

+
+
+
🧪
+

Easy testing

+

Plain CDI beans can be instantiated directly in tests without an EJB container.

+
+
+
+ +
+
+
Abordagem Antiga
+
@Singleton EJB
+
+
+
Abordagem Moderna
+
@ApplicationScoped CDI
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Singleton EJB vs CDI @ApplicationScoped
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

Singleton EJBs bundle concurrency management (@Lock, @ConcurrencyManagement) and eager initialisation (@Startup) into the EJB container. A CDI @ApplicationScoped bean achieves the same single-instance lifecycle with far less ceremony. When concurrency control is needed, standard java.util.concurrent utilities give you finer-grained control than the EJB lock annotations.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/soap-vs-jakarta-rest.html b/site/pt-BR/enterprise/soap-vs-jakarta-rest.html new file mode 100644 index 0000000..12863c6 --- /dev/null +++ b/site/pt-BR/enterprise/soap-vs-jakarta-rest.html @@ -0,0 +1,414 @@ + + + + + + SOAP Web Services vs Jakarta REST | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

SOAP Web Services vs Jakarta REST

+

Replace heavyweight SOAP/WSDL endpoints with clean Jakarta REST resources returning JSON.

+
+ +
+ +
+
+
+ ✕ Java EE + +
+
+
@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;
+    }
+}
+
+
+
+
+ ✓ Jakarta EE 8+ + +
+
+
@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);
+    }
+}
+
+
+
+
+ +
+ +
+
+
🪶
+

Lighter payloads

+

JSON is more compact than SOAP XML envelopes, reducing bandwidth and parsing overhead.

+
+
+
📐
+

Simple annotations

+

@GET, @Path, and @Produces replace WSDL, @WebService, and @WebMethod ceremony.

+
+
+
🔌
+

Microservice-ready

+

REST/JSON is the standard for service-to-service communication in cloud-native architectures.

+
+
+
+ +
+
+
Abordagem Antiga
+
JAX-WS / SOAP
+
+
+
Abordagem Moderna
+
Jakarta REST / JSON
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
SOAP Web Services vs Jakarta REST
+ Disponível +

Widely available since Jakarta EE 8 / Java 11

+
+
+ +
+ +

SOAP-based web services rely on WSDL contracts, XML marshalling, and JAX-WS annotations that add significant overhead. Jakarta REST (formerly JAX-RS) uses intuitive annotations like @GET, @Path, and @Produces to expose RESTful JSON APIs. The programming model is simpler, the payloads are smaller, and the approach aligns with how modern microservices communicate.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/spring-api-versioning.html b/site/pt-BR/enterprise/spring-api-versioning.html new file mode 100644 index 0000000..0f3b1ef --- /dev/null +++ b/site/pt-BR/enterprise/spring-api-versioning.html @@ -0,0 +1,445 @@ + + + + + + Spring Framework 7 API Versioning | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

Spring Framework 7 API Versioning

+

Replace duplicated version-prefixed controllers with Spring Framework 7's native API versioning support.

+
+ +
+ +
+
+
+ ✕ Spring Boot 2/3 + +
+
+
// 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);
+    }
+}
+
+
+
+
+ ✓ Spring Framework 7+ + +
+
+
// 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);
+    }
+}
+
+
+
+
+ +
+ +
+
+
🗂️
+

No controller duplication

+

All versions live in one controller class; only the individual handler methods carry a version attribute.

+
+
+
⚙️
+

Centralised version strategy

+

Switch from header to URL or query-param versioning in a single configureApiVersioning call.

+
+
+
📈
+

Incremental evolution

+

Add a new version to one method without touching unrelated endpoints or creating new controller files.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual URL Path Versioning
+
+
+
Abordagem Moderna
+
Native API Versioning
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Spring Framework 7 API Versioning
+ Disponível +

Available since Spring Framework 7.0 (requires Java 17+)

+
+
+ +
+ +

Before Spring Framework 7, API versioning required separate controller classes per version (e.g., /api/v1/products, /api/v2/products), duplicating request mappings and scattering version logic across many files. Spring Framework 7 introduces native versioning through a new version attribute on @RequestMapping and related annotations, plus a configureApiVersioning hook in WebMvcConfigurer. The version can be resolved from a request header, a URL path segment, or a query parameter — all controlled in one place.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/spring-null-safety-jspecify.html b/site/pt-BR/enterprise/spring-null-safety-jspecify.html new file mode 100644 index 0000000..fd708c7 --- /dev/null +++ b/site/pt-BR/enterprise/spring-null-safety-jspecify.html @@ -0,0 +1,399 @@ + + + + + + Spring Null Safety with JSpecify | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

Spring Null Safety with JSpecify

+

Spring 7 adopts JSpecify annotations, making non-null the default and reducing annotation noise.

+
+ +
+ +
+
+
+ ✕ Spring 5/6 + +
+
+
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<User> findAll() {
+        return repository.findAll();
+    }
+
+    @NonNull
+    public User save(@NonNull User user) {
+        return repository.save(user);
+    }
+}
+
+
+
+
+ ✓ Spring 7 + +
+
+
import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+public class UserService {
+
+    public @Nullable User findById(String id) {
+        return repository.findById(id).orElse(null);
+    }
+
+    public List<User> findAll() {
+        return repository.findAll();
+    }
+
+    public User save(User user) {
+        return repository.save(user);
+    }
+}
+
+
+
+
+ +
+ +
+
+
✂️
+

Non-null by default

+

@NullMarked makes all unannotated types non-null, so only nullable exceptions need annotation.

+
+
+
🌐
+

Ecosystem standard

+

JSpecify annotations are a cross-framework standard recognized by NullAway, Error Prone, and IDEs.

+
+
+
🔍
+

Richer tooling

+

Modern static analyzers understand JSpecify's null model and report violations at compile time.

+
+
+
+ +
+
+
Abordagem Antiga
+
Spring @NonNull/@Nullable
+
+
+
Abordagem Moderna
+
JSpecify @NullMarked
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Spring Null Safety with JSpecify
+ Disponível +

Available since Spring Framework 7.0 (requires Java 17+)

+
+
+ +
+ +

Spring 5 and 6 introduced their own null safety annotations in the `org.springframework.lang` package. While useful, these were framework-specific and required annotating every non-null element explicitly. Spring 7 migrates to JSpecify, a cross-ecosystem standard for null safety. The `@NullMarked` annotation at the class or package level declares that all unannotated types are non-null by default. Only actual nullable types need the `@Nullable` annotation, dramatically reducing verbosity. JSpecify annotations are recognized by major static analysis tools such as NullAway, Error Prone, and IntelliJ IDEA, bringing richer tooling support beyond what Spring-specific annotations provided.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/enterprise/spring-xml-config-vs-annotations.html b/site/pt-BR/enterprise/spring-xml-config-vs-annotations.html new file mode 100644 index 0000000..13493c5 --- /dev/null +++ b/site/pt-BR/enterprise/spring-xml-config-vs-annotations.html @@ -0,0 +1,458 @@ + + + + + + Spring XML Bean Config vs Annotation-Driven | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Enterprise + intermediate +
+

Spring XML Bean Config vs Annotation-Driven

+

Replace verbose Spring XML bean definitions with concise annotation-driven configuration in Spring Boot.

+
+ +
+ +
+
+
+ ✕ Spring (XML) + +
+
+
<!-- applicationContext.xml -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <bean id="userRepository"
+          class="com.example.UserRepository">
+        <property name="dataSource" ref="dataSource"/>
+    </bean>
+
+    <bean id="userService"
+          class="com.example.UserService">
+        <property name="repository" ref="userRepository"/>
+    </bean>
+
+</beans>
+
+
+
+
+ ✓ Spring Boot 3+ + +
+
+
@SpringBootApplication
+public class Application {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}
+
+@Repository
+public class UserRepository {
+    private final JdbcTemplate jdbc;
+
+    public UserRepository(JdbcTemplate jdbc) {
+        this.jdbc = jdbc;
+    }
+}
+
+@Service
+public class UserService {
+    private final UserRepository repository;
+
+    public UserService(UserRepository repository) {
+        this.repository = repository;
+    }
+}
+
+
+
+
+ +
+ +
+
+
🚫
+

No XML

+

@SpringBootApplication triggers component scanning and auto-configuration, eliminating all XML wiring files.

+
+
+
💉
+

Constructor injection

+

Spring injects dependencies through constructors automatically, making beans easier to test and reason about.

+
+
+
+

Auto-configuration

+

Spring Boot configures DataSource, JPA, and other infrastructure from the classpath with zero boilerplate.

+
+
+
+ +
+
+
Abordagem Antiga
+
XML Bean Definitions
+
+
+
Abordagem Moderna
+
Annotation-Driven Beans
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Spring XML Bean Config vs Annotation-Driven
+ Disponível +

Widely available since Spring Boot 1.0 (April 2014); Spring Boot 3 requires Java 17+

+
+
+ +
+ +

Traditional Spring applications wired beans through XML configuration files, declaring each class and its dependencies as verbose <bean> elements. While annotation support existed since Spring 2.5, XML remained the dominant approach until Spring Boot introduced auto-configuration. Spring Boot detects beans annotated with @Component, @Service, @Repository, and @Controller via classpath scanning, satisfies dependencies through constructor injection automatically, and configures infrastructure like DataSource from the classpath — eliminating all XML wiring files.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/helpful-npe.html b/site/pt-BR/errors/helpful-npe.html new file mode 100644 index 0000000..e8da794 --- /dev/null +++ b/site/pt-BR/errors/helpful-npe.html @@ -0,0 +1,383 @@ + + + + + + Helpful NullPointerExceptions | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + beginner +
+

Helpful NullPointerExceptions

+

JVM automatically tells you exactly which variable was null.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Old NPE message:
+// "NullPointerException"
+// at MyApp.main(MyApp.java:42)
+// Which variable was null?!
+
+
+
+
+ ✓ Java 14+ + +
+
+
// Modern NPE message:
+// Cannot invoke "String.length()"
+// because "user.address().city()"
+// is null
+// Exact variable identified!
+
+
+
+
+ +
+ +
+
+
🔍
+

Exact variable

+

The message names the null variable in the chain.

+
+
+
+

Faster debugging

+

No more guessing which of 5 chained calls was null.

+
+
+
🆓
+

Free upgrade

+

No code changes — just run on JDK 14+.

+
+
+
+ +
+
+
Abordagem Antiga
+
Cryptic NPE
+
+
+
Abordagem Moderna
+
Detailed NPE
+
+
+
Desde o JDK
+
14
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Helpful NullPointerExceptions
+ Disponível +

Widely available since JDK 14 (March 2020)

+
+
+ +
+ +

Helpful NPEs describe which expression was null and what operation failed. This is enabled by default since Java 14 — no code change needed, just upgrade the JDK.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/multi-catch.html b/site/pt-BR/errors/multi-catch.html new file mode 100644 index 0000000..914203d --- /dev/null +++ b/site/pt-BR/errors/multi-catch.html @@ -0,0 +1,383 @@ + + + + + + Multi-catch exception handling | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + beginner +
+

Multi-catch exception handling

+

Catch multiple exception types in a single catch block.

+
+ +
+ +
+
+
+ ✕ Pre-Java 7 + +
+
+
try {
+    process();
+} catch (IOException e) {
+    log(e);
+} catch (SQLException e) {
+    log(e);
+} catch (ParseException e) {
+    log(e);
+}
+
+
+
+
+ ✓ Java 7+ + +
+
+
try {
+    process();
+} catch (IOException
+    | SQLException
+    | ParseException e) {
+    log(e);
+}
+
+
+
+
+ +
+ +
+
+
📏
+

DRY

+

Same handling logic written once instead of three times.

+
+
+
🔄
+

Rethrowable

+

The caught exception can be rethrown with its precise type.

+
+
+
📖
+

Scannable

+

All handled types are visible in one place.

+
+
+
+ +
+
+
Abordagem Antiga
+
Separate Catch Blocks
+
+
+
Abordagem Moderna
+
Multi-catch
+
+
+
Desde o JDK
+
7
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Multi-catch exception handling
+ Disponível +

Widely available since JDK 7 (July 2011)

+
+
+ +
+ +

Multi-catch handles multiple exception types with the same code. The exception variable is effectively final, so you can rethrow it without wrapping.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/null-in-switch.html b/site/pt-BR/errors/null-in-switch.html new file mode 100644 index 0000000..eca8f32 --- /dev/null +++ b/site/pt-BR/errors/null-in-switch.html @@ -0,0 +1,391 @@ + + + + + + Null case in switch | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + beginner +
+

Null case in switch

+

Handle null directly as a switch case — no separate guard needed.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Must check before switch
+if (status == null) {
+    return "unknown";
+}
+return switch (status) {
+    case ACTIVE  -> "active";
+    case PAUSED  -> "paused";
+    default      -> "other";
+};
+
+
+
+
+ ✓ Java 21+ + +
+
+
return switch (status) {
+    case null    -> "unknown";
+    case ACTIVE  -> "active";
+    case PAUSED  -> "paused";
+    default      -> "other";
+};
+
+
+
+
+ +
+ +
+
+
🎯
+

Explicit

+

null handling is visible right in the switch.

+
+
+
🛡️
+

No NPE

+

Switch on a null value won't throw NullPointerException.

+
+
+
📐
+

All-in-one

+

All cases including null in a single switch expression.

+
+
+
+ +
+
+
Abordagem Antiga
+
Guard Before Switch
+
+
+
Abordagem Moderna
+
case null
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Null case in switch
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Pattern matching switch can match null as a case label. This eliminates the need for a null check before the switch and makes null handling explicit and visible.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/optional-chaining.html b/site/pt-BR/errors/optional-chaining.html new file mode 100644 index 0000000..4a1a2ed --- /dev/null +++ b/site/pt-BR/errors/optional-chaining.html @@ -0,0 +1,380 @@ + + + + + + Optional chaining | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + beginner +
+

Optional chaining

+

Replace nested null checks with an Optional pipeline.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String city = null;
+if (user != null) {
+    Address addr = user.getAddress();
+    if (addr != null) {
+        city = addr.getCity();
+    }
+}
+if (city == null) city = "Unknown";
+
+
+
+
+ ✓ Java 9+ + +
+
+
String city = Optional.ofNullable(user)
+    .map(User::address)
+    .map(Address::city)
+    .orElse("Unknown");
+
+
+
+
+ +
+ +
+
+
🔗
+

Chainable

+

Each .map() step handles null transparently.

+
+
+
📖
+

Linear flow

+

Read left-to-right instead of nested if-blocks.

+
+
+
🛡️
+

NPE-proof

+

null is handled at each step — no crash possible.

+
+
+
+ +
+
+
Abordagem Antiga
+
Nested Null Checks
+
+
+
Abordagem Moderna
+
Optional Pipeline
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Optional chaining
+ Disponível +

Available since JDK 8+ (improved in 9+)

+
+
+ +
+ +

Optional.map() chains through nullable values, short-circuiting on the first null. orElse() provides the default. This eliminates pyramid-of-doom null checking.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/optional-orelsethrow.html b/site/pt-BR/errors/optional-orelsethrow.html new file mode 100644 index 0000000..9207b92 --- /dev/null +++ b/site/pt-BR/errors/optional-orelsethrow.html @@ -0,0 +1,373 @@ + + + + + + Optional.orElseThrow() without supplier | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + beginner +
+

Optional.orElseThrow() without supplier

+

Use Optional.orElseThrow() as a clearer, intent-revealing alternative to get().

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Risky: get() throws if empty, no clear intent
+String value = optional.get();
+
+// Verbose: supplier just for NoSuchElementException
+String value = optional
+    .orElseThrow(NoSuchElementException::new);
+
+
+
+
+ ✓ Java 10+ + +
+
+
// Clear intent: throws NoSuchElementException if empty
+String value = optional.orElseThrow();
+
+
+
+
+ +
+ +
+
+
📖
+

Self-documenting

+

orElseThrow() clearly signals that absence is unexpected.

+
+
+
🔒
+

Avoids get()

+

Static analysis tools flag get() as risky; orElseThrow() is idiomatic.

+
+
+
+

Less boilerplate

+

No need to pass a supplier for the default NoSuchElementException.

+
+
+
+ +
+
+
Abordagem Antiga
+
get() or orElseThrow(supplier)
+
+
+
Abordagem Moderna
+
orElseThrow()
+
+
+
Desde o JDK
+
10
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Optional.orElseThrow() without supplier
+ Disponível +

Available since JDK 10 (March 2018).

+
+
+ +
+ +

Optional.get() is widely considered a code smell because it hides the possibility of failure. The no-arg orElseThrow(), added in Java 10, does exactly the same thing but makes the intent explicit: the developer expects a value and wants an exception if absent.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/record-based-errors.html b/site/pt-BR/errors/record-based-errors.html new file mode 100644 index 0000000..8fb2749 --- /dev/null +++ b/site/pt-BR/errors/record-based-errors.html @@ -0,0 +1,385 @@ + + + + + + Record-based error responses | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + intermediate +
+

Record-based error responses

+

Use records for concise, immutable error response types.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Verbose error class
+public class ErrorResponse {
+    private final int code;
+    private final String message;
+    // constructor, getters, equals,
+    // hashCode, toString...
+}
+
+
+
+
+ ✓ Java 16+ + +
+
+
public record ApiError(
+    int code,
+    String message,
+    Instant timestamp
+) {
+    public ApiError(int code, String msg) {
+        this(code, msg, Instant.now());
+    }
+}
+
+
+
+
+ +
+ +
+
+
📏
+

Concise

+

Define error types in 3 lines instead of 30.

+
+
+
🔒
+

Immutable

+

Error data can't be accidentally modified after creation.

+
+
+
📋
+

Auto toString

+

Perfect for logging — shows all fields automatically.

+
+
+
+ +
+
+
Abordagem Antiga
+
Map or Verbose Class
+
+
+
Abordagem Moderna
+
Error Records
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Record-based error responses
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

Records are perfect for error responses — they're immutable, have built-in equals/hashCode for comparison, and toString for logging. Custom constructors add validation or defaults.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/errors/require-nonnull-else.html b/site/pt-BR/errors/require-nonnull-else.html new file mode 100644 index 0000000..0314d0e --- /dev/null +++ b/site/pt-BR/errors/require-nonnull-else.html @@ -0,0 +1,381 @@ + + + + + + Objects.requireNonNullElse() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Errors + beginner +
+

Objects.requireNonNullElse()

+

Get a non-null value with a clear default, no ternary needed.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String name = input != null
+    ? input
+    : "default";
+// easy to get the order wrong
+
+
+
+
+ ✓ Java 9+ + +
+
+
String name = Objects
+    .requireNonNullElse(
+        input, "default"
+    );
+
+
+
+
+ +
+ +
+
+
📖
+

Clear intent

+

Method name describes exactly what it does.

+
+
+
🛡️
+

Null-safe default

+

The default value is also checked for null.

+
+
+
📏
+

Readable

+

Better than ternary for simple null-or-default logic.

+
+
+
+ +
+
+
Abordagem Antiga
+
Ternary Null Check
+
+
+
Abordagem Moderna
+
requireNonNullElse()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Objects.requireNonNullElse()
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

requireNonNullElse returns the first argument if non-null, otherwise the second. The default itself cannot be null — it throws NPE if both are null, catching bugs early.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/index.html b/site/pt-BR/index.html new file mode 100644 index 0000000..d7edb51 --- /dev/null +++ b/site/pt-BR/index.html @@ -0,0 +1,4309 @@ + + + + + + java.evolved Code Snippets | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
✦ 112 padrões modernos · Java 8 → Java 25
+

O Java evoluiu.
Seu código também pode.

+

Uma coleção de snippets modernos de Java. Cada padrão antigo ao lado da sua substituição moderna e limpa — lado a lado.

+ +
+
✕ Antigo
+
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
+}
+
+
+
✓ Moderno
+
public record Point(int x, int y) {}
+
+
+
+ + + +
+
+ 🤖 +
+ Modernize sua base de código Java com o GitHub Copilot. + Deixe o Copilot ajudá-lo a migrar padrões legados para Java moderno — automaticamente. +
+ +
+
+ +
+
+

Todas as comparações

+ 112 snippets +
+
+ Mostrar: + + + + + + + + + + + + +
+
+ +
+
+ +
+
+
Language
+
+

Compact canonical constructor

+
+
+
+ Antigo + public record Person(String name, + List<String> pets) { + // Full canonical constructor + public Person(String name, + List<String> pets) { + Objects.requireNonNull(name); + this.name = name; + this.pets = List.copyOf(pets); + } +} +
+
+ Moderno + public record Person(String name, + List<String> pets) { + // Compact constructor + public Person { + Objects.requireNonNull(name); + pets = List.copyOf(pets); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Compact source files

+
+
+
+ Antigo + public class HelloWorld { + public static void main(String[] args) { + System.out.println( + "Hello, World!"); + } +} +
+
+ Moderno + void main() { + IO.println("Hello, World!"); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Default interface methods

+
+
+
+ Antigo + // 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 { ... } +
+
+ Moderno + public interface Logger { + default void log(String msg) { + System.out.println( + timestamp() + ": " + msg); + } + String timestamp(); +} + +// Multiple interfaces allowed +public class FileLogger + implements Logger, Closeable { ... } +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Diamond with anonymous classes

+
+
+
+ Antigo + Map<String, List<String>> map = + new HashMap<String, List<String>>(); +// anonymous class: no diamond +Predicate<String> p = + new Predicate<String>() { + public boolean test(String s) {..} + }; +
+
+ Moderno + Map<String, List<String>> map = + new HashMap<>(); +// Java 9: diamond with anonymous classes +Predicate<String> p = + new Predicate<>() { + public boolean test(String s) {..} + }; +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Exhaustive switch without default

+
+
+
+ Antigo + // 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(); +} +
+
+ Moderno + // 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! +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Flexible constructor bodies

+
+
+
+ Antigo + class Square extends Shape { + Square(double side) { + super(side, side); + // can't validate BEFORE super! + if (side <= 0) + throw new IAE("bad"); + } +} +
+
+ Moderno + class Square extends Shape { + Square(double side) { + if (side <= 0) + throw new IAE("bad"); + super(side, side); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Guarded patterns with when

+
+
+
+ Antigo + if (shape instanceof Circle c) { + if (c.radius() > 10) { + return "large circle"; + } else { + return "small circle"; + } +} else { + return "not a circle"; +} +
+
+ Moderno + return switch (shape) { + case Circle c + when c.radius() > 10 + -> "large circle"; + case Circle c + -> "small circle"; + default -> "not a circle"; +}; +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Markdown in Javadoc comments

+
+
+
+ Antigo + /** + * Returns the {@code User} with + * the given ID. + * + * <p>Example: + * <pre>{@code + * var user = findUser(123); + * }</pre> + * + * @param id the user ID + * @return the user + */ +public User findUser(int id) { ... } +
+
+ Moderno + /// 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) { ... } +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Module import declarations

+
+
+
+ Antigo + 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; +
+
+ Moderno + import module java.base; + +// All of java.util, java.io, java.nio +// etc. available in one line +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Pattern matching for instanceof

+
+
+
+ Antigo + if (obj instanceof String) { + String s = (String) obj; + System.out.println(s.length()); +} +
+
+ Moderno + if (obj instanceof String s) { + System.out.println(s.length()); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Pattern matching in switch

+
+
+
+ Antigo + 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"; +} +
+
+ Moderno + String format(Object obj) { + return switch (obj) { + case Integer i -> "int: " + i; + case Double d -> "double: " + d; + case String s -> "str: " + s; + default -> "unknown"; + }; +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Primitive types in patterns

+
+
+
+ Antigo + String classify(int code) { + if (code >= 200 && code < 300) + return "success"; + else if (code >= 400 && code < 500) + return "client error"; + else + return "other"; +} +
+
+ Moderno + 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"; + }; +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Private interface methods

+
+
+
+ Antigo + interface Logger { + default void logInfo(String msg) { + System.out.println( + "[INFO] " + timestamp() + msg); + } + default void logWarn(String msg) { + System.out.println( + "[WARN] " + timestamp() + msg); + } +} +
+
+ Moderno + interface Logger { + private String format(String lvl, String msg) { + return "[" + lvl + "] " + timestamp() + msg; + } + default void logInfo(String msg) { + System.out.println(format("INFO", msg)); + } + default void logWarn(String msg) { + System.out.println(format("WARN", msg)); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Record patterns (destructuring)

+
+
+
+ Antigo + if (obj instanceof Point) { + Point p = (Point) obj; + int x = p.getX(); + int y = p.getY(); + System.out.println(x + y); +} +
+
+ Moderno + if (obj instanceof Point(int x, int y)) { + System.out.println(x + y); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Records for data classes

+
+
+
+ Antigo + 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 +} +
+
+ Moderno + public record Point(int x, int y) {} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Sealed classes for type hierarchies

+
+
+
+ Antigo + // Anyone can extend Shape +public abstract class Shape { } +public class Circle extends Shape { } +public class Rect extends Shape { } +// unknown subclasses possible +
+
+ Moderno + public sealed interface Shape + permits Circle, Rect {} +public record Circle(double r) + implements Shape {} +public record Rect(double w, double h) + implements Shape {} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Static members in inner classes

+
+
+
+ Antigo + class Library { + // Must be static nested class + static class Book { + static int globalBookCount; + + Book() { + globalBookCount++; + } + } +} + +// Usage +var book = new Library.Book(); +
+
+ Moderno + 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(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Static methods in interfaces

+
+
+
+ Antigo + // Separate utility class needed +public class ValidatorUtils { + public static boolean isBlank( + String s) { + return s == null || + s.trim().isEmpty(); + } +} + +// Usage +if (ValidatorUtils.isBlank(input)) { ... } +
+
+ Moderno + public interface Validator { + boolean validate(String s); + + static boolean isBlank(String s) { + return s == null || + s.trim().isEmpty(); + } +} + +// Usage +if (Validator.isBlank(input)) { ... } +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Switch expressions

+
+
+
+ Antigo + String msg; +switch (day) { + case MONDAY: + msg = "Start"; + break; + case FRIDAY: + msg = "End"; + break; + default: + msg = "Mid"; +} +
+
+ Moderno + String msg = switch (day) { + case MONDAY -> "Start"; + case FRIDAY -> "End"; + default -> "Mid"; +}; +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Text blocks for multiline strings

+
+
+
+ Antigo + String json = "{\n" + + " \"name\": \"Duke\",\n" + + " \"age\": 30\n" + + "}"; +
+
+ Moderno + String json = """ + { + "name": "Duke", + "age": 30 + }"""; +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Type inference with var

+
+
+
+ Antigo + Map<String, List<Integer>> map = + new HashMap<String, List<Integer>>(); +for (Map.Entry<String, List<Integer>> e + : map.entrySet()) { + // verbose type noise +} +
+
+ Moderno + var map = new HashMap<String, List<Integer>>(); +for (var entry : map.entrySet()) { + // clean and readable +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Language
+
+

Unnamed variables with _

+
+
+
+ Antigo + try { + parse(input); +} catch (Exception ignored) { + log("parse failed"); +} +map.forEach((key, value) -> { + process(value); // key unused +}); +
+
+ Moderno + try { + parse(input); +} catch (Exception _) { + log("parse failed"); +} +map.forEach((_, value) -> { + process(value); +}); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Collectors.teeing()

+
+
+
+ Antigo + long count = items.stream().count(); +double sum = items.stream() + .mapToDouble(Item::price) + .sum(); +var result = new Stats(count, sum); +
+
+ Moderno + var result = items.stream().collect( + Collectors.teeing( + Collectors.counting(), + Collectors.summingDouble(Item::price), + Stats::new + ) +); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Copying collections immutably

+
+
+
+ Antigo + List<String> copy = + Collections.unmodifiableList( + new ArrayList<>(original) + ); +
+
+ Moderno + List<String> copy = + List.copyOf(original); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Immutable list creation

+
+
+
+ Antigo + List<String> list = + Collections.unmodifiableList( + new ArrayList<>( + Arrays.asList("a", "b", "c") + ) + ); +
+
+ Moderno + List<String> list = + List.of("a", "b", "c"); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Immutable map creation

+
+
+
+ Antigo + Map<String, Integer> map = new HashMap<>(); +map.put("a", 1); +map.put("b", 2); +map.put("c", 3); +map = Collections.unmodifiableMap(map); +
+
+ Moderno + Map<String, Integer> map = + Map.of("a", 1, "b", 2, "c", 3); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Immutable set creation

+
+
+
+ Antigo + Set<String> set = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList("a", "b", "c") + ) + ); +
+
+ Moderno + Set<String> set = + Set.of("a", "b", "c"); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Map.entry() factory

+
+
+
+ Antigo + Map.Entry<String, Integer> e = + new AbstractMap.SimpleEntry<>( + "key", 42 + ); +
+
+ Moderno + var e = Map.entry("key", 42); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Reverse list iteration

+
+
+
+ Antigo + for (ListIterator<String> it = + list.listIterator(list.size()); + it.hasPrevious(); ) { + String element = it.previous(); + System.out.println(element); +} +
+
+ Moderno + for (String element : list.reversed()) { + System.out.println(element); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Sequenced collections

+
+
+
+ Antigo + // Get last element +var last = list.get(list.size() - 1); +// Get first +var first = list.get(0); +// Reverse iteration: manual +
+
+ Moderno + var last = list.getLast(); +var first = list.getFirst(); +var reversed = list.reversed(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Typed stream toArray

+
+
+
+ Antigo + List<String> list = getNames(); +String[] arr = new String[list.size()]; +for (int i = 0; i < list.size(); i++) { + arr[i] = list.get(i); +} +
+
+ Moderno + String[] arr = getNames().stream() + .filter(n -> n.length() > 3) + .toArray(String[]::new); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Collections
+
+

Unmodifiable collectors

+
+
+
+ Antigo + List<String> list = stream.collect( + Collectors.collectingAndThen( + Collectors.toList(), + Collections::unmodifiableList + ) +); +
+
+ Moderno + List<String> list = stream.toList(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String chars as stream

+
+
+
+ Antigo + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (Character.isDigit(c)) { + process(c); + } +} +
+
+ Moderno + str.chars() + .filter(Character::isDigit) + .forEach(c -> process((char) c)); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String.formatted()

+
+
+
+ Antigo + String msg = String.format( + "Hello %s, you are %d", + name, age +); +
+
+ Moderno + String msg = + "Hello %s, you are %d" + .formatted(name, age); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String.indent() and transform()

+
+
+
+ Antigo + String[] lines = text.split("\n"); +StringBuilder sb = new StringBuilder(); +for (String line : lines) { + sb.append(" ").append(line) + .append("\n"); +} +String indented = sb.toString(); +
+
+ Moderno + String indented = text.indent(4); + +String result = text + .transform(String::strip) + .transform(s -> s.replace(" ", "-")); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String.isBlank()

+
+
+
+ Antigo + boolean blank = + str.trim().isEmpty(); +// or: str.trim().length() == 0 +
+
+ Moderno + boolean blank = str.isBlank(); +// handles Unicode whitespace too +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String.lines() for line splitting

+
+
+
+ Antigo + String text = "one\ntwo\nthree"; +String[] lines = text.split("\n"); +for (String line : lines) { + System.out.println(line); +} +
+
+ Moderno + String text = "one\ntwo\nthree"; +text.lines().forEach(System.out::println); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String.repeat()

+
+
+
+ Antigo + StringBuilder sb = new StringBuilder(); +for (int i = 0; i < 3; i++) { + sb.append("abc"); +} +String result = sb.toString(); +
+
+ Moderno + String result = "abc".repeat(3); +// "abcabcabc" +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Strings
+
+

String.strip() vs trim()

+
+
+
+ Antigo + // trim() only removes ASCII whitespace +// (chars <= U+0020) +String clean = str.trim(); +
+
+ Moderno + // strip() removes all Unicode whitespace +String clean = str.strip(); +String left = str.stripLeading(); +String right = str.stripTrailing(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Collectors.flatMapping()

+
+
+
+ Antigo + // Flatten within a grouping collector +// Required complex custom collector +Map<String, Set<String>> tagsByDept = + // no clean way in Java 8 +
+
+ Moderno + var tagsByDept = employees.stream() + .collect(groupingBy( + Emp::dept, + flatMapping( + e -> e.tags().stream(), + toSet() + ) + )); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Optional.ifPresentOrElse()

+
+
+
+ Antigo + Optional<User> user = findUser(id); +if (user.isPresent()) { + greet(user.get()); +} else { + handleMissing(); +} +
+
+ Moderno + findUser(id).ifPresentOrElse( + this::greet, + this::handleMissing +); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Optional.or() fallback

+
+
+
+ Antigo + Optional<Config> cfg = primary(); +if (!cfg.isPresent()) { + cfg = secondary(); +} +if (!cfg.isPresent()) { + cfg = defaults(); +} +
+
+ Moderno + Optional<Config> cfg = primary() + .or(this::secondary) + .or(this::defaults); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Predicate.not() for negation

+
+
+
+ Antigo + List<String> nonEmpty = list.stream() + .filter(s -> !s.isBlank()) + .collect(Collectors.toList()); +
+
+ Moderno + List<String> nonEmpty = list.stream() + .filter(Predicate.not(String::isBlank)) + .toList(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Stream gatherers

+
+
+
+ Antigo + // Sliding window: manual implementation +List<List<T>> windows = new ArrayList<>(); +for (int i = 0; i <= list.size()-3; i++) { + windows.add( + list.subList(i, i + 3)); +} +
+
+ Moderno + var windows = stream + .gather( + Gatherers.windowSliding(3) + ) + .toList(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Stream.iterate() with predicate

+
+
+
+ Antigo + Stream.iterate(1, n -> n * 2) + .limit(10) + .forEach(System.out::println); +// can't stop at a condition +
+
+ Moderno + Stream.iterate( + 1, + n -> n < 1000, + n -> n * 2 +).forEach(System.out::println); +// stops when n >= 1000 +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Stream.mapMulti()

+
+
+
+ Antigo + stream.flatMap(order -> + order.items().stream() + .map(item -> new OrderItem( + order.id(), item) + ) +); +
+
+ Moderno + stream.<OrderItem>mapMulti( + (order, downstream) -> { + for (var item : order.items()) + downstream.accept( + new OrderItem(order.id(), item)); + } +); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Stream.ofNullable()

+
+
+
+ Antigo + Stream<String> s = val != null + ? Stream.of(val) + : Stream.empty(); +
+
+ Moderno + Stream<String> s = + Stream.ofNullable(val); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Stream takeWhile / dropWhile

+
+
+
+ Antigo + List<Integer> result = new ArrayList<>(); +for (int n : sorted) { + if (n >= 100) break; + result.add(n); +} +// no stream equivalent in Java 8 +
+
+ Moderno + var result = sorted.stream() + .takeWhile(n -> n < 100) + .toList(); +// or: .dropWhile(n -> n < 10) +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Stream.toList()

+
+
+
+ Antigo + List<String> result = stream + .filter(s -> s.length() > 3) + .collect(Collectors.toList()); +
+
+ Moderno + List<String> result = stream + .filter(s -> s.length() > 3) + .toList(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Streams
+
+

Virtual thread executor

+
+
+
+ Antigo + ExecutorService exec = + Executors.newFixedThreadPool(10); +try { + futures = tasks.stream() + .map(t -> exec.submit(t)) + .toList(); +} finally { + exec.shutdown(); +} +
+
+ Moderno + try (var exec = Executors + .newVirtualThreadPerTaskExecutor()) { + var futures = tasks.stream() + .map(exec::submit) + .toList(); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

CompletableFuture chaining

+
+
+
+ Antigo + Future<String> future = + executor.submit(this::fetchData); +String data = future.get(); // blocks +String result = transform(data); +
+
+ Moderno + CompletableFuture.supplyAsync( + this::fetchData +) +.thenApply(this::transform) +.thenAccept(System.out::println); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Concurrent HTTP with virtual threads

+
+
+
+ Antigo + ExecutorService pool = + Executors.newFixedThreadPool(10); +List<Future<String>> futures = + urls.stream() + .map(u -> pool.submit( + () -> fetchUrl(u))) + .toList(); +// manual shutdown, blocking get() +
+
+ Moderno + try (var exec = Executors + .newVirtualThreadPerTaskExecutor()) { + var results = urls.stream() + .map(u -> exec.submit( + () -> client.send(req(u), + ofString()).body())) + .toList().stream() + .map(Future::join).toList(); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

ExecutorService auto-close

+
+
+
+ Antigo + ExecutorService exec = + Executors.newCachedThreadPool(); +try { + exec.submit(task); +} finally { + exec.shutdown(); + exec.awaitTermination( + 1, TimeUnit.MINUTES); +} +
+
+ Moderno + try (var exec = + Executors.newCachedThreadPool()) { + exec.submit(task); +} +// auto shutdown + await on close +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Lock-free lazy initialization

+
+
+
+ Antigo + class Config { + private static volatile Config inst; + static Config get() { + if (inst == null) { + synchronized (Config.class) { + if (inst == null) + inst = load(); + } + } + return inst; + } +} +
+
+ Moderno + class Config { + private static final + StableValue<Config> INST = + StableValue.of(Config::load); + + static Config get() { + return INST.get(); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Modern Process API

+
+
+
+ Antigo + Process p = Runtime.getRuntime() + .exec("ls -la"); +int code = p.waitFor(); +// no way to get PID +// no easy process info +
+
+ Moderno + ProcessHandle ph = + ProcessHandle.current(); +long pid = ph.pid(); +ph.info().command() + .ifPresent(System.out::println); +ph.children().forEach( + c -> System.out.println(c.pid())); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Scoped values

+
+
+
+ Antigo + static final ThreadLocal<User> CURRENT = + new ThreadLocal<>(); +void handle(Request req) { + CURRENT.set(authenticate(req)); + try { process(); } + finally { CURRENT.remove(); } +} +
+
+ Moderno + static final ScopedValue<User> CURRENT = + ScopedValue.newInstance(); +void handle(Request req) { + ScopedValue.where(CURRENT, + authenticate(req) + ).run(this::process); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Stable values

+
+
+
+ Antigo + private volatile Logger logger; +Logger getLogger() { + if (logger == null) { + synchronized (this) { + if (logger == null) + logger = createLogger(); + } + } + return logger; +} +
+
+ Moderno + private final StableValue<Logger> logger = + StableValue.of(this::createLogger); + +Logger getLogger() { + return logger.get(); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Structured concurrency

+
+
+
+ Antigo + ExecutorService exec = + Executors.newFixedThreadPool(2); +Future<User> u = exec.submit(this::fetchUser); +Future<Order> o = exec.submit(this::fetchOrder); +try { + return combine(u.get(), o.get()); +} finally { exec.shutdown(); } +
+
+ Moderno + try (var scope = new StructuredTaskScope + .ShutdownOnFailure()) { + var u = scope.fork(this::fetchUser); + var o = scope.fork(this::fetchOrder); + scope.join().throwIfFailed(); + return combine(u.get(), o.get()); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Thread.sleep with Duration

+
+
+
+ Antigo + // What unit is 5000? ms? us? +Thread.sleep(5000); + +// 2.5 seconds: math required +Thread.sleep(2500); +
+
+ Moderno + Thread.sleep( + Duration.ofSeconds(5) +); +Thread.sleep( + Duration.ofMillis(2500) +); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Concurrency
+
+

Virtual threads

+
+
+
+ Antigo + Thread thread = new Thread(() -> { + System.out.println("hello"); +}); +thread.start(); +thread.join(); +
+
+ Moderno + Thread.startVirtualThread(() -> { + System.out.println("hello"); +}).join(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Deserialization filters

+
+
+
+ Antigo + // Dangerous: accepts any class +ObjectInputStream ois = + new ObjectInputStream(input); +Object obj = ois.readObject(); +// deserialization attacks possible! +
+
+ Moderno + ObjectInputFilter filter = + ObjectInputFilter.Config + .createFilter( + "com.myapp.*;!*" + ); +ois.setObjectInputFilter(filter); +Object obj = ois.readObject(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

File memory mapping

+
+
+
+ Antigo + 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 +} +
+
+ Moderno + 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 +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Files.mismatch()

+
+
+
+ Antigo + // 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 +
+
+ Moderno + long pos = Files.mismatch(path1, path2); +// -1 if identical +// otherwise: position of first difference +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Modern HTTP client

+
+
+
+ Antigo + 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... +
+
+ Moderno + 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(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

InputStream.transferTo()

+
+
+
+ Antigo + byte[] buf = new byte[8192]; +int n; +while ((n = input.read(buf)) != -1) { + output.write(buf, 0, n); +} +
+
+ Moderno + input.transferTo(output); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

IO class for console I/O

+
+
+
+ Antigo + 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(); +
+
+ Moderno + String name = IO.readln("Name: "); +IO.println("Hello, " + name); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Path.of() factory

+
+
+
+ Antigo + Path path = Paths.get("src", "main", + "java", "App.java"); +
+
+ Moderno + Path path = Path.of("src", "main", + "java", "App.java"); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Reading files

+
+
+
+ Antigo + 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(); +
+
+ Moderno + String content = + Files.readString(Path.of("data.txt")); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Try-with-resources improvement

+
+
+
+ Antigo + Connection conn = getConnection(); +// Must re-declare in try +try (Connection c = conn) { + use(c); +} +
+
+ Moderno + Connection conn = getConnection(); +// Use existing variable directly +try (conn) { + use(conn); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
I/O
+
+

Writing files

+
+
+
+ Antigo + try (FileWriter fw = + new FileWriter("out.txt"); + BufferedWriter bw = + new BufferedWriter(fw)) { + bw.write(content); +} +
+
+ Moderno + Files.writeString( + Path.of("out.txt"), + content +); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Helpful NullPointerExceptions

+
+
+
+ Antigo + // Old NPE message: +// "NullPointerException" +// at MyApp.main(MyApp.java:42) +// Which variable was null?! +
+
+ Moderno + // Modern NPE message: +// Cannot invoke "String.length()" +// because "user.address().city()" +// is null +// Exact variable identified! +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Multi-catch exception handling

+
+
+
+ Antigo + try { + process(); +} catch (IOException e) { + log(e); +} catch (SQLException e) { + log(e); +} catch (ParseException e) { + log(e); +} +
+
+ Moderno + try { + process(); +} catch (IOException + | SQLException + | ParseException e) { + log(e); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Null case in switch

+
+
+
+ Antigo + // Must check before switch +if (status == null) { + return "unknown"; +} +return switch (status) { + case ACTIVE -> "active"; + case PAUSED -> "paused"; + default -> "other"; +}; +
+
+ Moderno + return switch (status) { + case null -> "unknown"; + case ACTIVE -> "active"; + case PAUSED -> "paused"; + default -> "other"; +}; +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Optional chaining

+
+
+
+ Antigo + String city = null; +if (user != null) { + Address addr = user.getAddress(); + if (addr != null) { + city = addr.getCity(); + } +} +if (city == null) city = "Unknown"; +
+
+ Moderno + String city = Optional.ofNullable(user) + .map(User::address) + .map(Address::city) + .orElse("Unknown"); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Optional.orElseThrow() without supplier

+
+
+
+ Antigo + // Risky: get() throws if empty, no clear intent +String value = optional.get(); + +// Verbose: supplier just for NoSuchElementException +String value = optional + .orElseThrow(NoSuchElementException::new); +
+
+ Moderno + // Clear intent: throws NoSuchElementException if empty +String value = optional.orElseThrow(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Record-based error responses

+
+
+
+ Antigo + // Verbose error class +public class ErrorResponse { + private final int code; + private final String message; + // constructor, getters, equals, + // hashCode, toString... +} +
+
+ Moderno + public record ApiError( + int code, + String message, + Instant timestamp +) { + public ApiError(int code, String msg) { + this(code, msg, Instant.now()); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Errors
+
+

Objects.requireNonNullElse()

+
+
+
+ Antigo + String name = input != null + ? input + : "default"; +// easy to get the order wrong +
+
+ Moderno + String name = Objects + .requireNonNullElse( + input, "default" + ); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Date/Time
+
+

Date formatting

+
+
+
+ Antigo + // Not thread-safe! +SimpleDateFormat sdf = + new SimpleDateFormat("yyyy-MM-dd"); +String formatted = sdf.format(date); +// Must synchronize for concurrent use +
+
+ Moderno + DateTimeFormatter fmt = + DateTimeFormatter.ofPattern( + "uuuu-MM-dd"); +String formatted = + LocalDate.now().format(fmt); +// Thread-safe, immutable +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Date/Time
+
+

Duration and Period

+
+
+
+ Antigo + // How many days between two dates? +long diff = date2.getTime() + - date1.getTime(); +long days = diff + / (1000 * 60 * 60 * 24); +// ignores DST, leap seconds +
+
+ Moderno + long days = ChronoUnit.DAYS + .between(date1, date2); +Period period = Period.between( + date1, date2); +Duration elapsed = Duration.between( + time1, time2); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Date/Time
+
+

HexFormat

+
+
+
+ Antigo + // Pad to 2 digits, uppercase +String hex = String.format( + "%02X", byteValue); +// Parse hex string +int val = Integer.parseInt( + "FF", 16); +
+
+ Moderno + HexFormat hex = HexFormat.of() + .withUpperCase(); +String s = hex.toHexDigits( + byteValue); +byte[] bytes = + hex.parseHex("48656C6C6F"); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Date/Time
+
+

Instant with nanosecond precision

+
+
+
+ Antigo + // Millisecond precision only +long millis = + System.currentTimeMillis(); +// 1708012345678 +
+
+ Moderno + // Microsecond/nanosecond precision +Instant now = Instant.now(); +// 2025-02-15T20:12:25.678901234Z +long nanos = now.getNano(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Date/Time
+
+

java.time API basics

+
+
+
+ Antigo + // Mutable, confusing, zero-indexed months +Calendar cal = Calendar.getInstance(); +cal.set(2025, 0, 15); // January = 0! +Date date = cal.getTime(); +// not thread-safe +
+
+ Moderno + LocalDate date = LocalDate.of( + 2025, Month.JANUARY, 15); +LocalTime time = LocalTime.of(14, 30); +Instant now = Instant.now(); +// immutable, thread-safe +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Date/Time
+
+

Math.clamp()

+
+
+
+ Antigo + // Clamp value between min and max +int clamped = + Math.min(Math.max(value, 0), 100); +// or: min and max order confusion +
+
+ Moderno + int clamped = + Math.clamp(value, 0, 100); +// value constrained to [0, 100] +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Security
+
+

Key Derivation Functions

+
+
+
+ Antigo + SecretKeyFactory factory = + SecretKeyFactory.getInstance( + "PBKDF2WithHmacSHA256"); +KeySpec spec = new PBEKeySpec( + password, salt, 10000, 256); +SecretKey key = + factory.generateSecret(spec); +
+
+ Moderno + KDF kdf = KDF.getInstance("HKDF-SHA256"); +SecretKey key = kdf.deriveKey( + "AES", + KDF.HKDFParameterSpec + .ofExtract() + .addIKM(inputKey) + .addSalt(salt) + .thenExpand(info, 32) + .build() +); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Security
+
+

PEM encoding/decoding

+
+
+
+ Antigo + String pem = "-----BEGIN CERTIFICATE-----\n" + + Base64.getMimeEncoder() + .encodeToString( + cert.getEncoded()) + + "\n-----END CERTIFICATE-----"; +
+
+ Moderno + // Encode to PEM +String pem = PEMEncoder.of() + .encodeToString(cert); +// Decode from PEM +var cert = PEMDecoder.of() + .decode(pemString); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Security
+
+

RandomGenerator interface

+
+
+
+ Antigo + // 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); +
+
+ Moderno + // 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(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Security
+
+

Strong random generation

+
+
+
+ Antigo + // Default algorithm — may not be +// the strongest available +SecureRandom random = + new SecureRandom(); +byte[] bytes = new byte[32]; +random.nextBytes(bytes); +
+
+ Moderno + // Platform's strongest algorithm +SecureRandom random = + SecureRandom.getInstanceStrong(); +byte[] bytes = new byte[32]; +random.nextBytes(bytes); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Security
+
+

TLS 1.3 by default

+
+
+
+ Antigo + SSLContext ctx = + SSLContext.getInstance("TLSv1.2"); +ctx.init(null, trustManagers, null); +SSLSocketFactory factory = + ctx.getSocketFactory(); +// Must specify protocol version +
+
+ Moderno + // TLS 1.3 is the default! +var client = HttpClient.newBuilder() + .sslContext(SSLContext.getDefault()) + .build(); +// Already using TLS 1.3 +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

AOT class preloading

+
+
+
+ Antigo + // Every startup: +// - Load 10,000+ classes +// - Verify bytecode +// - JIT compile hot paths +// Startup: 2-5 seconds +
+
+ Moderno + // Training run: +$ java -XX:AOTCacheOutput=app.aot \ + -cp app.jar com.App +// Production: +$ java -XX:AOTCache=app.aot \ + -cp app.jar com.App +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

Built-in HTTP server

+
+
+
+ Antigo + // 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(); +
+
+ Moderno + // Terminal: serve current directory +$ jwebserver + +// Or use the API (JDK 18+) +var server = SimpleFileServer.createFileServer( + new InetSocketAddress(8080), + Path.of("."), + OutputLevel.VERBOSE); +server.start(); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

Compact object headers

+
+
+
+ Antigo + // Default: 128-bit object header +// = 16 bytes overhead per object +// A boolean field object = 32 bytes! +// Mark word (64) + Klass pointer (64) +
+
+ Moderno + // -XX:+UseCompactObjectHeaders +// 64-bit object header +// = 8 bytes overhead per object +// 50% less header memory +// More objects fit in cache +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

JFR for profiling

+
+
+
+ Antigo + // Install VisualVM / YourKit / JProfiler +// Attach to running process +// Configure sampling +// Export and analyze +// External tool required +
+
+ Moderno + // Start with profiling enabled +$ java -XX:StartFlightRecording= + filename=rec.jfr MyApp + +// Or attach to running app: +$ jcmd <pid> JFR.start +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

JShell for prototyping

+
+
+
+ Antigo + // 1. Create Test.java +// 2. javac Test.java +// 3. java Test +// Just to test one expression! +
+
+ Moderno + $ jshell +jshell> "hello".chars().count() +$1 ==> 5 +jshell> List.of(1,2,3).reversed() +$2 ==> [3, 2, 1] +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

JUnit 6 with JSpecify null safety

+
+
+
+ Antigo + 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")); + } +} +
+
+ Moderno + 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 + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

Multi-file source launcher

+
+
+
+ Antigo + $ javac *.java +$ java Main +// Must compile all files first +// Need a build tool for dependencies +
+
+ Moderno + $ java Main.java +// Automatically finds and compiles +// other source files referenced +// by Main.java +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Tooling
+
+

Single-file execution

+
+
+
+ Antigo + $ javac HelloWorld.java +$ java HelloWorld +// Two steps every time +
+
+ Moderno + $ java HelloWorld.java +// Compiles and runs in one step +// Also works with shebangs: +#!/usr/bin/java --source 25 +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

EJB Timer vs Jakarta Scheduler

+
+
+
+ Antigo + @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(); + } +} +
+
+ Moderno + @ApplicationScoped +public class ReportGenerator { + @Resource + ManagedScheduledExecutorService scheduler; + + @PostConstruct + public void init() { + scheduler.scheduleAtFixedRate( + this::generateReport, + 0, 24, TimeUnit.HOURS); + } + + public void generateReport() { + buildDailyReport(); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

EJB versus CDI

+
+
+
+ Antigo + @Stateless +public class OrderEJB { + @EJB + private InventoryEJB inventory; + + public void placeOrder(Order order) { + // container-managed transaction + inventory.reserve(order.getItem()); + } +} +
+
+ Moderno + @ApplicationScoped +public class OrderService { + @Inject + private InventoryService inventory; + + @Transactional + public void placeOrder(Order order) { + inventory.reserve(order.getItem()); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

JDBC ResultSet Mapping vs JPA Criteria API

+
+
+
+ Antigo + 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<User> users = new ArrayList<>(); + while (rs.next()) { + User u = new User(); + u.setId(rs.getLong("id")); + u.setName(rs.getString("name")); + u.setAge(rs.getInt("age")); + users.add(u); + } +} +
+
+ Moderno + @PersistenceContext +EntityManager em; + +public List<User> findActiveAboveAge( + String status, int minAge) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery<User> cq = + cb.createQuery(User.class); + Root<User> root = cq.from(User.class); + cq.select(root).where( + cb.equal(root.get("status"), status), + cb.greaterThan(root.get("age"), minAge)); + return em.createQuery(cq).getResultList(); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

JDBC versus jOOQ

+
+
+
+ Antigo + 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<User> result = new ArrayList<>(); + while (rs.next()) { + result.add(new User( + rs.getLong("id"), + rs.getString("name"), + rs.getString("email"))); + } + return result; +} +
+
+ Moderno + DSLContext dsl = DSL.using(ds, SQLDialect.POSTGRES); + +return dsl + .select(USERS.ID, USERS.NAME, USERS.EMAIL) + .from(USERS) + .where(USERS.DEPARTMENT.eq(department) + .and(USERS.SALARY.gt(minSalary))) + .fetchInto(User.class); +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

JDBC versus JPA

+
+
+
+ Antigo + 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")); + } +} +
+
+ Moderno + @PersistenceContext +EntityManager em; + +public User findUser(Long id) { + return em.find(User.class, id); +} + +public List<User> findByName(String name) { + return em.createQuery( + "SELECT u FROM User u WHERE u.name = :name", + User.class) + .setParameter("name", name) + .getResultList(); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

JNDI Lookup vs CDI Injection

+
+
+
+ Antigo + 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<Order> findAll() + throws SQLException { + try (Connection con = ds.getConnection()) { + // query orders + } + } +} +
+
+ Moderno + @ApplicationScoped +public class OrderService { + @Inject + @Resource(name = "jdbc/OrderDB") + DataSource ds; + + public List<Order> findAll() + throws SQLException { + try (Connection con = ds.getConnection()) { + // query orders + } + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

JPA versus Jakarta Data

+
+
+
+ Antigo + @PersistenceContext +EntityManager em; + +public User findById(Long id) { + return em.find(User.class, id); +} + +public List<User> findByName(String name) { + return em.createQuery( + "SELECT u FROM User u WHERE u.name = :name", + User.class) + .setParameter("name", name) + .getResultList(); +} + +public void save(User user) { + em.persist(user); +} +
+
+ Moderno + @Repository +public interface Users extends CrudRepository<User, Long> { + List<User> findByName(String name); +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

JSF Managed Bean vs CDI Named Bean

+
+
+
+ Antigo + @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; + } +} +
+
+ Moderno + @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; + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Manual JPA Transaction vs Declarative @Transactional

+
+
+
+ Antigo + @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; + } +} +
+
+ Moderno + @ApplicationScoped +public class AccountService { + @PersistenceContext + EntityManager em; + + @Transactional + public void transferFunds(Long from, Long to, + BigDecimal amount) { + Account src = em.find(Account.class, from); + Account dst = em.find(Account.class, to); + src.debit(amount); + dst.credit(amount); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Message-Driven Bean vs Reactive Messaging

+
+
+
+ Antigo + @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()); + } +} +
+
+ Moderno + @ApplicationScoped +public class OrderProcessor { + @Incoming("orders") + public void process(Order order) { + // automatically deserialized from + // the "orders" channel + fulfillOrder(order); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Servlet versus JAX-RS

+
+
+
+ Antigo + @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 + "\"}"); + } +} +
+
+ Moderno + @Path("/users") +public class UserResource { + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getUser( + @QueryParam("id") String id) { + return Response.ok(new User(id)).build(); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Singleton EJB vs CDI @ApplicationScoped

+
+
+
+ Antigo + @Singleton +@Startup +@ConcurrencyManagement( + ConcurrencyManagementType.CONTAINER) +public class ConfigCache { + private Map<String, String> cache; + + @PostConstruct + public void load() { + cache = loadFromDatabase(); + } + + @Lock(LockType.READ) + public String get(String key) { + return cache.get(key); + } + + @Lock(LockType.WRITE) + public void refresh() { + cache = loadFromDatabase(); + } +} +
+
+ Moderno + @ApplicationScoped +public class ConfigCache { + private volatile Map<String, String> cache; + + @PostConstruct + public void load() { + cache = loadFromDatabase(); + } + + public String get(String key) { + return cache.get(key); + } + + public void refresh() { + cache = loadFromDatabase(); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

SOAP Web Services vs Jakarta REST

+
+
+
+ Antigo + @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; + } +} +
+
+ Moderno + @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); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Spring Framework 7 API Versioning

+
+
+
+ Antigo + // 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); + } +} +
+
+ Moderno + // 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); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Spring Null Safety with JSpecify

+
+
+
+ Antigo + 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<User> findAll() { + return repository.findAll(); + } + + @NonNull + public User save(@NonNull User user) { + return repository.save(user); + } +} +
+
+ Moderno + import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public class UserService { + + public @Nullable User findById(String id) { + return repository.findById(id).orElse(null); + } + + public List<User> findAll() { + return repository.findAll(); + } + + public User save(User user) { + return repository.save(user); + } +} +
+ passe o mouse para ver o moderno → +
+ +
+ +
+
+
Enterprise
+
+

Spring XML Bean Config vs Annotation-Driven

+
+
+
+ Antigo + <!-- applicationContext.xml --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="userRepository" + class="com.example.UserRepository"> + <property name="dataSource" ref="dataSource"/> + </bean> + + <bean id="userService" + class="com.example.UserService"> + <property name="repository" ref="userRepository"/> + </bean> + +</beans> +
+
+ Moderno + @SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} + +@Repository +public class UserRepository { + private final JdbcTemplate jdbc; + + public UserRepository(JdbcTemplate jdbc) { + this.jdbc = jdbc; + } +} + +@Service +public class UserService { + private final UserRepository repository; + + public UserService(UserRepository repository) { + this.repository = repository; + } +} +
+ passe o mouse para ver o moderno → +
+ +
+
+
+ +
+
+
112
+
Padrões Modernos
+
+
+
17
+
Versões do JDK Cobertas
+
+
+
10
+
Categorias
+
+
+
0
+
Linhas de Python Necessárias
+
+
+ + + +
+
+
+ + + ESC +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/site/pt-BR/io/deserialization-filters.html b/site/pt-BR/io/deserialization-filters.html new file mode 100644 index 0000000..8cad518 --- /dev/null +++ b/site/pt-BR/io/deserialization-filters.html @@ -0,0 +1,372 @@ + + + + + + Deserialization filters | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + advanced +
+

Deserialization filters

+

Restrict which classes can be deserialized to prevent attacks.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Dangerous: accepts any class
+ObjectInputStream ois =
+    new ObjectInputStream(input);
+Object obj = ois.readObject();
+// deserialization attacks possible!
+
+
+
+
+ ✓ Java 9+ + +
+
+
ObjectInputFilter filter =
+    ObjectInputFilter.Config
+    .createFilter(
+        "com.myapp.*;!*"
+    );
+ois.setObjectInputFilter(filter);
+Object obj = ois.readObject();
+
+
+
+
+ +
+ +
+
+
🛡️
+

Security

+

Prevent deserialization of unexpected/malicious classes.

+
+
+
📐
+

Fine-grained

+

Control depth, array size, references, and class patterns.

+
+
+
🏗️
+

JVM-wide

+

Set a global filter for all deserialization in the JVM.

+
+
+
+ +
+
+
Abordagem Antiga
+
Accept Everything
+
+
+
Abordagem Moderna
+
ObjectInputFilter
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Deserialization filters
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

ObjectInputFilter lets you allowlist/denylist classes, limit object graph depth, array sizes, and reference counts. This defends against deserialization vulnerabilities without external libraries.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/file-memory-mapping.html b/site/pt-BR/io/file-memory-mapping.html new file mode 100644 index 0000000..66579a4 --- /dev/null +++ b/site/pt-BR/io/file-memory-mapping.html @@ -0,0 +1,393 @@ + + + + + + File memory mapping | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + advanced +
+

File memory mapping

+

Map files larger than 2GB with deterministic cleanup using MemorySegment.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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
+}
+
+
+
+
+ ✓ Java 22+ + +
+
+
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
+
+
+
+
+ +
+ +
+
+
📏
+

No size limit

+

Map files larger than 2GB without workarounds.

+
+
+
🔒
+

Deterministic cleanup

+

Arena ensures memory is freed at scope exit, not GC time.

+
+
+
+

Better performance

+

Aligned with modern memory models and hardware.

+
+
+
+ +
+
+
Abordagem Antiga
+
MappedByteBuffer
+
+
+
Abordagem Moderna
+
MemorySegment with Arena
+
+
+
Desde o JDK
+
22
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
File memory mapping
+ Disponível +

Available since JDK 22 (March 2024)

+
+
+ +
+ +

The Foreign Function & Memory API (JEP 454) introduces MemorySegment for safe and efficient memory access. Unlike MappedByteBuffer, MemorySegment supports files larger than 2GB (Integer.MAX_VALUE), provides deterministic cleanup via Arena, and offers better performance with modern hardware.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/files-mismatch.html b/site/pt-BR/io/files-mismatch.html new file mode 100644 index 0000000..e73a69f --- /dev/null +++ b/site/pt-BR/io/files-mismatch.html @@ -0,0 +1,371 @@ + + + + + + Files.mismatch() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

Files.mismatch()

+

Compare two files efficiently without loading them into memory.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// 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
+
+
+
+
+ ✓ Java 12+ + +
+
+
long pos = Files.mismatch(path1, path2);
+// -1 if identical
+// otherwise: position of first difference
+
+
+
+
+ +
+ +
+
+
+

Memory-efficient

+

Doesn't load entire files into byte arrays.

+
+
+
🎯
+

Pinpoints difference

+

Returns the exact byte position of the first mismatch.

+
+
+
📏
+

One call

+

No manual byte array comparison logic.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Byte Compare
+
+
+
Abordagem Moderna
+
Files.mismatch()
+
+
+
Desde o JDK
+
12
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Files.mismatch()
+ Disponível +

Widely available since JDK 12 (March 2019)

+
+
+ +
+ +

Files.mismatch() returns the position of the first byte that differs, or -1 if the files are identical. It reads lazily and short-circuits on the first difference.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/http-client.html b/site/pt-BR/io/http-client.html new file mode 100644 index 0000000..4cbd537 --- /dev/null +++ b/site/pt-BR/io/http-client.html @@ -0,0 +1,377 @@ + + + + + + Modern HTTP client | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

Modern HTTP client

+

Use the built-in HttpClient for clean, modern HTTP requests.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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...
+
+
+
+
+ ✓ Java 11+ + +
+
+
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();
+
+
+
+
+ +
+ +
+
+
📐
+

Builder API

+

Fluent builder for requests, headers, and timeouts.

+
+
+
🔄
+

HTTP/2 support

+

Built-in HTTP/2 with multiplexing and server push.

+
+
+
+

Async ready

+

sendAsync() returns CompletableFuture.

+
+
+
+ +
+
+
Abordagem Antiga
+
HttpURLConnection
+
+
+
Abordagem Moderna
+
HttpClient
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Modern HTTP client
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

HttpClient supports HTTP/1.1 and HTTP/2, async requests, WebSocket, custom executors, and connection pooling. No more casting URLConnection or manually reading InputStreams.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/inputstream-transferto.html b/site/pt-BR/io/inputstream-transferto.html new file mode 100644 index 0000000..63b408d --- /dev/null +++ b/site/pt-BR/io/inputstream-transferto.html @@ -0,0 +1,373 @@ + + + + + + InputStream.transferTo() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

InputStream.transferTo()

+

Copy an InputStream to an OutputStream in one call.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
byte[] buf = new byte[8192];
+int n;
+while ((n = input.read(buf)) != -1) {
+    output.write(buf, 0, n);
+}
+
+
+
+
+ ✓ Java 9+ + +
+
+
input.transferTo(output);
+
+
+
+
+ +
+ +
+
+
📏
+

One line

+

Replace the entire read/write loop with one method call.

+
+
+
+

Optimized

+

Internal buffer size is tuned for performance.

+
+
+
🛡️
+

No bugs

+

No off-by-one errors in buffer management.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Copy Loop
+
+
+
Abordagem Moderna
+
transferTo()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
InputStream.transferTo()
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

transferTo() reads all bytes from the input stream and writes them to the output stream. No buffer management, no loop. It uses an optimized internal buffer.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/io-class-console-io.html b/site/pt-BR/io/io-class-console-io.html new file mode 100644 index 0000000..63f0e38 --- /dev/null +++ b/site/pt-BR/io/io-class-console-io.html @@ -0,0 +1,375 @@ + + + + + + IO class for console I/O | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

IO class for console I/O

+

The new IO class provides simple, concise methods for console input and output.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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();
+
+
+
+
+ ✓ Java 25+ + +
+
+
String name = IO.readln("Name: ");
+IO.println("Hello, " + name);
+
+
+
+
+ +
+ +
+
+
+

Dramatically simpler

+

Two methods replace seven lines of Scanner setup, prompting, reading, and cleanup.

+
+
+
🔒
+

No resource leaks

+

No Scanner to close — IO methods handle resource management internally.

+
+
+
🎓
+

Beginner-friendly

+

New developers can do console I/O without learning Scanner, System.out, or import statements.

+
+
+
+ +
+
+
Abordagem Antiga
+
System.out / Scanner
+
+
+
Abordagem Moderna
+
IO class
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
IO class for console I/O
+ Preview +

Preview in JDK 25 as part of implicitly declared classes (JEP 495)

+
+
+ +
+ +

Java 25 introduces the IO class (java.io.IO) as part of the implicitly declared classes feature. It provides static methods like println(), print(), readln(), and read() that replace the verbose combination of System.out and Scanner. IO.readln(prompt) handles both prompting and reading in a single call. The class is automatically available in compact source files and can be used in traditional classes via import.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/path-of.html b/site/pt-BR/io/path-of.html new file mode 100644 index 0000000..45e2ca4 --- /dev/null +++ b/site/pt-BR/io/path-of.html @@ -0,0 +1,366 @@ + + + + + + Path.of() factory | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

Path.of() factory

+

Use Path.of() — the modern factory method on the Path interface.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Path path = Paths.get("src", "main",
+    "java", "App.java");
+
+
+
+
+ ✓ Java 11+ + +
+
+
Path path = Path.of("src", "main",
+    "java", "App.java");
+
+
+
+
+ +
+ +
+
+
📐
+

Consistent API

+

Follows the .of() factory pattern like List.of(), Set.of().

+
+
+
📖
+

Discoverable

+

Found on the Path type itself, not a separate Paths class.

+
+
+
🧹
+

One less class

+

No need to import the Paths utility class.

+
+
+
+ +
+
+
Abordagem Antiga
+
Paths.get()
+
+
+
Abordagem Moderna
+
Path.of()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Path.of() factory
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

Path.of() is a factory method added directly to the Path interface, replacing the separate Paths utility class. It's more discoverable and consistent with List.of(), Map.of(), etc.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/reading-files.html b/site/pt-BR/io/reading-files.html new file mode 100644 index 0000000..18f8ef4 --- /dev/null +++ b/site/pt-BR/io/reading-files.html @@ -0,0 +1,375 @@ + + + + + + Reading files | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

Reading files

+

Read an entire file into a String with one line.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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();
+
+
+
+
+ ✓ Java 11+ + +
+
+
String content =
+    Files.readString(Path.of("data.txt"));
+
+
+
+
+ +
+ +
+
+
📏
+

One line

+

Replace 8 lines of BufferedReader boilerplate.

+
+
+
🧹
+

Auto cleanup

+

File handle is closed automatically.

+
+
+
🌐
+

UTF-8 default

+

Correct encoding by default — no charset confusion.

+
+
+
+ +
+
+
Abordagem Antiga
+
BufferedReader
+
+
+
Abordagem Moderna
+
Files.readString()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Reading files
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

Files.readString() reads a file's entire content into a String. It handles encoding (UTF-8 by default) and resource cleanup. For large files, use Files.lines() for lazy streaming.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/try-with-resources-effectively-final.html b/site/pt-BR/io/try-with-resources-effectively-final.html new file mode 100644 index 0000000..5f50ddf --- /dev/null +++ b/site/pt-BR/io/try-with-resources-effectively-final.html @@ -0,0 +1,374 @@ + + + + + + Try-with-resources improvement | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

Try-with-resources improvement

+

Use existing effectively-final variables directly in try-with-resources.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Connection conn = getConnection();
+// Must re-declare in try
+try (Connection c = conn) {
+    use(c);
+}
+
+
+
+
+ ✓ Java 9+ + +
+
+
Connection conn = getConnection();
+// Use existing variable directly
+try (conn) {
+    use(conn);
+}
+
+
+
+
+ +
+ +
+
+
🧹
+

No re-declaration

+

Use the existing variable name directly.

+
+
+
📖
+

Less confusion

+

No separate variable name inside the try block.

+
+
+
📏
+

Concise

+

Fewer lines, same resource safety.

+
+
+
+ +
+
+
Abordagem Antiga
+
Re-declare Variable
+
+
+
Abordagem Moderna
+
Effectively Final
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Try-with-resources improvement
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Java 9 allows effectively-final variables to be used directly in try-with-resources without re-declaration. This is cleaner when the resource was created outside the try block.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/io/writing-files.html b/site/pt-BR/io/writing-files.html new file mode 100644 index 0000000..b84b215 --- /dev/null +++ b/site/pt-BR/io/writing-files.html @@ -0,0 +1,374 @@ + + + + + + Writing files | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ I/O + beginner +
+

Writing files

+

Write a String to a file with one line.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
try (FileWriter fw =
+        new FileWriter("out.txt");
+    BufferedWriter bw =
+        new BufferedWriter(fw)) {
+    bw.write(content);
+}
+
+
+
+
+ ✓ Java 11+ + +
+
+
Files.writeString(
+    Path.of("out.txt"),
+    content
+);
+
+
+
+
+ +
+ +
+
+
📏
+

One line

+

No writer wrapping or try-with-resources needed.

+
+
+
🛡️
+

Safe defaults

+

UTF-8 encoding, proper file handle cleanup.

+
+
+
🔧
+

Options

+

Pass OpenOption flags for append, create, etc.

+
+
+
+ +
+
+
Abordagem Antiga
+
FileWriter + BufferedWriter
+
+
+
Abordagem Moderna
+
Files.writeString()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Writing files
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

Files.writeString() writes content to a file with UTF-8 encoding by default. Options can be passed for appending, creating, etc.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/compact-canonical-constructor.html b/site/pt-BR/language/compact-canonical-constructor.html new file mode 100644 index 0000000..3828cba --- /dev/null +++ b/site/pt-BR/language/compact-canonical-constructor.html @@ -0,0 +1,392 @@ + + + + + + Compact canonical constructor | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Compact canonical constructor

+

Validate and normalize record fields without repeating parameter lists.

+
+ +
+ +
+
+
+ ✕ Java 16 + +
+
+
public record Person(String name,
+                     List<String> pets) {
+    // Full canonical constructor
+    public Person(String name,
+                  List<String> pets) {
+        Objects.requireNonNull(name);
+        this.name = name;
+        this.pets = List.copyOf(pets);
+    }
+}
+
+
+
+
+ ✓ Java 16+ + +
+
+
public record Person(String name,
+                     List<String> pets) {
+    // Compact constructor
+    public Person {
+        Objects.requireNonNull(name);
+        pets = List.copyOf(pets);
+    }
+}
+
+
+
+
+ +
+ +
+
+
✂️
+

Less repetition

+

No need to repeat parameter list or assign each field manually.

+
+
+
🛡️
+

Validation

+

Perfect for null checks, range validation, and defensive copies.

+
+
+
📖
+

Clearer intent

+

Compact syntax emphasizes validation, not boilerplate.

+
+
+
+ +
+
+
Abordagem Antiga
+
Explicit constructor validation
+
+
+
Abordagem Moderna
+
Compact constructor
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Compact canonical constructor
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

Records can declare a compact canonical constructor that omits the parameter list and field assignments. The compiler automatically assigns parameters to fields after your validation logic runs. This is ideal for precondition checks, defensive copies, and normalization.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/compact-source-files.html b/site/pt-BR/language/compact-source-files.html new file mode 100644 index 0000000..6dce915 --- /dev/null +++ b/site/pt-BR/language/compact-source-files.html @@ -0,0 +1,379 @@ + + + + + + Compact source files | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Compact source files

+

Write a complete program without class declaration or public static void main.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
public class HelloWorld {
+    public static void main(String[] args) {
+        System.out.println(
+            "Hello, World!");
+    }
+}
+
+
+
+
+ ✓ Java 25 + +
+
+
void main() {
+    IO.println("Hello, World!");
+}
+
+
+
+
+ +
+ +
+
+
🚀
+

Zero ceremony

+

No class, no public static void main, no String[] args.

+
+
+
🎓
+

Beginner-friendly

+

New programmers can write useful code from line 1.

+
+
+
📝
+

Script-like

+

Perfect for quick prototypes, scripts, and examples.

+
+
+
+ +
+
+
Abordagem Antiga
+
Main Class Ceremony
+
+
+
Abordagem Moderna
+
void main()
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Compact source files
+ Disponível +

Finalized in JDK 25 LTS (JEP 512, Sept 2025).

+
+
+ +
+ +

Compact source files remove the ceremony of class declarations and the main method signature for simple programs. Combined with implicit import of java.io.IO, even println is available directly.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/default-interface-methods.html b/site/pt-BR/language/default-interface-methods.html new file mode 100644 index 0000000..05ea271 --- /dev/null +++ b/site/pt-BR/language/default-interface-methods.html @@ -0,0 +1,397 @@ + + + + + + Default interface methods | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Default interface methods

+

Add method implementations directly in interfaces, enabling multiple inheritance of behavior.

+
+ +
+ +
+
+
+ ✕ Java 7 + +
+
+
// 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 { ... }
+
+
+
+
+ ✓ Java 8+ + +
+
+
public interface Logger {
+    default void log(String msg) {
+        System.out.println(
+            timestamp() + ": " + msg);
+    }
+    String timestamp();
+}
+
+// Multiple interfaces allowed
+public class FileLogger
+    implements Logger, Closeable { ... }
+
+
+
+
+ +
+ +
+
+
🔀
+

Multiple inheritance

+

Classes can implement many interfaces with default methods, unlike single abstract class inheritance.

+
+
+
📦
+

API evolution

+

Add new methods to interfaces without breaking existing implementations.

+
+
+
🧩
+

Composable behavior

+

Mix and match capabilities from multiple interfaces freely.

+
+
+
+ +
+
+
Abordagem Antiga
+
Abstract classes for shared behavior
+
+
+
Abordagem Moderna
+
Default methods on interfaces
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Default interface methods
+ Disponível +

Available since JDK 8 (March 2014).

+
+
+ +
+ +

Before Java 8, sharing behavior across unrelated classes required abstract classes, which limited you to single inheritance. Default methods let interfaces provide method implementations, so classes can inherit behavior from multiple interfaces. This was essential for evolving the Collections API (e.g., List.forEach, Map.getOrDefault) without breaking existing implementations.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/diamond-operator.html b/site/pt-BR/language/diamond-operator.html new file mode 100644 index 0000000..e524a8e --- /dev/null +++ b/site/pt-BR/language/diamond-operator.html @@ -0,0 +1,384 @@ + + + + + + Diamond with anonymous classes | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Diamond with anonymous classes

+

Diamond operator now works with anonymous classes too.

+
+ +
+ +
+
+
+ ✕ Java 7/8 + +
+
+
Map<String, List<String>> map =
+    new HashMap<String, List<String>>();
+// anonymous class: no diamond
+Predicate<String> p =
+    new Predicate<String>() {
+        public boolean test(String s) {..}
+    };
+
+
+
+
+ ✓ Java 9+ + +
+
+
Map<String, List<String>> map =
+    new HashMap<>();
+// Java 9: diamond with anonymous classes
+Predicate<String> p =
+    new Predicate<>() {
+        public boolean test(String s) {..}
+    };
+
+
+
+
+ +
+ +
+
+
📏
+

Consistent rules

+

Diamond works everywhere — constructors and anonymous classes alike.

+
+
+
🧹
+

Less redundancy

+

Type arguments are stated once on the left, never repeated.

+
+
+
🔧
+

DRY principle

+

The compiler already knows the type — why write it twice?

+
+
+
+ +
+
+
Abordagem Antiga
+
Repeat Type Args
+
+
+
Abordagem Moderna
+
Diamond <>
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Diamond with anonymous classes
+ Disponível +

Diamond with anonymous classes since JDK 9 (Sept 2017).

+
+
+ +
+ +

Java 7 introduced <> but it didn't work with anonymous inner classes. Java 9 fixed this, so you never need to repeat type arguments on the right-hand side.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/exhaustive-switch.html b/site/pt-BR/language/exhaustive-switch.html new file mode 100644 index 0000000..4a6c054 --- /dev/null +++ b/site/pt-BR/language/exhaustive-switch.html @@ -0,0 +1,382 @@ + + + + + + Exhaustive switch without default | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Exhaustive switch without default

+

Compiler verifies all sealed subtypes are covered — no default needed.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// 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();
+}
+
+
+
+
+ ✓ Java 21+ + +
+
+
// 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!
+}
+
+
+
+
+ +
+ +
+
+
+

Compile-time safety

+

Add a new subtype and the compiler shows every place to update.

+
+
+
🚫
+

No dead code

+

No unreachable default branch that masks bugs.

+
+
+
📐
+

Algebraic types

+

Sealed + records + exhaustive switch = proper ADTs in Java.

+
+
+
+ +
+
+
Abordagem Antiga
+
Mandatory default
+
+
+
Abordagem Moderna
+
Sealed Exhaustiveness
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Exhaustive switch without default
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

When switching over a sealed type, the compiler knows all possible subtypes and verifies every case is handled. If you add a new subtype, the compiler flags every switch that's now incomplete.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/flexible-constructor-bodies.html b/site/pt-BR/language/flexible-constructor-bodies.html new file mode 100644 index 0000000..6f8d7ab --- /dev/null +++ b/site/pt-BR/language/flexible-constructor-bodies.html @@ -0,0 +1,385 @@ + + + + + + Flexible constructor bodies | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Flexible constructor bodies

+

Validate and compute values before calling super() or this().

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
class Square extends Shape {
+    Square(double side) {
+        super(side, side);
+        // can't validate BEFORE super!
+        if (side <= 0)
+            throw new IAE("bad");
+    }
+}
+
+
+
+
+ ✓ Java 25+ + +
+
+
class Square extends Shape {
+    Square(double side) {
+        if (side <= 0)
+            throw new IAE("bad");
+        super(side, side);
+    }
+}
+
+
+
+
+ +
+ +
+
+
🛡️
+

Fail fast

+

Validate arguments before the superclass constructor runs.

+
+
+
🧮
+

Compute first

+

Derive values and prepare data before calling super().

+
+
+
🧹
+

No workarounds

+

No more static helper methods or factory patterns to work around the restriction.

+
+
+
+ +
+
+
Abordagem Antiga
+
Validate After super()
+
+
+
Abordagem Moderna
+
Code Before super()
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Flexible constructor bodies
+ Disponível +

Finalized in JDK 25 LTS (JEP 513, Sept 2025).

+
+
+ +
+ +

Java 25 lifts the restriction that super() must be the first statement. You can now validate arguments, compute derived values, and set up state before delegating to the parent constructor.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/guarded-patterns.html b/site/pt-BR/language/guarded-patterns.html new file mode 100644 index 0000000..e528d77 --- /dev/null +++ b/site/pt-BR/language/guarded-patterns.html @@ -0,0 +1,380 @@ + + + + + + Guarded patterns with when | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Guarded patterns with when

+

Add conditions to pattern cases using when guards.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
if (shape instanceof Circle c) {
+    if (c.radius() > 10) {
+        return "large circle";
+    } else {
+        return "small circle";
+    }
+} else {
+    return "not a circle";
+}
+
+
+
+
+ ✓ Java 21+ + +
+
+
return switch (shape) {
+    case Circle c
+        when c.radius() > 10
+            -> "large circle";
+    case Circle c
+            -> "small circle";
+    default -> "not a circle";
+};
+
+
+
+
+ +
+ +
+
+
🎯
+

Precise matching

+

Combine type + condition in a single case label.

+
+
+
📐
+

Flat structure

+

No nested if/else inside switch cases.

+
+
+
📖
+

Readable intent

+

The when clause reads like natural language.

+
+
+
+ +
+
+
Abordagem Antiga
+
Nested if
+
+
+
Abordagem Moderna
+
when Clause
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Guarded patterns with when
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Guarded patterns let you refine a type match with an additional boolean condition. This keeps all the branching logic in the switch instead of nesting if statements inside cases.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/markdown-javadoc-comments.html b/site/pt-BR/language/markdown-javadoc-comments.html new file mode 100644 index 0000000..baf2ab0 --- /dev/null +++ b/site/pt-BR/language/markdown-javadoc-comments.html @@ -0,0 +1,386 @@ + + + + + + Markdown in Javadoc comments | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Markdown in Javadoc comments

+

Write Javadoc comments in Markdown instead of HTML for better readability.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
/**
+ * Returns the {@code User} with
+ * the given ID.
+ *
+ * <p>Example:
+ * <pre>{@code
+ * var user = findUser(123);
+ * }</pre>
+ *
+ * @param id the user ID
+ * @return the user
+ */
+public User findUser(int id) { ... }
+
+
+
+
+ ✓ Java 23+ + +
+
+
/// 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) { ... }
+
+
+
+
+ +
+ +
+
+
📖
+

Natural syntax

+

Use backticks for inline code and ``` for blocks instead of HTML tags.

+
+
+
✍️
+

Easier to write

+

No need for {@code}, <pre>, <p> tags — just write Markdown.

+
+
+
👁
+

Better in editors

+

Markdown renders beautifully in modern IDEs and text editors.

+
+
+
+ +
+
+
Abordagem Antiga
+
HTML-based Javadoc
+
+
+
Abordagem Moderna
+
Markdown Javadoc
+
+
+
Desde o JDK
+
23
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Markdown in Javadoc comments
+ Disponível +

Available since JDK 23 (Sept 2024)

+
+
+ +
+ +

Java 23 introduces /// Markdown-style Javadoc comments as an alternative to the traditional /** */ HTML-based format. Markdown syntax is more natural to write and read, with support for code blocks, emphasis, lists, and links. The compiler converts Markdown to HTML for javadoc output.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/module-import-declarations.html b/site/pt-BR/language/module-import-declarations.html new file mode 100644 index 0000000..1ab02d8 --- /dev/null +++ b/site/pt-BR/language/module-import-declarations.html @@ -0,0 +1,380 @@ + + + + + + Module import declarations | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Module import declarations

+

Import all exported packages of a module with a single declaration.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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;
+
+
+
+
+ ✓ Java 25+ + +
+
+
import module java.base;
+
+// All of java.util, java.io, java.nio
+// etc. available in one line
+
+
+
+
+ +
+ +
+
+
🧹
+

One line

+

Replace a wall of imports with a single module import.

+
+
+
📦
+

Module-aware

+

Leverages the module system to import coherent sets of packages.

+
+
+
🚀
+

Quick starts

+

Perfect for scripts and prototypes where import lists are tedious.

+
+
+
+ +
+
+
Abordagem Antiga
+
Many Imports
+
+
+
Abordagem Moderna
+
import module
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Module import declarations
+ Disponível +

Finalized in JDK 25 LTS (JEP 511, Sept 2025).

+
+
+ +
+ +

Module import declarations let you import everything a module exports with one line. This is especially useful for java.base which covers collections, I/O, streams, and more.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/pattern-matching-instanceof.html b/site/pt-BR/language/pattern-matching-instanceof.html new file mode 100644 index 0000000..87f78bf --- /dev/null +++ b/site/pt-BR/language/pattern-matching-instanceof.html @@ -0,0 +1,370 @@ + + + + + + Pattern matching for instanceof | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Pattern matching for instanceof

+

Combine type check and cast in one step with pattern matching.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
if (obj instanceof String) {
+    String s = (String) obj;
+    System.out.println(s.length());
+}
+
+
+
+
+ ✓ Java 16+ + +
+
+
if (obj instanceof String s) {
+    System.out.println(s.length());
+}
+
+
+
+
+ +
+ +
+
+
🔄
+

No redundant cast

+

Type check and variable binding happen in a single expression.

+
+
+
📏
+

Fewer lines

+

One line instead of two — the cast line disappears entirely.

+
+
+
🛡️
+

Scope safety

+

The pattern variable is only in scope where the type is guaranteed.

+
+
+
+ +
+
+
Abordagem Antiga
+
instanceof + Cast
+
+
+
Abordagem Moderna
+
Pattern Variable
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Pattern matching for instanceof
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

Pattern matching for instanceof eliminates the redundant cast after a type check. The variable is automatically scoped to where the pattern matches, making code safer and shorter.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/pattern-matching-switch.html b/site/pt-BR/language/pattern-matching-switch.html new file mode 100644 index 0000000..0cb8207 --- /dev/null +++ b/site/pt-BR/language/pattern-matching-switch.html @@ -0,0 +1,383 @@ + + + + + + Pattern matching in switch | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Pattern matching in switch

+

Replace if-else instanceof chains with clean switch type patterns.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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";
+}
+
+
+
+
+ ✓ Java 21+ + +
+
+
String format(Object obj) {
+    return switch (obj) {
+        case Integer i -> "int: " + i;
+        case Double d  -> "double: " + d;
+        case String s  -> "str: " + s;
+        default        -> "unknown";
+    };
+}
+
+
+
+
+ +
+ +
+
+
📐
+

Structured dispatch

+

Switch makes the branching structure explicit and scannable.

+
+
+
🎯
+

Expression form

+

Returns a value directly — no mutable variable needed.

+
+
+
+

Exhaustiveness

+

The compiler ensures all types are handled.

+
+
+
+ +
+
+
Abordagem Antiga
+
if-else Chain
+
+
+
Abordagem Moderna
+
Type Patterns
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Pattern matching in switch
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Pattern matching in switch lets you match on types directly, combining the type test, cast, and binding in one concise case label. The compiler checks completeness.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/primitive-types-in-patterns.html b/site/pt-BR/language/primitive-types-in-patterns.html new file mode 100644 index 0000000..ed0ca66 --- /dev/null +++ b/site/pt-BR/language/primitive-types-in-patterns.html @@ -0,0 +1,387 @@ + + + + + + Primitive types in patterns | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + advanced +
+

Primitive types in patterns

+

Pattern matching now works with primitive types, not just objects.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String classify(int code) {
+    if (code >= 200 && code < 300)
+        return "success";
+    else if (code >= 400 && code < 500)
+        return "client error";
+    else
+        return "other";
+}
+
+
+
+
+ ✓ Java 25 (Preview) + +
+
+
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";
+    };
+}
+
+
+
+
+ +
+ +
+
+
📦
+

No boxing

+

Match primitives directly — no Integer wrapper needed.

+
+
+
🎯
+

Pattern consistency

+

Same pattern syntax for objects and primitives.

+
+
+
+

Better performance

+

Avoid autoboxing overhead in pattern matching.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Range Checks
+
+
+
Abordagem Moderna
+
Primitive Patterns
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Primitive types in patterns
+ Preview +

Preview in JDK 25 (third preview, JEP 507). Requires --enable-preview.

+
+
+ +
+ +

Java 25 extends pattern matching to primitive types. You can use int, long, double etc. in switch patterns with when guards, eliminating the need for boxing or manual range checks.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/private-interface-methods.html b/site/pt-BR/language/private-interface-methods.html new file mode 100644 index 0000000..475a282 --- /dev/null +++ b/site/pt-BR/language/private-interface-methods.html @@ -0,0 +1,390 @@ + + + + + + Private interface methods | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Private interface methods

+

Extract shared logic in interfaces using private methods.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
interface Logger {
+    default void logInfo(String msg) {
+        System.out.println(
+            "[INFO] " + timestamp() + msg);
+    }
+    default void logWarn(String msg) {
+        System.out.println(
+            "[WARN] " + timestamp() + msg);
+    }
+}
+
+
+
+
+ ✓ Java 9+ + +
+
+
interface Logger {
+    private String format(String lvl, String msg) {
+        return "[" + lvl + "] " + timestamp() + msg;
+    }
+    default void logInfo(String msg) {
+        System.out.println(format("INFO", msg));
+    }
+    default void logWarn(String msg) {
+        System.out.println(format("WARN", msg));
+    }
+}
+
+
+
+
+ +
+ +
+
+
🧩
+

Code reuse

+

Share logic between default methods without duplication.

+
+
+
🔐
+

Encapsulation

+

Implementation details stay hidden from implementing classes.

+
+
+
🧹
+

DRY interfaces

+

No more copy-paste between default methods.

+
+
+
+ +
+
+
Abordagem Antiga
+
Duplicated Logic
+
+
+
Abordagem Moderna
+
Private Methods
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Private interface methods
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Java 9 allows private methods in interfaces, enabling you to share code between default methods without exposing implementation details to implementing classes.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/record-patterns.html b/site/pt-BR/language/record-patterns.html new file mode 100644 index 0000000..96f8222 --- /dev/null +++ b/site/pt-BR/language/record-patterns.html @@ -0,0 +1,382 @@ + + + + + + Record patterns (destructuring) | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Record patterns (destructuring)

+

Destructure records directly in patterns — extract fields in one step.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
if (obj instanceof Point) {
+    Point p = (Point) obj;
+    int x = p.getX();
+    int y = p.getY();
+    System.out.println(x + y);
+}
+
+
+
+
+ ✓ Java 21+ + +
+
+
if (obj instanceof Point(int x, int y)) {
+    System.out.println(x + y);
+}
+
+
+
+
+ +
+ +
+
+
🎯
+

Direct extraction

+

Access record components without calling accessors manually.

+
+
+
🪆
+

Nestable

+

Patterns can nest — match inner records in a single expression.

+
+
+
📏
+

Compact code

+

Five lines become two — less ceremony, same clarity.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Access
+
+
+
Abordagem Moderna
+
Destructuring
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Record patterns (destructuring)
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

Record patterns let you decompose a record's components directly in instanceof and switch. Nested patterns are supported too, enabling deep matching without intermediate variables.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/records-for-data-classes.html b/site/pt-BR/language/records-for-data-classes.html new file mode 100644 index 0000000..4048d2f --- /dev/null +++ b/site/pt-BR/language/records-for-data-classes.html @@ -0,0 +1,397 @@ + + + + + + Records para classes de dados | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Records para classes de dados

+

Uma linha substitui mais de 30 linhas de boilerplate para portadores de dados imutáveis.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
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
+}
+
+
+
+
+ ✓ Java 16+ + +
+
+
public record Point(int x, int y) {}
+
+
+
+
+ +
+ +
+
+
+

Definição em uma linha

+

Uma única linha substitui construtor, getters, equals, hashCode, toString.

+
+
+
🔒
+

Imutável por padrão

+

Todos os campos são final — sem armadilhas de setters.

+
+
+
🧩
+

Compatível com patterns

+

Records funcionam com padrões de desestruturação em switch e instanceof.

+
+
+
+ +
+
+
Abordagem Antiga
+
POJO verboso
+
+
+
Abordagem Moderna
+
record
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Records para classes de dados
+ Disponível +

Amplamente disponível desde o JDK 16 (março de 2021)

+
+
+ +
+ +

Records geram automaticamente o construtor, acessores (x(), y()), equals(), hashCode() e toString(). São imutáveis por design e ideais para DTOs, objetos de valor e pattern matching.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/sealed-classes.html b/site/pt-BR/language/sealed-classes.html new file mode 100644 index 0000000..8f6a1d8 --- /dev/null +++ b/site/pt-BR/language/sealed-classes.html @@ -0,0 +1,389 @@ + + + + + + Sealed classes for type hierarchies | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Sealed classes for type hierarchies

+

Restrict which classes can extend a type — enabling exhaustive switches.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Anyone can extend Shape
+public abstract class Shape { }
+public class Circle extends Shape { }
+public class Rect extends Shape { }
+// unknown subclasses possible
+
+
+
+
+ ✓ Java 17+ + +
+
+
public sealed interface Shape
+    permits Circle, Rect {}
+public record Circle(double r)
+    implements Shape {}
+public record Rect(double w, double h)
+    implements Shape {}
+
+
+
+
+ +
+ +
+
+
🔐
+

Controlled hierarchy

+

Only permitted subtypes can extend — no surprise subclasses.

+
+
+
+

Exhaustive matching

+

The compiler verifies switch covers all cases, no default needed.

+
+
+
📐
+

Algebraic data types

+

Model sum types naturally — sealed + records = ADTs in Java.

+
+
+
+ +
+
+
Abordagem Antiga
+
Open Hierarchy
+
+
+
Abordagem Moderna
+
sealed permits
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Sealed classes for type hierarchies
+ Disponível +

Widely available since JDK 17 LTS (Sept 2021)

+
+
+ +
+ +

Sealed classes define a closed set of subtypes. The compiler knows all possible cases, enabling exhaustive pattern matching without a default branch. Combined with records, they model algebraic data types.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/static-members-in-inner-classes.html b/site/pt-BR/language/static-members-in-inner-classes.html new file mode 100644 index 0000000..bc1e34b --- /dev/null +++ b/site/pt-BR/language/static-members-in-inner-classes.html @@ -0,0 +1,402 @@ + + + + + + Static members in inner classes | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + intermediate +
+

Static members in inner classes

+

Define static members in inner classes without requiring static nested classes.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
class Library {
+    // Must be static nested class
+    static class Book {
+        static int globalBookCount;
+
+        Book() {
+            globalBookCount++;
+        }
+    }
+}
+
+// Usage
+var book = new Library.Book();
+
+
+
+
+ ✓ Java 16+ + +
+
+
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();
+
+
+
+
+ +
+ +
+
+
🔓
+

More flexibility

+

Inner classes can now have static members when needed.

+
+
+
🧩
+

Shared state

+

Track shared state across instances of an inner class.

+
+
+
📐
+

Design freedom

+

No need to promote to static nested class just for one static field.

+
+
+
+ +
+
+
Abordagem Antiga
+
Must use static nested class
+
+
+
Abordagem Moderna
+
Static members in inner classes
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Static members in inner classes
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

Before Java 16, only static nested classes could contain static members. Inner (non-static) classes couldn't have statics because they required an enclosing instance. Java 16 relaxes this restriction, allowing static fields, methods, and even nested types in inner classes.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/static-methods-in-interfaces.html b/site/pt-BR/language/static-methods-in-interfaces.html new file mode 100644 index 0000000..9f136aa --- /dev/null +++ b/site/pt-BR/language/static-methods-in-interfaces.html @@ -0,0 +1,409 @@ + + + + + + Static methods in interfaces | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Static methods in interfaces

+

Add static utility methods directly to interfaces instead of separate utility classes.

+
+ +
+ +
+
+
+ ✕ Java 7 + +
+
+
// Separate utility class needed
+public class ValidatorUtils {
+    public static boolean isBlank(
+        String s) {
+        return s == null ||
+               s.trim().isEmpty();
+    }
+}
+
+// Usage
+if (ValidatorUtils.isBlank(input)) { ... }
+
+
+
+
+ ✓ Java 8+ + +
+
+
public interface Validator {
+    boolean validate(String s);
+
+    static boolean isBlank(String s) {
+        return s == null ||
+               s.trim().isEmpty();
+    }
+}
+
+// Usage
+if (Validator.isBlank(input)) { ... }
+
+
+
+
+ +
+ +
+
+
📦
+

Better organization

+

Keep related utilities with the interface, not in a separate class.

+
+
+
🔍
+

Discoverability

+

Factory and helper methods are found where you'd expect them.

+
+
+
🧩
+

API cohesion

+

No need for separate *Utils or *Helper classes.

+
+
+
+ +
+
+
Abordagem Antiga
+
Utility classes
+
+
+
Abordagem Moderna
+
Interface static methods
+
+
+
Desde o JDK
+
8
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Static methods in interfaces
+ Disponível +

Available since JDK 8 (March 2014)

+
+
+ +
+ +

Before Java 8, utility methods related to an interface had to live in a separate class (e.g., Collections for Collection). Static methods in interfaces let you keep related utilities together. Common in modern APIs like Comparator.comparing(), Stream.of(), and List.of().

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/switch-expressions.html b/site/pt-BR/language/switch-expressions.html new file mode 100644 index 0000000..2dce9da --- /dev/null +++ b/site/pt-BR/language/switch-expressions.html @@ -0,0 +1,380 @@ + + + + + + Switch expressions | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Switch expressions

+

Switch as an expression that returns a value — no break, no fall-through.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String msg;
+switch (day) {
+    case MONDAY:
+        msg = "Start";
+        break;
+    case FRIDAY:
+        msg = "End";
+        break;
+    default:
+        msg = "Mid";
+}
+
+
+
+
+ ✓ Java 14+ + +
+
+
String msg = switch (day) {
+    case MONDAY  -> "Start";
+    case FRIDAY  -> "End";
+    default      -> "Mid";
+};
+
+
+
+
+ +
+ +
+
+
🎯
+

Returns a value

+

Assign the switch result directly — no temporary variable needed.

+
+
+
🛡️
+

No fall-through

+

Arrow syntax eliminates accidental fall-through bugs from missing break.

+
+
+
+

Exhaustiveness check

+

The compiler ensures all cases are covered.

+
+
+
+ +
+
+
Abordagem Antiga
+
Switch Statement
+
+
+
Abordagem Moderna
+
Switch Expression
+
+
+
Desde o JDK
+
14
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Switch expressions
+ Disponível +

Widely available since JDK 14 (March 2020)

+
+
+ +
+ +

Switch expressions return a value directly, use arrow syntax to prevent fall-through bugs, and the compiler verifies exhaustiveness. This replaces the error-prone statement form.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/text-blocks-for-multiline-strings.html b/site/pt-BR/language/text-blocks-for-multiline-strings.html new file mode 100644 index 0000000..6a89185 --- /dev/null +++ b/site/pt-BR/language/text-blocks-for-multiline-strings.html @@ -0,0 +1,386 @@ + + + + + + Text blocks for multiline strings | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Text blocks for multiline strings

+

Write multiline strings naturally with triple-quote text blocks.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String json = "{\n" +
+    "  \"name\": \"Duke\",\n" +
+    "  \"age\": 30\n" +
+    "}";
+
+
+
+
+ ✓ Java 15+ + +
+
+
String json = """
+    {
+      "name": "Duke",
+      "age": 30
+    }""";
+
+
+
+
+ +
+ +
+
+
📖
+

Readable as-is

+

JSON, SQL, and HTML look like real JSON, SQL, and HTML in your source.

+
+
+
🚫
+

No escape hell

+

Embedded quotes don't need backslash escaping.

+
+
+
📐
+

Smart indentation

+

Leading whitespace is trimmed automatically based on the closing delimiter position.

+
+
+
+ +
+
+
Abordagem Antiga
+
String Concatenation
+
+
+
Abordagem Moderna
+
Text Blocks
+
+
+
Desde o JDK
+
15
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Text blocks for multiline strings
+ Disponível +

Widely available since JDK 15 (Sept 2020)

+
+
+ +
+ +

Text blocks let you write multiline strings exactly as they appear. No more escaping quotes or adding \n. The compiler strips incidental indentation automatically.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/type-inference-with-var.html b/site/pt-BR/language/type-inference-with-var.html new file mode 100644 index 0000000..d93ea6e --- /dev/null +++ b/site/pt-BR/language/type-inference-with-var.html @@ -0,0 +1,382 @@ + + + + + + Inferência de tipo com var | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Inferência de tipo com var

+

Use var para inferência de tipo em variáveis locais — menos ruído, mesma segurança.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Map<String, List<Integer>> map =
+    new HashMap<String, List<Integer>>();
+for (Map.Entry<String, List<Integer>> e
+    : map.entrySet()) {
+    // verbose type noise
+}
+
+
+
+
+ ✓ Java 10+ + +
+
+
var map = new HashMap<String, List<Integer>>();
+for (var entry : map.entrySet()) {
+    // clean and readable
+}
+
+
+
+
+ +
+ +
+
+
+

Menos boilerplate

+

Não é necessário repetir tipos genéricos complexos em ambos os lados da atribuição.

+
+
+
👁
+

Melhor legibilidade

+

Foco nos nomes de variáveis e valores, não nas declarações de tipo.

+
+
+
🔒
+

Ainda seguro em tipos

+

O compilador infere e impõe o tipo exato em tempo de compilação.

+
+
+
+ +
+
+
Abordagem Antiga
+
Tipos explícitos
+
+
+
Abordagem Moderna
+
Palavra-chave var
+
+
+
Desde o JDK
+
10
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Inferência de tipo com var
+ Disponível +

Amplamente disponível desde o JDK 10 (março de 2018)

+
+
+ +
+ +

Desde o Java 10, o compilador infere os tipos de variáveis locais a partir do lado direito da atribuição. Isso reduz o ruído visual sem sacrificar a segurança de tipos. Use var quando o tipo for óbvio pelo contexto.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/language/unnamed-variables.html b/site/pt-BR/language/unnamed-variables.html new file mode 100644 index 0000000..76eeb42 --- /dev/null +++ b/site/pt-BR/language/unnamed-variables.html @@ -0,0 +1,386 @@ + + + + + + Unnamed variables with _ | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Language + beginner +
+

Unnamed variables with _

+

Use _ to signal intent when a variable is intentionally unused.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
try {
+    parse(input);
+} catch (Exception ignored) {
+    log("parse failed");
+}
+map.forEach((key, value) -> {
+    process(value); // key unused
+});
+
+
+
+
+ ✓ Java 22+ + +
+
+
try {
+    parse(input);
+} catch (Exception _) {
+    log("parse failed");
+}
+map.forEach((_, value) -> {
+    process(value);
+});
+
+
+
+
+ +
+ +
+
+
📢
+

Clear intent

+

_ explicitly says 'this value is not needed here'.

+
+
+
🔇
+

No warnings

+

IDEs and linters won't flag intentionally unused variables.

+
+
+
🧹
+

Cleaner lambdas

+

Multi-param lambdas are cleaner when you only need some params.

+
+
+
+ +
+
+
Abordagem Antiga
+
Unused Variable
+
+
+
Abordagem Moderna
+
_ Placeholder
+
+
+
Desde o JDK
+
22
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Unnamed variables with _
+ Disponível +

Finalized in JDK 22 (JEP 456, March 2024).

+
+
+ +
+ +

Unnamed variables communicate to readers and tools that a value is deliberately ignored. No more 'ignored' or 'unused' naming conventions, no more IDE warnings.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/security/key-derivation-functions.html b/site/pt-BR/security/key-derivation-functions.html new file mode 100644 index 0000000..5f24702 --- /dev/null +++ b/site/pt-BR/security/key-derivation-functions.html @@ -0,0 +1,385 @@ + + + + + + Key Derivation Functions | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Security + advanced +
+

Key Derivation Functions

+

Derive cryptographic keys using the standard KDF API.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
SecretKeyFactory factory =
+    SecretKeyFactory.getInstance(
+        "PBKDF2WithHmacSHA256");
+KeySpec spec = new PBEKeySpec(
+    password, salt, 10000, 256);
+SecretKey key =
+    factory.generateSecret(spec);
+
+
+
+
+ ✓ Java 25 + +
+
+
KDF kdf = KDF.getInstance("HKDF-SHA256");
+SecretKey key = kdf.deriveKey(
+    "AES",
+    KDF.HKDFParameterSpec
+        .ofExtract()
+        .addIKM(inputKey)
+        .addSalt(salt)
+        .thenExpand(info, 32)
+        .build()
+);
+
+
+
+
+ +
+ +
+
+
📐
+

Clean API

+

Builder pattern instead of awkward KeySpec constructors.

+
+
+
🔧
+

HKDF support

+

Modern HKDF algorithm alongside PBKDF2.

+
+
+
🛡️
+

Standard

+

Unified API for all key derivation algorithms.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual PBKDF2
+
+
+
Abordagem Moderna
+
KDF API
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Key Derivation Functions
+ Disponível +

Finalized in JDK 25 LTS (JEP 510, Sept 2025).

+
+
+ +
+ +

The KDF API provides a standard interface for key derivation functions including HKDF. It replaces the awkward SecretKeyFactory + PBEKeySpec pattern with a clean builder API.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/security/pem-encoding.html b/site/pt-BR/security/pem-encoding.html new file mode 100644 index 0000000..3f28519 --- /dev/null +++ b/site/pt-BR/security/pem-encoding.html @@ -0,0 +1,385 @@ + + + + + + PEM encoding/decoding | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Security + advanced +
+

PEM encoding/decoding

+

Encode and decode PEM-formatted cryptographic objects natively.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String pem = "-----BEGIN CERTIFICATE-----\n"
+    + Base64.getMimeEncoder()
+        .encodeToString(
+            cert.getEncoded())
+    + "\n-----END CERTIFICATE-----";
+
+
+
+
+ ✓ Java 25 (Preview) + +
+
+
// Encode to PEM
+String pem = PEMEncoder.of()
+    .encodeToString(cert);
+// Decode from PEM
+var cert = PEMDecoder.of()
+    .decode(pemString);
+
+
+
+
+ +
+ +
+
+
🧹
+

No manual Base64

+

PEM headers, line wrapping, and Base64 handled automatically.

+
+
+
🔄
+

Bidirectional

+

Encode to PEM and decode from PEM with one API.

+
+
+
🛡️
+

Standard format

+

Produces RFC 7468-compliant PEM output.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Base64 + Headers
+
+
+
Abordagem Moderna
+
PEM API
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
PEM encoding/decoding
+ Preview +

Preview in JDK 25 (JEP 470). Requires --enable-preview.

+
+
+ +
+ +

The PEM API provides standard encoding/decoding for certificates, keys, and other cryptographic objects in PEM format. No more manual Base64 wrapping with BEGIN/END headers.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/security/random-generator.html b/site/pt-BR/security/random-generator.html new file mode 100644 index 0000000..e6b7c5d --- /dev/null +++ b/site/pt-BR/security/random-generator.html @@ -0,0 +1,386 @@ + + + + + + RandomGenerator interface | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Security + intermediate +
+

RandomGenerator interface

+

Use the RandomGenerator interface to choose random number algorithms by name without coupling to a specific class.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// 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);
+
+
+
+
+ ✓ Java 17+ + +
+
+
// 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();
+
+
+
+
+ +
+ +
+
+
🔧
+

Algorithm-agnostic

+

Choose the best RNG algorithm by name without changing code structure.

+
+
+
+

Better algorithms

+

Access to modern LXM generators with superior statistical properties.

+
+
+
🔗
+

Unified API

+

One interface covers Random, ThreadLocalRandom, SplittableRandom, and more.

+
+
+
+ +
+
+
Abordagem Antiga
+
new Random() / ThreadLocalRandom
+
+
+
Abordagem Moderna
+
RandomGenerator factory
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
RandomGenerator interface
+ Disponível +

Available since JDK 17 (September 2021, JEP 356).

+
+
+ +
+ +

JDK 17 introduced RandomGenerator as a common interface for all RNG implementations. Instead of hard-coding new Random() or ThreadLocalRandom, you can select algorithms by name via a factory, making it easy to swap between algorithms optimized for different use cases (speed, statistical quality, splittability).

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/security/strong-random.html b/site/pt-BR/security/strong-random.html new file mode 100644 index 0000000..1d242ed --- /dev/null +++ b/site/pt-BR/security/strong-random.html @@ -0,0 +1,385 @@ + + + + + + Strong random generation | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Security + beginner +
+

Strong random generation

+

Get the platform's strongest SecureRandom implementation.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Default algorithm — may not be
+// the strongest available
+SecureRandom random =
+    new SecureRandom();
+byte[] bytes = new byte[32];
+random.nextBytes(bytes);
+
+
+
+
+ ✓ Java 9+ + +
+
+
// Platform's strongest algorithm
+SecureRandom random =
+    SecureRandom.getInstanceStrong();
+byte[] bytes = new byte[32];
+random.nextBytes(bytes);
+
+
+
+
+ +
+ +
+
+
🛡️
+

Strongest available

+

Automatically selects the best algorithm for the platform.

+
+
+
📖
+

Explicit intent

+

Clearly communicates that strong randomness is required.

+
+
+
🔧
+

Configurable

+

Administrators can change the strong algorithm via security properties.

+
+
+
+ +
+
+
Abordagem Antiga
+
new SecureRandom()
+
+
+
Abordagem Moderna
+
getInstanceStrong()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Strong random generation
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

getInstanceStrong() returns the SecureRandom implementation configured as the strongest on the platform. This is controlled by the securerandom.strongAlgorithms security property.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/security/tls-default.html b/site/pt-BR/security/tls-default.html new file mode 100644 index 0000000..c429e55 --- /dev/null +++ b/site/pt-BR/security/tls-default.html @@ -0,0 +1,386 @@ + + + + + + TLS 1.3 by default | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Security + intermediate +
+

TLS 1.3 by default

+

TLS 1.3 is enabled by default — no explicit protocol configuration needed.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
SSLContext ctx =
+    SSLContext.getInstance("TLSv1.2");
+ctx.init(null, trustManagers, null);
+SSLSocketFactory factory =
+    ctx.getSocketFactory();
+// Must specify protocol version
+
+
+
+
+ ✓ Java 11+ + +
+
+
// TLS 1.3 is the default!
+var client = HttpClient.newBuilder()
+    .sslContext(SSLContext.getDefault())
+    .build();
+// Already using TLS 1.3
+
+
+
+
+ +
+ +
+
+
🛡️
+

More secure

+

TLS 1.3 removes obsolete cipher suites and handshake patterns.

+
+
+
+

Faster handshake

+

TLS 1.3 completes in one round trip vs two.

+
+
+
🆓
+

Zero config

+

Secure by default — no explicit protocol selection needed.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual TLS Config
+
+
+
Abordagem Moderna
+
TLS 1.3 Default
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
TLS 1.3 by default
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

Java 11 added TLS 1.3 support and made it the preferred protocol. The HttpClient uses it automatically. No more manually specifying protocol versions for secure connections.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/collectors-flatmapping.html b/site/pt-BR/streams/collectors-flatmapping.html new file mode 100644 index 0000000..3e53673 --- /dev/null +++ b/site/pt-BR/streams/collectors-flatmapping.html @@ -0,0 +1,372 @@ + + + + + + Collectors.flatMapping() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + intermediate +
+

Collectors.flatMapping()

+

Use flatMapping() to flatten inside a grouping collector.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Flatten within a grouping collector
+// Required complex custom collector
+Map<String, Set<String>> tagsByDept =
+    // no clean way in Java 8
+
+
+
+
+ ✓ Java 9+ + +
+
+
var tagsByDept = employees.stream()
+    .collect(groupingBy(
+        Emp::dept,
+        flatMapping(
+            e -> e.tags().stream(),
+            toSet()
+        )
+    ));
+
+
+
+
+ +
+ +
+
+
🧩
+

Composable

+

Works as a downstream collector inside groupingBy.

+
+
+
📐
+

One pass

+

Flatten and group in a single stream traversal.

+
+
+
🔗
+

Nestable

+

Combine with other downstream collectors.

+
+
+
+ +
+
+
Abordagem Antiga
+
Nested flatMap
+
+
+
Abordagem Moderna
+
flatMapping()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Collectors.flatMapping()
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Collectors.flatMapping() applies a one-to-many mapping as a downstream collector. It's the collector equivalent of Stream.flatMap() — useful inside groupingBy or partitioningBy.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/optional-ifpresentorelse.html b/site/pt-BR/streams/optional-ifpresentorelse.html new file mode 100644 index 0000000..c22205b --- /dev/null +++ b/site/pt-BR/streams/optional-ifpresentorelse.html @@ -0,0 +1,374 @@ + + + + + + Optional.ifPresentOrElse() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + beginner +
+

Optional.ifPresentOrElse()

+

Handle both present and empty cases of Optional in one call.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Optional<User> user = findUser(id);
+if (user.isPresent()) {
+    greet(user.get());
+} else {
+    handleMissing();
+}
+
+
+
+
+ ✓ Java 9+ + +
+
+
findUser(id).ifPresentOrElse(
+    this::greet,
+    this::handleMissing
+);
+
+
+
+
+ +
+ +
+
+
📏
+

Single expression

+

Both cases handled in one method call.

+
+
+
🚫
+

No get()

+

Eliminates the dangerous isPresent() + get() pattern.

+
+
+
🔗
+

Fluent

+

Chains naturally after findUser() or any Optional-returning method.

+
+
+
+ +
+
+
Abordagem Antiga
+
if/else on Optional
+
+
+
Abordagem Moderna
+
ifPresentOrElse()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Optional.ifPresentOrElse()
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

ifPresentOrElse() takes a Consumer for the present case and a Runnable for the empty case. It avoids the isPresent/get anti-pattern.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/optional-or.html b/site/pt-BR/streams/optional-or.html new file mode 100644 index 0000000..633922a --- /dev/null +++ b/site/pt-BR/streams/optional-or.html @@ -0,0 +1,371 @@ + + + + + + Optional.or() fallback | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + intermediate +
+

Optional.or() fallback

+

Chain Optional fallbacks without nested checks.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Optional<Config> cfg = primary();
+if (!cfg.isPresent()) {
+    cfg = secondary();
+}
+if (!cfg.isPresent()) {
+    cfg = defaults();
+}
+
+
+
+
+ ✓ Java 9+ + +
+
+
Optional<Config> cfg = primary()
+    .or(this::secondary)
+    .or(this::defaults);
+
+
+
+
+ +
+ +
+
+
🔗
+

Chainable

+

Stack fallbacks in a readable pipeline.

+
+
+
+

Lazy evaluation

+

Fallback suppliers only execute if needed.

+
+
+
📖
+

Declarative

+

Reads as 'try primary, or secondary, or defaults'.

+
+
+
+ +
+
+
Abordagem Antiga
+
Nested Fallback
+
+
+
Abordagem Moderna
+
.or() chain
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Optional.or() fallback
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Optional.or() returns the original Optional if it has a value, otherwise evaluates the supplier to get an alternative Optional. Suppliers are lazy — only called when needed.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/predicate-not.html b/site/pt-BR/streams/predicate-not.html new file mode 100644 index 0000000..75dee84 --- /dev/null +++ b/site/pt-BR/streams/predicate-not.html @@ -0,0 +1,362 @@ + + + + + + Predicate.not() for negation | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + beginner +
+

Predicate.not() for negation

+

Use Predicate.not() to negate method references cleanly instead of writing lambda wrappers.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
List<String> nonEmpty = list.stream()
+    .filter(s -> !s.isBlank())
+    .collect(Collectors.toList());
+
+
+
+
+ ✓ Java 11+ + +
+
+
List<String> nonEmpty = list.stream()
+    .filter(Predicate.not(String::isBlank))
+    .toList();
+
+
+
+
+ +
+ +
+
+
👁
+

Cleaner negation

+

No need to wrap method references in lambdas just to negate them.

+
+
+
🔗
+

Composable

+

Works with any Predicate, enabling clean predicate chains.

+
+
+
📖
+

Reads naturally

+

Predicate.not(String::isBlank) reads like English.

+
+
+
+ +
+
+
Abordagem Antiga
+
Lambda negation
+
+
+
Abordagem Moderna
+
Predicate.not()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Predicate.not() for negation
+ Disponível +

Available since JDK 11 (September 2018).

+
+
+ +
+ +

Before Java 11, negating a method reference required wrapping it in a lambda. Predicate.not() lets you negate any predicate directly, keeping the code readable and consistent with method reference style throughout the stream pipeline.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/stream-gatherers.html b/site/pt-BR/streams/stream-gatherers.html new file mode 100644 index 0000000..c620162 --- /dev/null +++ b/site/pt-BR/streams/stream-gatherers.html @@ -0,0 +1,372 @@ + + + + + + Stream gatherers | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + advanced +
+

Stream gatherers

+

Use gatherers for custom intermediate stream operations.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Sliding window: manual implementation
+List<List<T>> windows = new ArrayList<>();
+for (int i = 0; i <= list.size()-3; i++) {
+    windows.add(
+        list.subList(i, i + 3));
+}
+
+
+
+
+ ✓ Java 24+ + +
+
+
var windows = stream
+    .gather(
+        Gatherers.windowSliding(3)
+    )
+    .toList();
+
+
+
+
+ +
+ +
+
+
🧩
+

Composable

+

Gatherers compose with other stream operations.

+
+
+
📦
+

Built-in operations

+

windowFixed, windowSliding, fold, scan out of the box.

+
+
+
🔧
+

Extensible

+

Write custom gatherers for any intermediate transformation.

+
+
+
+ +
+
+
Abordagem Antiga
+
Custom Collector
+
+
+
Abordagem Moderna
+
gather()
+
+
+
Desde o JDK
+
24
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Stream gatherers
+ Disponível +

Finalized in JDK 24 (JEP 485, March 2025).

+
+
+ +
+ +

Gatherers are a new intermediate stream operation that can express complex transformations like sliding windows, fixed-size groups, and scan operations that were impossible with standard stream ops.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/stream-iterate-predicate.html b/site/pt-BR/streams/stream-iterate-predicate.html new file mode 100644 index 0000000..370c480 --- /dev/null +++ b/site/pt-BR/streams/stream-iterate-predicate.html @@ -0,0 +1,380 @@ + + + + + + Stream.iterate() with predicate | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + intermediate +
+

Stream.iterate() with predicate

+

Use a predicate to stop iteration — like a for-loop in stream form.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Stream.iterate(1, n -> n * 2)
+    .limit(10)
+    .forEach(System.out::println);
+// can't stop at a condition
+
+
+
+
+ ✓ Java 9+ + +
+
+
Stream.iterate(
+    1,
+    n -> n < 1000,
+    n -> n * 2
+).forEach(System.out::println);
+// stops when n >= 1000
+
+
+
+
+ +
+ +
+
+
🎯
+

Natural termination

+

Stop based on a condition, not an arbitrary limit.

+
+
+
📐
+

For-loop equivalent

+

Same semantics as for(seed; hasNext; next).

+
+
+
🛡️
+

No infinite stream risk

+

The predicate guarantees termination.

+
+
+
+ +
+
+
Abordagem Antiga
+
iterate + limit
+
+
+
Abordagem Moderna
+
iterate(seed, pred, op)
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Stream.iterate() with predicate
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

The three-argument Stream.iterate(seed, hasNext, next) works like a for-loop: seed is the start, hasNext determines when to stop, and next produces the next value.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/stream-mapmulti.html b/site/pt-BR/streams/stream-mapmulti.html new file mode 100644 index 0000000..5a47640 --- /dev/null +++ b/site/pt-BR/streams/stream-mapmulti.html @@ -0,0 +1,374 @@ + + + + + + Stream.mapMulti() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + intermediate +
+

Stream.mapMulti()

+

Emit zero or more elements per input without creating intermediate streams.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
stream.flatMap(order ->
+    order.items().stream()
+        .map(item -> new OrderItem(
+            order.id(), item)
+        )
+);
+
+
+
+
+ ✓ Java 16+ + +
+
+
stream.<OrderItem>mapMulti(
+    (order, downstream) -> {
+        for (var item : order.items())
+            downstream.accept(
+                new OrderItem(order.id(), item));
+    }
+);
+
+
+
+
+ +
+ +
+
+
+

Less allocation

+

No intermediate Stream created per element.

+
+
+
🎯
+

Imperative style

+

Use loops and conditionals directly.

+
+
+
📐
+

Flexible

+

Emit zero, one, or many elements with full control.

+
+
+
+ +
+
+
Abordagem Antiga
+
flatMap + List
+
+
+
Abordagem Moderna
+
mapMulti()
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Stream.mapMulti()
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

mapMulti() is an imperative alternative to flatMap that avoids creating intermediate Stream objects for each element. It's more efficient when the mapping produces a small number of elements.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/stream-of-nullable.html b/site/pt-BR/streams/stream-of-nullable.html new file mode 100644 index 0000000..07bc867 --- /dev/null +++ b/site/pt-BR/streams/stream-of-nullable.html @@ -0,0 +1,371 @@ + + + + + + Stream.ofNullable() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + beginner +
+

Stream.ofNullable()

+

Create a zero-or-one element stream from a nullable value.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
Stream<String> s = val != null
+    ? Stream.of(val)
+    : Stream.empty();
+
+
+
+
+ ✓ Java 9+ + +
+
+
Stream<String> s =
+    Stream.ofNullable(val);
+
+
+
+
+ +
+ +
+
+
📏
+

Concise

+

One call replaces the ternary conditional.

+
+
+
🔗
+

Flatmap-friendly

+

Perfect inside flatMap to skip null values.

+
+
+
🛡️
+

Null-safe

+

No NPE risk — null becomes empty stream.

+
+
+
+ +
+
+
Abordagem Antiga
+
Null Check
+
+
+
Abordagem Moderna
+
ofNullable()
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Stream.ofNullable()
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

Stream.ofNullable() returns a single-element stream if the value is non-null, or an empty stream if null. Eliminates the ternary null check pattern.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/stream-takewhile-dropwhile.html b/site/pt-BR/streams/stream-takewhile-dropwhile.html new file mode 100644 index 0000000..9e6c6e7 --- /dev/null +++ b/site/pt-BR/streams/stream-takewhile-dropwhile.html @@ -0,0 +1,375 @@ + + + + + + Stream takeWhile / dropWhile | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + beginner +
+

Stream takeWhile / dropWhile

+

Take or drop elements from a stream based on a predicate.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
List<Integer> result = new ArrayList<>();
+for (int n : sorted) {
+    if (n >= 100) break;
+    result.add(n);
+}
+// no stream equivalent in Java 8
+
+
+
+
+ ✓ Java 9+ + +
+
+
var result = sorted.stream()
+    .takeWhile(n -> n < 100)
+    .toList();
+// or: .dropWhile(n -> n < 10)
+
+
+
+
+ +
+ +
+
+
🎯
+

Short-circuit

+

Stops processing as soon as the predicate fails.

+
+
+
🔗
+

Pipeline-friendly

+

Chain with other stream operations naturally.

+
+
+
📖
+

Declarative

+

takeWhile reads like English: 'take while less than 100'.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Loop
+
+
+
Abordagem Moderna
+
takeWhile/dropWhile
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Stream takeWhile / dropWhile
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

takeWhile() returns elements while the predicate is true and stops at the first false. dropWhile() skips elements while true and returns the rest. Both work best on ordered streams.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/stream-tolist.html b/site/pt-BR/streams/stream-tolist.html new file mode 100644 index 0000000..d0793d4 --- /dev/null +++ b/site/pt-BR/streams/stream-tolist.html @@ -0,0 +1,369 @@ + + + + + + Stream.toList() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + beginner +
+

Stream.toList()

+

Terminal toList() replaces the verbose collect(Collectors.toList()).

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
List<String> result = stream
+    .filter(s -> s.length() > 3)
+    .collect(Collectors.toList());
+
+
+
+
+ ✓ Java 16+ + +
+
+
List<String> result = stream
+    .filter(s -> s.length() > 3)
+    .toList();
+
+
+
+
+ +
+ +
+
+
📏
+

7 chars vs 24

+

.toList() replaces .collect(Collectors.toList()).

+
+
+
🔒
+

Immutable

+

The result list cannot be modified.

+
+
+
📖
+

Fluent

+

Reads naturally at the end of a pipeline.

+
+
+
+ +
+
+
Abordagem Antiga
+
Collectors.toList()
+
+
+
Abordagem Moderna
+
.toList()
+
+
+
Desde o JDK
+
16
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Stream.toList()
+ Disponível +

Widely available since JDK 16 (March 2021)

+
+
+ +
+ +

Stream.toList() returns an unmodifiable list. It's equivalent to .collect(Collectors.toUnmodifiableList()) but much shorter. Note: the result is immutable, unlike Collectors.toList().

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/streams/virtual-thread-executor.html b/site/pt-BR/streams/virtual-thread-executor.html new file mode 100644 index 0000000..11ff772 --- /dev/null +++ b/site/pt-BR/streams/virtual-thread-executor.html @@ -0,0 +1,382 @@ + + + + + + Virtual thread executor | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Streams + intermediate +
+

Virtual thread executor

+

Use virtual thread executors for unlimited lightweight concurrency.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
ExecutorService exec =
+    Executors.newFixedThreadPool(10);
+try {
+    futures = tasks.stream()
+        .map(t -> exec.submit(t))
+        .toList();
+} finally {
+    exec.shutdown();
+}
+
+
+
+
+ ✓ Java 21+ + +
+
+
try (var exec = Executors
+        .newVirtualThreadPerTaskExecutor()) {
+    var futures = tasks.stream()
+        .map(exec::submit)
+        .toList();
+}
+
+
+
+
+ +
+ +
+
+
♾️
+

No sizing

+

No pool size to tune — create as many threads as needed.

+
+
+
+

Lightweight

+

Virtual threads use KB of memory, not MB.

+
+
+
🧹
+

Auto-closeable

+

try-with-resources handles shutdown automatically.

+
+
+
+ +
+
+
Abordagem Antiga
+
Fixed Thread Pool
+
+
+
Abordagem Moderna
+
Virtual Thread Executor
+
+
+
Desde o JDK
+
21
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Virtual thread executor
+ Disponível +

Widely available since JDK 21 LTS (Sept 2023)

+
+
+ +
+ +

The virtual thread executor creates a new virtual thread for each task. No pool sizing needed — virtual threads are cheap enough to create millions of them.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-chars-stream.html b/site/pt-BR/strings/string-chars-stream.html new file mode 100644 index 0000000..1055765 --- /dev/null +++ b/site/pt-BR/strings/string-chars-stream.html @@ -0,0 +1,364 @@ + + + + + + String chars as stream | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String chars as stream

+

Process string characters as a stream pipeline.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
for (int i = 0; i < str.length(); i++) {
+    char c = str.charAt(i);
+    if (Character.isDigit(c)) {
+        process(c);
+    }
+}
+
+
+
+
+ ✓ Java 9+ + +
+
+
str.chars()
+    .filter(Character::isDigit)
+    .forEach(c -> process((char) c));
+
+
+
+
+ +
+ +
+
+
🔗
+

Chainable

+

Use filter, map, collect on character streams.

+
+
+
📐
+

Declarative

+

Describe what to do, not how to loop.

+
+
+
🌐
+

Unicode-ready

+

codePoints() correctly handles emoji and supplementary chars.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Loop
+
+
+
Abordagem Moderna
+
chars() Stream
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String chars as stream
+ Disponível +

Available since JDK 8+ (improved in 9+)

+
+
+ +
+ +

String.chars() returns an IntStream of character values, enabling functional processing. For Unicode support, codePoints() handles supplementary characters correctly.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-formatted.html b/site/pt-BR/strings/string-formatted.html new file mode 100644 index 0000000..9b4beb2 --- /dev/null +++ b/site/pt-BR/strings/string-formatted.html @@ -0,0 +1,368 @@ + + + + + + String.formatted() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String.formatted()

+

Call formatted() on the template string itself.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String msg = String.format(
+    "Hello %s, you are %d",
+    name, age
+);
+
+
+
+
+ ✓ Java 15+ + +
+
+
String msg =
+    "Hello %s, you are %d"
+    .formatted(name, age);
+
+
+
+
+ +
+ +
+
+
📖
+

Reads naturally

+

Template.formatted(args) flows better than String.format(template, args).

+
+
+
🔗
+

Chainable

+

Can be chained with other string methods.

+
+
+
📏
+

Less verbose

+

Drops the redundant String.format() static call.

+
+
+
+ +
+
+
Abordagem Antiga
+
String.format()
+
+
+
Abordagem Moderna
+
formatted()
+
+
+
Desde o JDK
+
15
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String.formatted()
+ Disponível +

Widely available since JDK 15 (Sept 2020)

+
+
+ +
+ +

String.formatted() is an instance method equivalent to String.format() but called on the format string. It reads more naturally in a left-to-right flow.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-indent-transform.html b/site/pt-BR/strings/string-indent-transform.html new file mode 100644 index 0000000..f78aec7 --- /dev/null +++ b/site/pt-BR/strings/string-indent-transform.html @@ -0,0 +1,367 @@ + + + + + + String.indent() and transform() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String.indent() and transform()

+

Indent text and chain string transformations fluently.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String[] lines = text.split("\n");
+StringBuilder sb = new StringBuilder();
+for (String line : lines) {
+    sb.append("    ").append(line)
+      .append("\n");
+}
+String indented = sb.toString();
+
+
+
+
+ ✓ Java 12+ + +
+
+
String indented = text.indent(4);
+
+String result = text
+    .transform(String::strip)
+    .transform(s -> s.replace(" ", "-"));
+
+
+
+
+ +
+ +
+
+
📏
+

Built-in

+

Indentation is a common operation — now it's one call.

+
+
+
🔗
+

Chainable

+

transform() enables fluent pipelines on strings.

+
+
+
🧹
+

Clean code

+

No manual line splitting and StringBuilder loops.

+
+
+
+ +
+
+
Abordagem Antiga
+
Manual Indentation
+
+
+
Abordagem Moderna
+
indent() / transform()
+
+
+
Desde o JDK
+
12
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String.indent() and transform()
+ Disponível +

Widely available since JDK 12 (March 2019)

+
+
+ +
+ +

indent(n) adds n spaces to each line. transform(fn) applies any function and returns the result, enabling fluent chaining of string operations.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-isblank.html b/site/pt-BR/strings/string-isblank.html new file mode 100644 index 0000000..06450f4 --- /dev/null +++ b/site/pt-BR/strings/string-isblank.html @@ -0,0 +1,366 @@ + + + + + + String.isBlank() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String.isBlank()

+

Check for blank strings with a single method call.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
boolean blank =
+    str.trim().isEmpty();
+// or: str.trim().length() == 0
+
+
+
+
+ ✓ Java 11+ + +
+
+
boolean blank = str.isBlank();
+// handles Unicode whitespace too
+
+
+
+
+ +
+ +
+
+
📖
+

Self-documenting

+

isBlank() says exactly what it checks.

+
+
+
🌐
+

Unicode-aware

+

Handles all Unicode whitespace, not just ASCII.

+
+
+
+

No allocation

+

No intermediate trimmed string is created.

+
+
+
+ +
+
+
Abordagem Antiga
+
trim().isEmpty()
+
+
+
Abordagem Moderna
+
isBlank()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String.isBlank()
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

isBlank() returns true if the string is empty or contains only whitespace, including Unicode whitespace characters that trim() misses.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-lines.html b/site/pt-BR/strings/string-lines.html new file mode 100644 index 0000000..a4403ab --- /dev/null +++ b/site/pt-BR/strings/string-lines.html @@ -0,0 +1,366 @@ + + + + + + String.lines() for line splitting | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String.lines() for line splitting

+

Use String.lines() to split text into a stream of lines without regex overhead.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
String text = "one\ntwo\nthree";
+String[] lines = text.split("\n");
+for (String line : lines) {
+    System.out.println(line);
+}
+
+
+
+
+ ✓ Java 11+ + +
+
+
String text = "one\ntwo\nthree";
+text.lines().forEach(System.out::println);
+
+
+
+
+ +
+ +
+
+
+

Lazy streaming

+

Lines are produced on demand, not all at once like split().

+
+
+
🔧
+

Universal line endings

+

Handles \n, \r, and \r\n automatically without regex.

+
+
+
🔗
+

Stream integration

+

Returns a Stream for direct use with filter, map, collect.

+
+
+
+ +
+
+
Abordagem Antiga
+
split("\\n")
+
+
+
Abordagem Moderna
+
lines()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String.lines() for line splitting
+ Disponível +

Available since JDK 11 (September 2018).

+
+
+ +
+ +

String.lines() returns a Stream<String> of lines split by \n, \r, or \r\n. It is lazier and more efficient than split(), avoids regex compilation, and integrates naturally with the Stream API for further processing.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-repeat.html b/site/pt-BR/strings/string-repeat.html new file mode 100644 index 0000000..7123717 --- /dev/null +++ b/site/pt-BR/strings/string-repeat.html @@ -0,0 +1,363 @@ + + + + + + String.repeat() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String.repeat()

+

Repeat a string n times without a loop.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
StringBuilder sb = new StringBuilder();
+for (int i = 0; i < 3; i++) {
+    sb.append("abc");
+}
+String result = sb.toString();
+
+
+
+
+ ✓ Java 11+ + +
+
+
String result = "abc".repeat(3);
+// "abcabcabc"
+
+
+
+
+ +
+ +
+
+
📏
+

One-liner

+

Replace 5 lines of StringBuilder code with one call.

+
+
+
+

Optimized

+

Internal implementation is optimized for large repeats.

+
+
+
📖
+

Clear intent

+

repeat(3) immediately conveys the purpose.

+
+
+
+ +
+
+
Abordagem Antiga
+
StringBuilder Loop
+
+
+
Abordagem Moderna
+
repeat()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String.repeat()
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

String.repeat(int) returns the string concatenated with itself n times. Handles edge cases: repeat(0) returns empty string, repeat(1) returns the same string.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/strings/string-strip.html b/site/pt-BR/strings/string-strip.html new file mode 100644 index 0000000..e953598 --- /dev/null +++ b/site/pt-BR/strings/string-strip.html @@ -0,0 +1,370 @@ + + + + + + String.strip() vs trim() | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Strings + beginner +
+

String.strip() vs trim()

+

Use Unicode-aware stripping with strip(), stripLeading(), stripTrailing().

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// trim() only removes ASCII whitespace
+// (chars <= U+0020)
+String clean = str.trim();
+
+
+
+
+ ✓ Java 11+ + +
+
+
// strip() removes all Unicode whitespace
+String clean = str.strip();
+String left  = str.stripLeading();
+String right = str.stripTrailing();
+
+
+
+
+ +
+ +
+
+
🌐
+

Unicode-correct

+

Handles all whitespace characters from every script.

+
+
+
🎯
+

Directional

+

stripLeading() and stripTrailing() for one-sided trimming.

+
+
+
🛡️
+

Fewer bugs

+

No surprise whitespace left behind in international text.

+
+
+
+ +
+
+
Abordagem Antiga
+
trim()
+
+
+
Abordagem Moderna
+
strip()
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
String.strip() vs trim()
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

trim() only removes characters ≤ U+0020 (ASCII control chars and space). strip() uses Character.isWhitespace() which handles Unicode spaces like non-breaking space, ideographic space, etc.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/aot-class-preloading.html b/site/pt-BR/tooling/aot-class-preloading.html new file mode 100644 index 0000000..ae3aeae --- /dev/null +++ b/site/pt-BR/tooling/aot-class-preloading.html @@ -0,0 +1,371 @@ + + + + + + AOT class preloading | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + advanced +
+

AOT class preloading

+

Cache class loading and compilation for instant startup.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Every startup:
+// - Load 10,000+ classes
+// - Verify bytecode
+// - JIT compile hot paths
+// Startup: 2-5 seconds
+
+
+
+
+ ✓ Java 25 + +
+
+
// Training run:
+$ java -XX:AOTCacheOutput=app.aot \
+    -cp app.jar com.App
+// Production:
+$ java -XX:AOTCache=app.aot \
+    -cp app.jar com.App
+
+
+
+
+ +
+ +
+
+
+

Faster startup

+

Skip class loading, verification, and linking.

+
+
+
📦
+

Cached state

+

Training run captures the ideal class state.

+
+
+
🔧
+

No code changes

+

Works with existing applications — just add JVM flags.

+
+
+
+ +
+
+
Abordagem Antiga
+
Cold Start Every Time
+
+
+
Abordagem Moderna
+
AOT Cache
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
AOT class preloading
+ Disponível +

Available as a standard feature in JDK 25 LTS (JEPs 514/515, Sept 2025).

+
+
+ +
+ +

AOT class preloading caches loaded and linked classes from a training run. On subsequent starts, classes are loaded from the cache, skipping verification and linking. Combined with AOT compilation, this dramatically reduces startup time.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/built-in-http-server.html b/site/pt-BR/tooling/built-in-http-server.html new file mode 100644 index 0000000..bc6cc74 --- /dev/null +++ b/site/pt-BR/tooling/built-in-http-server.html @@ -0,0 +1,384 @@ + + + + + + Built-in HTTP server | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + beginner +
+

Built-in HTTP server

+

Java 18 includes a built-in minimal HTTP server for prototyping and file serving.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// 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();
+
+
+
+
+ ✓ Java 18+ + +
+
+
// Terminal: serve current directory
+$ jwebserver
+
+// Or use the API (JDK 18+)
+var server = SimpleFileServer.createFileServer(
+    new InetSocketAddress(8080),
+    Path.of("."),
+    OutputLevel.VERBOSE);
+server.start();
+
+
+
+
+ +
+ +
+
+
🚀
+

Zero setup

+

Run jwebserver in any directory — no installation, config, or dependencies needed.

+
+
+
📦
+

Built into the JDK

+

Ships with every JDK 18+ installation, always available on any machine with Java.

+
+
+
🧪
+

Great for prototyping

+

Serve static files instantly for testing HTML, APIs, or front-end development.

+
+
+
+ +
+
+
Abordagem Antiga
+
External Server / Framework
+
+
+
Abordagem Moderna
+
jwebserver CLI
+
+
+
Desde o JDK
+
18
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Built-in HTTP server
+ Disponível +

Available since JDK 18 (March 2022)

+
+
+ +
+ +

JDK 18 added a simple, zero-dependency HTTP file server accessible via the jwebserver command-line tool or the SimpleFileServer API. It serves static files from a given directory with no configuration needed. The CLI tool is ideal for quick prototyping, testing, and ad-hoc file sharing — no external dependencies or frameworks required. The API allows programmatic use with customizable handlers and output levels.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/compact-object-headers.html b/site/pt-BR/tooling/compact-object-headers.html new file mode 100644 index 0000000..ad0b0b3 --- /dev/null +++ b/site/pt-BR/tooling/compact-object-headers.html @@ -0,0 +1,368 @@ + + + + + + Compact object headers | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + advanced +
+

Compact object headers

+

Cut object header size in half for better memory density and cache usage.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Default: 128-bit object header
+// = 16 bytes overhead per object
+// A boolean field object = 32 bytes!
+// Mark word (64) + Klass pointer (64)
+
+
+
+
+ ✓ Java 25 + +
+
+
// -XX:+UseCompactObjectHeaders
+// 64-bit object header
+// = 8 bytes overhead per object
+// 50% less header memory
+// More objects fit in cache
+
+
+
+
+ +
+ +
+
+
📦
+

50% smaller headers

+

8 bytes instead of 16 per object.

+
+
+
+

Better cache usage

+

More objects fit in CPU cache lines.

+
+
+
📊
+

Higher density

+

Fit more objects in the same heap size.

+
+
+
+ +
+
+
Abordagem Antiga
+
128-bit Headers
+
+
+
Abordagem Moderna
+
64-bit Headers
+
+
+
Desde o JDK
+
25
+
+
+
Dificuldade
+
advanced
+
+
+ +
+ +
+
Compact object headers
+ Disponível +

Finalized in JDK 25 LTS (JEP 519, Sept 2025).

+
+
+ +
+ +

Compact object headers reduce the per-object overhead from 128 bits to 64 bits on 64-bit platforms. This saves memory and improves cache utilization, especially for applications with many small objects.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/jfr-profiling.html b/site/pt-BR/tooling/jfr-profiling.html new file mode 100644 index 0000000..2fade8c --- /dev/null +++ b/site/pt-BR/tooling/jfr-profiling.html @@ -0,0 +1,371 @@ + + + + + + JFR for profiling | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + intermediate +
+

JFR for profiling

+

Profile any Java app with the built-in Flight Recorder — no external tools.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// Install VisualVM / YourKit / JProfiler
+// Attach to running process
+// Configure sampling
+// Export and analyze
+// External tool required
+
+
+
+
+ ✓ Java 9+ + +
+
+
// Start with profiling enabled
+$ java -XX:StartFlightRecording=
+    filename=rec.jfr MyApp
+
+// Or attach to running app:
+$ jcmd <pid> JFR.start
+
+
+
+
+ +
+ +
+
+
🆓
+

Built-in

+

No external profiler to install or license.

+
+
+
+

Low overhead

+

~1% performance impact — safe for production.

+
+
+
📊
+

Rich events

+

CPU, memory, GC, threads, I/O, locks, and custom events.

+
+
+
+ +
+
+
Abordagem Antiga
+
External Profiler
+
+
+
Abordagem Moderna
+
Java Flight Recorder
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JFR for profiling
+ Disponível +

Widely available since JDK 9/11 (open-sourced in 11)

+
+
+ +
+ +

Java Flight Recorder (JFR) is a low-overhead profiling tool built into the JVM. It captures events for CPU, memory, GC, I/O, threads, and custom events with minimal performance impact (~1%).

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/jshell-prototyping.html b/site/pt-BR/tooling/jshell-prototyping.html new file mode 100644 index 0000000..130b3c9 --- /dev/null +++ b/site/pt-BR/tooling/jshell-prototyping.html @@ -0,0 +1,373 @@ + + + + + + JShell for prototyping | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + beginner +
+

JShell for prototyping

+

Try Java expressions interactively without creating files.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
// 1. Create Test.java
+// 2. javac Test.java
+// 3. java Test
+// Just to test one expression!
+
+
+
+
+ ✓ Java 9+ + +
+
+
$ jshell
+jshell> "hello".chars().count()
+$1 ==> 5
+jshell> List.of(1,2,3).reversed()
+$2 ==> [3, 2, 1]
+
+
+
+
+ +
+ +
+
+
+

Instant feedback

+

Type an expression, see the result immediately.

+
+
+
📝
+

No files needed

+

No .java files, no compilation step.

+
+
+
🔍
+

API exploration

+

Tab completion helps discover methods and parameters.

+
+
+
+ +
+
+
Abordagem Antiga
+
Create File + Compile + Run
+
+
+
Abordagem Moderna
+
jshell REPL
+
+
+
Desde o JDK
+
9
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
JShell for prototyping
+ Disponível +

Widely available since JDK 9 (Sept 2017)

+
+
+ +
+ +

JShell is a Read-Eval-Print Loop for Java. Test expressions, experiment with APIs, and prototype code without creating files, compiling, or writing a main method. Tab completion and inline docs included.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/junit6-with-jspecify.html b/site/pt-BR/tooling/junit6-with-jspecify.html new file mode 100644 index 0000000..6dc7eaf --- /dev/null +++ b/site/pt-BR/tooling/junit6-with-jspecify.html @@ -0,0 +1,442 @@ + + + + + + JUnit 6 with JSpecify null safety | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + intermediate +
+

JUnit 6 with JSpecify null safety

+

JUnit 6 adopts JSpecify @NullMarked, making null contracts explicit across its assertion API.

+
+ +
+ +
+
+
+ ✕ JUnit 5 + +
+
+
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"));
+    }
+}
+
+
+
+
+ ✓ JUnit 6 + +
+
+
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
+    }
+}
+
+
+
+
+ +
+ +
+
+
📜
+

Explicit contracts

+

@NullMarked on the JUnit 6 module documents null semantics directly in the API — no source-reading required.

+
+
+
🛡️
+

Compile-time safety

+

IDEs and analyzers warn when null is passed where non-null is expected, catching bugs before tests run.

+
+
+
🌐
+

Ecosystem standard

+

JSpecify is adopted by Spring, Guava, and others — consistent null semantics across your whole stack.

+
+
+
+ +
+
+
Abordagem Antiga
+
Unannotated API
+
+
+
Abordagem Moderna
+
@NullMarked API
+
+
+
Desde o JDK
+
17
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
JUnit 6 with JSpecify null safety
+ Disponível +

Available since JUnit 6.0 (October 2025, requires Java 17+)

+
+
+ +
+ +

JUnit 5 shipped without standardized nullability annotations, leaving developers to guess whether assertion parameters or return values could be null. JUnit 6 adopts JSpecify across its entire module: the @NullMarked annotation makes all unannotated types non-null by default, and @Nullable marks the exceptions. The Assertions class explicitly annotates parameters such as assertNull(@Nullable Object actual) and fail(@Nullable String message), so IDEs and static analyzers like NullAway and Error Prone can catch null misuse at compile time instead of at runtime.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/multi-file-source.html b/site/pt-BR/tooling/multi-file-source.html new file mode 100644 index 0000000..6468e7b --- /dev/null +++ b/site/pt-BR/tooling/multi-file-source.html @@ -0,0 +1,372 @@ + + + + + + Multi-file source launcher | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + intermediate +
+

Multi-file source launcher

+

Launch multi-file programs without an explicit compile step.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
$ javac *.java
+$ java Main
+// Must compile all files first
+// Need a build tool for dependencies
+
+
+
+
+ ✓ Java 22+ + +
+
+
$ java Main.java
+// Automatically finds and compiles
+// other source files referenced
+// by Main.java
+
+
+
+
+ +
+ +
+
+
🚀
+

Zero setup

+

No build tool needed for small multi-file programs.

+
+
+
🔗
+

Auto-resolve

+

Referenced classes are found and compiled automatically.

+
+
+
📝
+

Script-like

+

Run multi-file programs like scripts.

+
+
+
+ +
+
+
Abordagem Antiga
+
Compile All First
+
+
+
Abordagem Moderna
+
Source Launcher
+
+
+
Desde o JDK
+
22
+
+
+
Dificuldade
+
intermediate
+
+
+ +
+ +
+
Multi-file source launcher
+ Disponível +

Available since JDK 22 (March 2024)

+
+
+ +
+ +

Java 22+ can automatically compile referenced source files when launching from a .java file. This makes small multi-file programs as easy to run as scripts, without needing Maven or Gradle.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/pt-BR/tooling/single-file-execution.html b/site/pt-BR/tooling/single-file-execution.html new file mode 100644 index 0000000..44b2844 --- /dev/null +++ b/site/pt-BR/tooling/single-file-execution.html @@ -0,0 +1,370 @@ + + + + + + Single-file execution | java.evolved + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ Tooling + beginner +
+

Single-file execution

+

Run single-file Java programs directly without javac.

+
+ +
+ +
+
+
+ ✕ Java 8 + +
+
+
$ javac HelloWorld.java
+$ java HelloWorld
+// Two steps every time
+
+
+
+
+ ✓ Java 11+ + +
+
+
$ java HelloWorld.java
+// Compiles and runs in one step
+// Also works with shebangs:
+#!/usr/bin/java --source 25
+
+
+
+
+ +
+ +
+
+
+

One command

+

java File.java compiles and runs in one step.

+
+
+
📝
+

Script-like

+

Add a shebang line to make .java files executable scripts.

+
+
+
🎓
+

Learning-friendly

+

Newcomers run code immediately without learning build tools.

+
+
+
+ +
+
+
Abordagem Antiga
+
Two-Step Compile
+
+
+
Abordagem Moderna
+
Direct Launch
+
+
+
Desde o JDK
+
11
+
+
+
Dificuldade
+
beginner
+
+
+ +
+ +
+
Single-file execution
+ Disponível +

Widely available since JDK 11 (Sept 2018)

+
+
+ +
+ +

The Java launcher can compile and run a single source file in one command. Combined with shebang support on Unix, Java files can work as scripts. No separate compilation step needed.

+
+ +
+ + +
+ + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/site/styles.css b/site/styles.css index 82f19e1..581dc5c 100644 --- a/site/styles.css +++ b/site/styles.css @@ -1572,3 +1572,84 @@ footer a:hover { } .share-li { font-family: Georgia, serif; font-weight: 800; font-size: 0.85rem; } .share-reddit { font-size: 1.1rem; } + +/* ============================ + Locale Picker + ============================ */ +.locale-picker { + position: relative; + display: inline-flex; +} +.locale-toggle { + display: flex; + align-items: center; + justify-content: center; + padding: 8px; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + background: var(--surface); + color: var(--text-muted); + font-size: 1rem; + line-height: 1; + transition: all 0.2s; + cursor: pointer; +} +.locale-toggle:hover { + background: var(--accent); + color: #fff; + border-color: var(--accent); +} +.locale-picker ul { + display: none; + position: absolute; + top: 100%; + right: 0; + margin: 4px 0 0; + padding: 4px 0; + list-style: none; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + z-index: 100; + min-width: 160px; +} +.locale-picker li { + padding: 8px 16px; + cursor: pointer; + font-size: 0.9rem; + color: var(--text); + white-space: nowrap; + transition: background 0.15s; +} +.locale-picker li:hover { + background: var(--accent); + color: #fff; +} +.locale-picker li.active { + font-weight: 600; + color: var(--accent); +} +.locale-picker li.active:hover { + color: #fff; +} + +/* ============================ + Untranslated Notice Banner + ============================ */ +.untranslated-notice { + background: var(--surface); + border: 1px solid var(--border); + border-left: 4px solid var(--accent); + padding: 12px 16px; + margin: 0 auto 24px; + max-width: 800px; + border-radius: var(--radius-sm); + font-size: 0.9rem; + color: var(--text-muted); +} +.untranslated-notice a { + color: var(--accent); + text-decoration: underline; + margin-left: 8px; +} diff --git a/templates/index-card.html b/templates/index-card.html index 89ea235..0117fdf 100644 --- a/templates/index-card.html +++ b/templates/index-card.html @@ -1,4 +1,4 @@ - +
{{catDisplay}}
@@ -7,14 +7,14 @@

{{title}}

- Old + {{cards.old}} {{oldCode}}
- Modern + {{cards.modern}} {{modernCode}}
- hover to see modern → + {{cards.hoverHint}}