Programmering

Statiske klasser og indre klasser i Java

Indlejrede klasser er klasser, der erklæres som medlemmer af andre klasser eller anvendelsesområder. Nestende klasser er en måde at bedre organisere din kode på. Sig for eksempel, at du har en klasse, der ikke er indlejret (også kendt som en topklasse), der gemmer objekter i en størrelse, der kan ændres, efterfulgt af en iteratorklasse, der returnerer hvert objekt. I stedet for at forurene det øverste klasses navneområde, kan du erklære iterator-klassen som medlem af den resizable array-samlingsklasse. Dette fungerer, fordi de to er tæt beslægtede.

I Java er indlejrede klasser kategoriseret som enten statiske medlemsklasser eller indre klasser. Indre klasser er ikke-statiske medlemsklasser, lokale klasser eller anonyme klasser. I denne vejledning lærer du at arbejde med statiske medlemsklasser og de tre typer indre klasser i din Java-kode.

Undgå hukommelseslækage i indlejrede klasser

Se også Java-tip, der er knyttet til denne tutorial, hvor du lærer, hvorfor indlejrede klasser er sårbare over for hukommelseslækage.

Statiske klasser i Java

I min Java 101 tutorial Klasser og objekter i Java, du lærte at erklære statiske felter og statiske metoder som medlemmer af en klasse. I klasse- og objektinitialisering i Java lærte du, hvordan man erklærer statiske initialiserere som medlemmer af en klasse. Nu lærer du at erklære statiske klasser. Formelt kendt som statiske medlemsklasser, dette er indlejrede klasser, som du erklærer på samme niveau som disse andre statiske enheder ved hjælp af statisk nøgleord. Her er et eksempel på en statisk erklæring om medlemsklasse:

 klasse C {statisk int f; statisk tomrum m () {} statisk {f = 2; } statisk klasse D {// medlemmer}} 

Dette eksempel introducerer topklasse C med statiske felt f, statisk metode m (), en statisk initialisering og en statisk medlemsklasse D. Læg mærke til det D er medlem af C. Det statiske felt f, statisk metode m ()og den statiske initialisering er også medlemmer af C. Da alle disse elementer hører til klassen C, det er kendt som omsluttende klasse. Klasse D er kendt som lukket klasse.

Indhegnings- og adgangsregler

Selv om den er lukket, kan en statisk medlemsklasse ikke få adgang til den omgivende klasses instansfelter og påberåbe sig dens instansmetoder. Det kan dog få adgang til den omgivende klasses statiske felter og påberåbe sig dens statiske metoder, selv de medlemmer, der er erklæret privat. For at demonstrere erklærer Listing 1 en Omslutningsklasse med en indlejret SMC-klasse.

Fortegnelse 1. Erklæring om en statisk medlemsklasse (EnclosingClass.java, version 1)

 klasse EnclosingClass {privat statisk streng s; privat statisk tomrum m1 () {System.out.println (s); } statisk ugyldigt m2 () {SMClass.accessEnclosingClass (); } static class SMClass {static void accessEnclosingClass () {s = "Opkaldt fra SMClasss accessEnclosingClass () -metode"; m1 (); } ugyldig adgangEnclosingClass2 () {m2 (); }}} 

Listing 1 erklærer en klasse på øverste niveau ved navn Omslutningsklasse med klassefelt s, klassemetoder m1 () og m2 (), og statisk medlemsklasse SMClass. SMC-klasse erklærer klassemetode accessEnclosingClass () og instansmetode accessEnclosingClass2 (). Bemærk følgende:

  • m2 ()påkaldelse af SMC-klasse's accessEnclosingClass () metoden kræver SMC-klasse. præfiks fordi accessEnclosingClass () erklæres statisk.
  • accessEnclosingClass () er i stand til at få adgang Omslutningsklasse's s felt og kalde dens m1 () metode, selvom begge er blevet erklæret privat.

Liste 2 viser kildekoden til en SMCDemo applikationsklasse, der viser, hvordan man påberåber sig SMC-klasse's accessEnclosingClass () metode. Det viser også, hvordan man kan instantere SMC-klasse og påberåbe sig dens accessEnclosingClass2 () eksempel metode.

Fortegnelse 2. Påkaldelse af en statisk medlems klasses metoder (SMCDemo.java)

 public class SMCDemo {public static void main (String [] args) {EnclosingClass.SMClass.accessEnclosingClass (); EnclosingClass.SMClass smc = ny EnclosingClass.SMClass (); smc.accessEnclosingClass2 (); }} 

Som vist i liste 2, hvis du vil påberåbe sig en topklasses metode fra en lukket klasse, skal du forudse den lukkede klasses navn med navnet på den lukkende klasse. For at starte en lukket klasse skal du ligeledes foran navnet på den klasse med navnet på den lukkede klasse. Du kan derefter påberåbe sig instansmetoden på normal måde.

Kompilér lister 1 og 2 som følger:

 javac * .java 

Når du kompilerer en omsluttende klasse, der indeholder en statisk medlemsklasse, opretter kompilatoren en klassefil til den statiske medlemsklasse, hvis navn består af dens vedlagte klassenavn, et dollartegnetegn og det statiske medlemsklassens navn. I dette tilfælde resulterer kompilering i EnclosingClass $ SMCClass.class og EnclosingClass.class.

Kør applikationen som følger:

 java SMCDemo 

Du skal overholde følgende output:

 Opkaldt fra SMClass's accessEnclosingClass () metode Opkaldt fra SMClass's accessEnclosingClass () metode 

Eksempel: Statiske klasser og Java 2D

Java'er standard klassebibliotek er et runtime-bibliotek med klassefiler, der gemmer kompilerede klasser og andre referencetyper. Biblioteket indeholder adskillige eksempler på statiske medlemsklasser, hvoraf nogle findes i Java 2D geometriske formklasser placeret i java.awt.geom pakke. (Du lærer om pakker i det næste Java 101 vejledning.)

Det Ellipse2D klasse fundet i java.awt.geom beskriver en ellipse, der er defineret af et indramningsrektangel i form af et (x, y) øverste venstre hjørne sammen med bredde- og højdeomfang. Det følgende kodefragment viser, at denne klasses arkitektur er baseret på Flyde og Dobbelt statiske medlemsklasser, som begge underklasser Ellipse2D:

 offentlig abstrakt klasse Ellipse2D udvider RectangularShape {offentlig statisk klasse Float udvider Ellipse2D implementerer Serialiserbar {offentlig float x, y, bredde, højde; public Float () {} public Float (float x, float y, float w, float h) {setFrame (x, y, w, h); } offentlig dobbelt getX () {retur (dobbelt) x; } // yderligere instansmetoder} offentlig statisk klasse Dobbelt udvider Ellipse2D implementerer Serialiserbar {offentlig dobbelt x, y, bredde, højde; offentlig dobbelt () {} offentlig dobbelt (dobbelt x, dobbelt y, dobbelt w, dobbelt h) {setFrame (x, y, w, h); } offentlig dobbelt getX () {return x; } // yderligere instansmetoder} offentlig boolsk indeholder (dobbelt x, dobbelt y) {// ...} // yderligere instansmetoder delt af Float, Double og andre // Ellipse2D-underklasser} 

Det Flyde og Dobbelt klasser udvides Ellipse2D, der giver flydende punkt og dobbelt præcision flydende punkt Ellipse2D implementeringer. Udviklere bruger Flyde for at reducere hukommelsesforbruget, især fordi du muligvis har brug for tusinder eller flere af disse objekter for at konstruere en enkelt 2D-scene. Vi bruger Dobbelt når der kræves større nøjagtighed.

Du kan ikke instantiere det abstrakte Ellipse2D klasse, men du kan instantiere enten Flyde eller Dobbelt. Du kan også udvide Ellipse2D til at beskrive en brugerdefineret form, der er baseret på en ellipse.

Lad os som et eksempel sige, at du vil introducere en Circle2D klasse, som ikke er til stede i java.awt.geom pakke. Følgende kodefragment viser, hvordan du opretter et Ellipse2D objekt med en floating-point implementering:

 Ellipse2D e2d = ny Ellipse2D.Float (10.0f, 10.0f, 20.0f, 30.0f); 

Det næste kodefragment viser, hvordan du opretter et Ellipse2D objekt med en dobbeltpræcisions implementering af flydende punkt:

 Ellipse2D e2d = ny Ellipse2D.Double (10.0, 10.0, 20.0, 30.0); 

Du kan nu påberåbe sig en af ​​de metoder, der er angivet i Flyde eller Dobbelt ved at påkalde metoden på den returnerede Ellipse2D reference (f.eks. e2d.getX ()). På samme måde kan du påberåbe dig hvilken som helst af de metoder, der er fælles for Flyde og Dobbelt, og som er erklæret i Ellipse2D. Et eksempel er:

 e2d.contains (2.0, 3.0) 

Det fuldender introduktionen til statiske medlemsklasser. Dernæst ser vi på indre klasser, som er ikke-statiske medlemsklasser, lokale klasser eller anonyme klasser. Du lærer at arbejde med alle tre typer af indre klasser.

download Hent koden Download kildekoden for eksempler i denne vejledning. Oprettet af Jeff Friesen til JavaWorld.

Indre klasser, type 1: Ikke-statiske medlemsklasser

Du har lært tidligere i Java 101 serie, hvordan man erklærer ikke-statiske (instans) felter, metoder og konstruktører som medlemmer af en klasse. Du kan også erklære ikke-statiske medlemsklasser, som er indlejrede ikke-statiske klasser, som du erklærer på samme niveau som instansfelter, metoder og konstruktører. Overvej dette eksempel:

 klasse C {int f; ugyldigt m () {} C () {f = 2; } klasse D {// medlemmer}} 

Her introducerer vi topklasse C med instansfelt f, eksempel metode m (), en konstruktør og ikke-statisk medlemsklasse D. Alle disse enheder er medlemmer af klassen C, som omslutter dem. Men i modsætning til i det foregående eksempel er disse instanssenheder tilknyttet tilfælde afC og ikke med C klasse selv.

Hver forekomst af den ikke-statiske medlemsklasse er implicit forbundet med en forekomst af dens omsluttende klasse. Den ikke-statiske medlemsklasse's instansmetoder kan kalde den omgivende klasses instansmetoder og få adgang til dens instansfelter. For at demonstrere denne adgang erklærer Listing 3 en Omslutningsklasse med en indlejret NSM-klasse.

Fortegnelse 3. Erklær en omsluttende klasse med en indlejret ikke-statisk medlemsklasse (EnclosingClass.java, version 2)

 klasse EnclosingClass {private String s; privat tomrum m () {System.out.println (s); } klasse NSMClass {void accessEnclosingClass () {s = "Opkaldt fra NSMClass's accessEnclosingClass () -metode"; m (); }}} 

Listing 3 erklærer en topklasse navngivet Omslutningsklasse med instansfelt s, eksempel metode m ()og ikke-statisk medlemsklasse NSM-klasse. Desuden, NSMClass erklærer instansmetode accessEnclosingClass ().

Fordi accessEnclosingClass () er ikke-statisk, NSM-klasse skal instantieres, før denne metode kan kaldes. Denne instantiering skal finde sted via en forekomst af Omslutningsklasse, som vist i liste 4.

Notering 4. NSMCDemo.java

 public class NSMCDemo {public static void main (String [] args) {EnclosingClass ec = new EnclosingClass (); ec.new NSMClass (). accessEnclosingClass (); }} 

Liste 4's hoved () metoden instantieres først Omslutningsklasse og gemmer sin reference i lokal variabel ec. Det hoved () metoden bruger derefter Omslutningsklasse henvisning som et præfiks til ny operatør for at instantiere NSM-klasse. Det NSM-klasse reference bruges derefter til at ringe accessEnclosingClass ().

Skal jeg bruge 'nyt' med en henvisning til den vedlagte klasse?

Præfiksering ny med en henvisning til den vedlagte klasse er sjælden. I stedet kalder du typisk en lukket klasses konstruktør fra en konstruktør eller en instansmetode i dens lukkende klasse.

Kompilér liste 3 og 4 som følger:

 javac * .java 

Når du kompilerer en omsluttende klasse, der indeholder en ikke-statisk medlemsklasse, opretter kompilatoren en klassefil til den ikke-statiske medlemsklasse, hvis navn består af dens vedlagte klassenavn, et dollartegnetegn og den ikke-statiske medlemsklasse navn. I dette tilfælde resulterer kompilering i EnclosingClass $ NSMCClass.class og EnclosingClass.class.

Kør applikationen som følger:

 java NSMCDemo 

Du skal overholde følgende output:

 Kaldes fra NSMClass's accessEnclosingClass () -metode 

Hvornår (og hvordan) at kvalificere 'dette'

En lukket klasses kode kan få en henvisning til dens lukkende klasse-forekomst ved at kvalificere det reserverede ord det her med den vedlagte klasses navn og medlemsadgangsoperatøren (.). For eksempel hvis kode inden for accessEnclosingClass () behov for at få en henvisning til dens Omslutningsklasse eksempel, ville det specificere EnclosingClass.this. Da compileren genererer kode for at udføre denne opgave, er det sjældent at specificere dette præfiks.

Eksempel: Ikke-statiske medlemsklasser i HashMap

Standardklassebiblioteket inkluderer ikke-statiske medlemsklasser såvel som statiske medlemsklasser. I dette eksempel ser vi på HashMap klasse, som er en del af Java Collections Framework i java.util pakke. HashMap, der beskriver en hash-tabelbaseret implementering af et kort, inkluderer flere ikke-statiske medlemsklasser.

F.eks Nøglesæt ikke-statisk medlemsklasse beskriver et sæt-baseret udsigt af nøglerne på kortet. Det følgende kodefragment vedrører det vedlagte Nøglesæt klasse til sin HashMap omsluttende klasse:

 offentlig klasse HashMap udvider AbstractMap implementerer Map, Cloneable, Serializable {// forskellige medlemmer endelig klasse KeySet udvider AbstractSet {// forskellige medlemmer} // forskellige medlemmer} 

Det og syntakser er eksempler på generiske lægemidler, en række relaterede sprogfunktioner, der hjælper kompilatoren med at håndhæve typesikkerhed. Jeg introducerer generiske lægemidler i en kommende Java 101 vejledning. For nu skal du bare vide, at disse syntakser hjælper kompilatoren med at håndhæve typen af ​​nøgleobjekter, der kan gemmes på kortet og i nøglesættet, og typen af ​​værdiobjekter, der kan gemmes på kortet.

HashMap giver en keySet () metode, der instantierer Nøglesæt når det er nødvendigt, og returnerer denne forekomst eller en cachelagret forekomst. Her er den komplette metode: