Statiska klasser och inre klasser i Java

Kapslade klasser är klasser som deklareras som medlemmar i andra klasser eller omfattningar. Häckande klasser är ett sätt att bättre organisera din kod. Anta till exempel att du har en icke-kapslad klass (även känd som en toppnivåklass ) som lagrar objekt i en resizable matris, följt av en iteratorklass som returnerar varje objekt. I stället för att förorena toppnivåns klassutrymme, kan du förklara iteratorklassen som medlem i den resizable array-samlingsklassen. Detta fungerar eftersom de två är nära besläktade.

I Java kategoriseras kapslade klasser som antingen statiska medlemsklasser eller inre klasser . Inre klasser är icke-statiska medlemsklasser, lokala klasser eller anonyma klasser. I den här handledningen lär du dig hur du arbetar med statiska medlemsklasser och de tre typerna av inre klasser i din Java-kod.

Undvik minnesläckor i kapslade klasser

Se även Java-tipset som är associerat med den här självstudien, där du lär dig varför kapslade klasser är utsatta för minnesläckor.

Statiska klasser i Java

I min Java 101- handledning Klasser och objekt i Java lärde du dig att förklara statiska fält och statiska metoder som medlemmar i en klass. I klass- och objektinitiering i Java lärde du dig att förklara statiska initialiserare som medlemmar i en klass. Nu lär du dig att deklarera statiska klasser . Formellt kända som statiska medlemsklasser , det här är kapslade klasser som du förklarar på samma nivå som dessa andra statiska enheter med hjälp av staticnyckelordet. Här är ett exempel på en statisk klassdeklaration:

 class C { static int f; static void m() {} static { f = 2; } static class D { // members } } 

I det här exemplet introduceras toppklass Cmed statiskt fält f, statisk metod m(), en statisk initialisering och statisk medlemsklass D. Observera att du Där medlem i C. Det statiska fältet f, den statiska metoden m()och den statiska initialiseraren är också medlemmar i C. Eftersom alla dessa element tillhör klass Cär det känt som den omslutande klassen . Klass Där känd som den slutna klassen .

Inkapslings- och åtkomstregler

Även om den är innesluten kan en statisk medlemsklass inte komma åt den omslutande klassens instansfält och åberopa dess instansmetoder. Den kan dock få tillgång till den stängande klassens statiska fält och åberopa dess statiska metoder, även de medlemmar som deklareras private. För att demonstrera förklarar Listing 1 en EnclosingClassmed en kapslad SMClass.

Listning 1. Deklarera en statisk medlemsklass (EnclosingClass.java, version 1)

 class EnclosingClass { private static String s; private static void m1() { System.out.println(s); } static void m2() { SMClass.accessEnclosingClass(); } static class SMClass { static void accessEnclosingClass() { s = "Called from SMClass's accessEnclosingClass() method"; m1(); } void accessEnclosingClass2() { m2(); } } } 

Listning 1 förklarar en klass på högsta nivå EnclosingClassmed klassfält s, klassmetoder m1()och m2()och statisk medlemsklass SMClass. SMClassförklarar klassmetod accessEnclosingClass()och instansmetod accessEnclosingClass2(). Observera följande:

  • m2()s anrop av SMClass's accessEnclosingClass()metod kräver SMClass.prefix eftersom accessEnclosingClass()deklareras static.
  • accessEnclosingClass()kan tillgång EnclosingClasss sområde och ringa sin m1()metod, även om båda har deklarerats private.

Notering 2 visar källkoden till ett SMCDemoprogram klass som visar hur man åberopa SMClasss accessEnclosingClass()metod. Det visar också hur man kan instansiera SMClassoch åberopa sin accessEnclosingClass2()instansmetod.

Lista 2. Åberopa en statisk medlemsklasss metoder (SMCDemo.java)

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

Som visas i Listing 2, om du vill åberopa en toppklasss metod från en sluten klass, måste du prefixa den bifogade klassens namn med namnet på dess omslutande klass. På samma sätt, för att starta en sluten klass, måste du prefixa namnet på den klassen med namnet på den slutna klassen. Du kan sedan åberopa instansmetoden på normalt sätt.

Sammanställa listor 1 och 2 enligt följande:

 javac *.java 

När du kompilerar en omslutande klass som innehåller en statisk medlemsklass skapar kompilatorn en klassfil för den klassiska medlemsklassen vars namn består av dess omslutande klassnamn, ett dollartecken och den statiska medlemsklassens namn. I detta fall resulterar sammanställning i EnclosingClass$SMCClass.classoch EnclosingClass.class.

Kör applikationen enligt följande:

 java SMCDemo 

Följ följande utdata:

 Called from SMClass's accessEnclosingClass() method Called from SMClass's accessEnclosingClass() method 

Exempel: Statiska klasser och Java 2D

Java: s standardklassbibliotek är ett runtime-bibliotek med klassfiler som lagrar kompilerade klasser och andra referenstyper. Biblioteket innehåller många exempel på statiska medlemsklasser, varav några finns i Java 2D geometriska formklasser som finns i java.awt.geompaketet. (Du lär dig mer om paket i nästa Java 101- handledning.)

Den Ellipse2Dklass som finns i java.awt.geombeskriver en ellips, som definieras av en inramnings rektangel i termer av en (x, y) övre vänstra hörnet tillsammans med bredd- och höjd utsträckningar. Följande kodfragment visar att klassens arkitektur är baserad på Floatoch Doublestatiska medlemsklasser, som båda underklassar Ellipse2D:

 public abstract class Ellipse2D extends RectangularShape { public static class Float extends Ellipse2D implements Serializable { public float x, y, width, height; public Float() { } public Float(float x, float y, float w, float h) { setFrame(x, y, w, h); } public double getX() { return (double) x; } // additional instance methods } public static class Double extends Ellipse2D implements Serializable { public double x, y, width, height; public Double() { } public Double(double x, double y, double w, double h) { setFrame(x, y, w, h); } public double getX() { return x; } // additional instance methods } public boolean contains(double x, double y) { // ... } // additional instance methods shared by Float, Double, and other // Ellipse2D subclasses } 

Den Floatoch Doubleklasser sträcker Ellipse2Dger flyttals och dubbel precision floating-point Ellipse2Dimplementationer. Utvecklare använder för Floatatt minska minnesförbrukningen, särskilt för att du kan behöva tusentals eller fler av dessa objekt för att konstruera en enda 2D-scen. Vi använder Doublenär större noggrannhet krävs.

Du kan inte instansera den abstrakta Ellipse2Dklassen, men du kan instantiera antingen Floateller Double. Du kan också utvidga för Ellipse2Datt beskriva en anpassad form som är baserad på en ellips.

Som ett exempel, låt oss säga att du vill introducera en Circle2Dklass som inte finns i java.awt.geompaketet. Följande kodfragment visar hur du skulle skapa ett Ellipse2Dobjekt med en flytande punktimplementering:

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

Nästa kodfragment visar hur du skulle skapa ett Ellipse2Dobjekt med en dubbelprecisionsflytpunktimplementering:

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

Du kan nu åberopa någon av de metoder som deklareras i Floateller Doublegenom att åberopa metoden på den returnerade Ellipse2Dreferensen (t.ex. e2d.getX()). På samma sätt kan du åberopa någon av de metoder som är gemensamma för Floatoch Doubleoch som deklareras i Ellipse2D. Ett exempel är:

 e2d.contains(2.0, 3.0) 

Det kompletterar introduktionen till statiska medlemsklasser. Därefter tittar vi på inre klasser, som är icke-statiska medlemsklasser, lokala klasser eller anonyma klasser. Du lär dig hur du arbetar med alla tre inre klasstyperna.

ladda ner Skaffa koden Ladda ner källkoden för exempel i denna handledning. Skapad av Jeff Friesen för JavaWorld.

Inner classes, type 1: Non-static member classes

You've learned previously in the Java 101 series how to declare non-static (instance) fields, methods, and constructors as members of a class. You can also declare non-static member classes, which are nested non-static classes that you declare at the same level as instance fields, methods, and constructors. Consider this example:

 class C { int f; void m() {} C() { f = 2; } class D { // members } } 

Here, we introduce top-level class C with instance field f, instance method m(), a constructor, and non-static member class D. All of these entities are members of class C, which encloses them. However, unlike in the previous example, these instance entities are associated with instances ofC and not with the C class itself.

Each instance of the non-static member class is implicitly associated with an instance of its enclosing class. The non-static member class's instance methods can call the enclosing class's instance methods and access its instance fields. To demonstrate this access, Listing 3 declares an EnclosingClass with a nested NSMClass.

Listing 3. Declare an enclosing class with a nested non-static member class (EnclosingClass.java, version 2)

 class EnclosingClass { private String s; private void m() { System.out.println(s); } class NSMClass { void accessEnclosingClass() { s = "Called from NSMClass's accessEnclosingClass() method"; m(); } } } 

Listing 3 declares a top-level class named EnclosingClass with instance field s, instance method m(), and non-static member class NSMClass. Furthermore, NSMClass declares instance method accessEnclosingClass().

Because accessEnclosingClass() is non-static, NSMClass must be instantiated before this method can be called. This instantiation must take place via an instance of EnclosingClass, as shown in Listing 4.

Listing 4. NSMCDemo.java

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

Listing 4's main() method first instantiates EnclosingClass and saves its reference in local variable ec. The main() method then uses the EnclosingClass reference as a prefix to the new operator, in order to instantiate NSMClass. The NSMClass reference is then used to call accessEnclosingClass().

Should I use 'new' with a reference to the enclosing class?

Prefixing new with a reference to the enclosing class is rare. Instead, you will typically call an enclosed class's constructor from within a constructor or an instance method of its enclosing class.

Compile Listings 3 and 4 as follows:

 javac *.java 

When you compile an enclosing class that contains a non-static member class, the compiler creates a class file for the non-static member class whose name consists of its enclosing class's name, a dollar-sign character, and the non-static member class's name. In this case, compiling results in EnclosingClass$NSMCClass.class and EnclosingClass.class.

Run the application as follows:

 java NSMCDemo 

You should observe the following output:

 Called from NSMClass's accessEnclosingClass() method 

When (and how) to qualify 'this'

An enclosed class's code can obtain a reference to its enclosing-class instance by qualifying reserved word this with the enclosing class's name and the member access operator (.). For example, if code within accessEnclosingClass() needed to obtain a reference to its EnclosingClass instance, it would specify EnclosingClass.this. Because the compiler generates code to accomplish this task, specifying this prefix is rare.

Example: Non-static member classes in HashMap

The standard class library includes non-static member classes as well as static member classes. For this example, we'll look at the HashMap class, which is part of the Java Collections Framework in the java.util package. HashMap, which describes a hash table-based implementation of a map, includes several non-static member classes.

For example, the KeySet non-static member class describes a set-based view of the keys contained in the map. The following code fragment relates the enclosed KeySet class to its HashMap enclosing class:

 public class HashMap extends AbstractMap implements Map, Cloneable, Serializable { // various members final class KeySet extends AbstractSet { // various members } // various members } 

The and syntaxes are examples of generics, a suite of related language features that help the compiler enforce type safety. I'll introduce generics in an upcoming Java 101 tutorial. For now, you just need to know that these syntaxes help the compiler enforce the type of key objects that can be stored in the map and in the keyset, and the type of value objects that can be stored in the map.

HashMaptillhandahåller en keySet()metod som startar KeySetvid behov och returnerar den här instansen eller en cachad instans. Här är den fullständiga metoden: