Java 17: Oppgrader nå – nye funksjoner og forbedret ytelse!

Java 17, en versjon med langvarig støtte (LTS) for Java-språk og kjøretidsplattform, ble lansert 14. september 2021. La oss utforske de nye funksjonene i Java 17 og om det er fordelaktig for deg å oppgradere.

Mange applikasjoner benytter eldre Java-versjoner, inkludert tidligere LTS-utgaver som Java 11 og Java 8.

Hvorfor bør virksomheter vurdere en overgang til den nyeste Java-versjonen? En oppgradering til Java 17 innebærer en viss innsats, hovedsakelig for å dra full nytte av de nye funksjonene og forbedringene i JVM.

Mange organisasjoner bruker Docker og Docker-avbildninger for å forenkle overgangen til Java 17 med minimalt arbeid og tidsbruk. Utviklere kan konfigurere sine kontinuerlige integrasjons-/distribusjonsflyter (CI/CD) og utføre alt i Docker-avbildninger. Dette vil ikke påvirke andre lag som bruker eldre Java-versjoner, da de kan fortsette å bruke sine eksisterende Docker-avbildninger.

Nye funksjoner i Java 17

Støtte for macOS og AArch64

En viktig JVM-funksjon i denne versjonen er forbedret støtte for macOS på AArch64-arkitektur, basert på JEP 391. Den gir støtte for den nyeste serien av prosessorer (M1) som Apple lanserte i sine datamaskiner det siste året.

Dette er ikke nødvendigvis en stor nyhet for brukere av disse plattformene, da noen leverandører allerede har lansert JDK-versjoner med støtte for denne arkitekturen, til og med tilbake til Java 8. Den offisielle godkjennelsen er likevel avgjørende for å sikre fremtidig vedlikehold og støtte for plattformen. Til sammenligning ble støtte for Linux/AArch64 lagt til i Java 9, og for Windows/AArch64 i Java 16.

Forseglede klasser

Forseglede klasser er en funksjon som ble introdusert i Java 17. Denne funksjonen har fullført sin prøveperiode og er nå en offisiell del av plattformen og språket i Java 17. Den gir utviklere muligheten til å spesifisere tillatte subtyper for en type, og hindrer andre i å utvide eller implementere den på en uønsket måte.

Forseglede klasser gir også kompilatoren muligheten til å generere feil ved kompileringstidspunktet hvis man forsøker å konvertere en ikke-forseglet type til en ikke-tillatt subtype. Java 17 introduserer også en ny gjengivelsespipeline for AWT/Swing-applikasjoner som kjører på macOS ved hjelp av Apple Metal API istedenfor OpenGL. Den har også forbedret API og funksjoner for generering av tilfeldige tall.

Endringer, slettinger og begrensninger i Java 17

Java 17 inneholder også flere endringer, slettinger og nye begrensninger.

Innkapsling av interne JDK-komponenter

En viktig endring er fullføringen av innkapslingsprosessen for interne JDK-komponenter. Dette ble først introdusert i Java 9 og genererte advarsler ved kjøretid når en bruker prøvde å bruke refleksjon eller lignende for å omgå de vanlige begrensningene for bruk av interne API-er. Kommandolinjeargumenter ble også introdusert for å kontrollere denne oppførselen.

Siden Java 9 har det blitt utviklet ulike API-er for å gi en enhetlig metode for å utføre de mest brukte oppgavene. Brukere forventes å bruke disse API-ene. I Java 16 ble standarden endret fra å gi en advarsel til å deaktivere tilgang og generere et unntak. Det var imidlertid mulig å endre denne atferden med et kommandolinjeargument.

I Java 17 er kommandolinjeargumentet fjernet, og det er ikke lenger mulig å deaktivere denne begrensningen. Dette betyr at all ikke-autorisert tilgang til disse interne API-ene nå er blokkert.

Alltid streng flytende punkt-semantikk

En annen viktig endring er gjeninnføringen av alltid streng flytende punkt-semantikk. Java 1.2 introduserte endringer i semantikkstandarden for flytende komma i Java, som gjorde det mulig for JVM å gi avkall på en liten mengde presisjon i flytende punkt-beregninger for å forbedre ytelsen. I klasser og metoder der det var nødvendig med streng semantikk, ble nøkkelordet `strictfp` lagt til. Siden den gang har ulike instruksjonssett blitt introdusert i CPU-er, som gjør det mulig å bruke streng flytende punkt-semantikk uten ytelsestap. Det er derfor ikke lenger behov for å implementere en standard eller streng semantikk.

Java 17 fjerner den tidligere standardsemantikken, og alle flyttallsoperasjoner utføres nå strengt. `strictfp` begrepet eksisterer fortsatt, men det har ingen effekt og genererer en advarsel ved kompilering.

Forhåndskompilering (AOT)

Java 9 introduserte forhåndskompilering (AOT) som en eksperimentell funksjon basert på Graal-kompilatoren. JIT-kode ble skrevet i Java. Java 10 gjorde Graal-kompilatoren tilgjengelig som en JIT-kompilator i OpenJDK ved å innlemme JVMCI-grensesnittet. Siden den gang har den opplevd store forbedringer. Graal-kompilatoren har gjort enorme fremskritt og har nå sin egen JVM, kalt GraalVM.

RMI-aktivering

RMI-aktivering ble fjernet i JEP 407, etter å ha blitt fjernet fra Java 8 og til slutt avviklet og merket som et krav for fjerning i Java 15. RMI-aktivering ga en metode for å aktivere distribuerte objekter ved behov ved hjelp av RMI. Den hadde imidlertid minimal bruk, og bedre alternativer er nå tilgjengelige. Resten av RMI forblir upåvirket av fjerningen av aktiveringsdelen.

Fjerning av Applet API

Applet API er endelig fjernet i JEP 398, etter å ha blitt avviklet i Java 9. Applet API gjorde det mulig å integrere Java AWT/Swing-kontroller på en nettside i en nettleser. Moderne nettlesere støtter imidlertid ikke dette, noe som betyr at applets har vært praktisk talt utilgjengelige det siste tiåret.

Sikkerhetsansvarlig

Den mest betydningsfulle avviklingen er sikkerhetsansvarlig ( JEP 411). Sikkerhetsansvarlig har vært i bruk siden Java 1.0. Den ble designet for å begrense hva Java kunne gjøre lokalt på maskinen, for eksempel tilgang til nettverk, filer og andre nettverksressurser. Den prøvde også å isolere kode som ikke var klarert, ved å blokkere refleksjon og spesifikke API-er.

Slutten på sikkerhetsansvarlig startet i Java 12. Et kommandolinjeargument ble lagt til for å blokkere bruken av sikkerhetsbehandling under kjøring. Endringen i Java 17 betyr at en kjøretidsadvarsel vil genereres i JVM når man prøver å sette en sikkerhetsansvarlig, enten fra kommandolinjen eller dynamisk under kjøring.

Inkubator- og forhåndsfunksjoner

Mange har lurt på om Java 17 ville inneholde forhånds- eller inkubatorfunksjoner, siden Java 17 er ment som en langsiktig støttet versjon. Java 17 har faktisk to inkubatormoduler og én forhåndsfunksjon!

Vektor API

Vektor API ( JEP 414) er for tiden i sin andre fase av inkubatoren. API-et gir utviklere mulighet til å definere vektorberegning som JIT-kompilatoren deretter vil konvertere til de riktige vektorinstruksjonene som støttes av CPU-arkitekturen som JVM kjører på (for eksempel ved bruk av SSE- eller AVX-instruksjonssett).

Tidligere måtte utviklere bruke skalarfunksjoner eller bygge native biblioteker som var spesifikke for plattformen. Implementeringen av Vektor API i Java tilbyr også en sømløs reservemekanisme som var komplisert i tidligere versjoner.

Standardiseringen av Vektor API gjør det mulig for klasser i JDK å bruke det. Java Arrays sin `mismatch()`-metoder kan endres til å kjøre i Java, noe som eliminerer behovet for å vedlikeholde og skrive flere plattformspesifikke implementasjoner i JVM.

Foreign Function & Memory API

En annen inkubatorfunksjon er Foreign Function & Memory API ( JEP 412). Det er en videreutvikling og sammenslåing av to andre inkubatormoduler fra Java 16, Foreign Linker API ( JEP 389) og Foreign-Memory API ( JEP 393). Begge gir tilgang til innebygd minne og kode ved bruk av statisk skrevet programmering i Java.

Mønstermatching for Switch

Den siste forhåndsfunksjonen i Java 17 er inkluderingen av mønstermatching for switch-uttrykk ( JEP 406). Denne språkfunksjonen utvider `switch`-uttrykk og setninger basert på type, i likhet med syntaksen som brukes for mønstermatching ( JEP 394), som ble standard i Java 16.

Tidligere, hvis man ønsket å utføre ulike handlinger basert på den dynamiske naturen til et objekt, måtte man konstruere en `if-else`-kjede med sjekker som:

String type(Object o) {
  if (o instanceof List) {
    return "En liste med elementer.";
  }
  else if (o instanceof Map) {
    return "Et Map! Det har nøkler og verdier.";
  }
  else if (o instanceof String) {
    return "Dette er en streng.";
  }
  else {
    return "Dette er noe annet.";
  }
}

Ved å kombinere `switch`-uttrykket med den nye mønstermatchingsfunksjonen, kan prosessen reduseres til noe lignende:

String type(Object o) {
  return switch (o) {
    case List l -> "En liste med elementer.";
    case Map m -> "Et Map! Det har nøkler og verdier.";
    case String s -> "Dette er en streng.";
    default -> "Dette er noe annet.";
  };
}

Som du kanskje har lagt merke til, deklareres en variabel samtidig som sjekken utføres. I likhet med andre variabler i mønstermatching, indikerer matchingen at objektet er type-sjekket og konvertert, og er tilgjengelig via variabelen innenfor sitt aktuelle område.

Denne forhåndsfunksjonen er enda et skritt mot mønstermatching. Det neste trinnet er å inkludere muligheten til å dekonstruere arrays og poster.

Bør du oppgradere til Java 17?

Ja, det er viktig å jevnlig oppgradere til den nyeste versjonen, men kanskje ikke umiddelbart ved lansering. Programvaren og bibliotekene du bruker kan ikke være oppdatert med Java 17 ennå, så du må kanskje vente en stund.

Hvis du bruker en eldre LTS-versjon av Java, som Java 8 eller Java 11, er det mange fordeler med språket og JVM som tilsier en oppgradering til Java 17. Siden dette er en langsiktig utgave, er det sannsynlig at produksjonsmiljøet ditt til slutt vil bli oppgradert til Java 17.

Hvis du starter et nytt prosjekt eller forbereder et eksisterende prosjekt for Java 17, vil det sannsynligvis være mest effektivt å bytte til Java 17 før eller siden, ettersom det reduserer kostnadene ved å flytte senere. Det gir også utviklerne muligheten til å benytte de nyeste funksjonene, i tillegg til forbedringene for drift.

Du kan dra nytte av de mange forbedringene som har skjedd de siste årene, inkludert bedre støtte for containere som kjører Java, samt nye implementeringer av søppeltømmere med lav latens.