Statische Klassen und innere Klassen in Java

Verschachtelte Klassen sind Klassen, die als Mitglieder anderer Klassen oder Bereiche deklariert sind. Das Verschachteln von Klassen ist eine Möglichkeit, Ihren Code besser zu organisieren. Angenommen , Sie haben eine nicht verschachtelte Klasse (auch als Klasse der obersten Ebene bezeichnet ), die Objekte in einem anpassbaren Array speichert, gefolgt von einer Iteratorklasse, die jedes Objekt zurückgibt. Anstatt den Namespace der Klasse der obersten Ebene zu verschmutzen, können Sie die Iteratorklasse als Mitglied der Sammelklasse für skalierbare Arrays deklarieren. Dies funktioniert, weil die beiden eng miteinander verbunden sind.

In Java werden verschachtelte Klassen entweder als statische Elementklassen oder als innere Klassen kategorisiert . Innere Klassen sind nicht statische Mitgliedsklassen, lokale Klassen oder anonyme Klassen. In diesem Tutorial erfahren Sie, wie Sie mit statischen Elementklassen und den drei Arten innerer Klassen in Ihrem Java-Code arbeiten.

Vermeiden Sie Speicherverluste in verschachtelten Klassen

Lesen Sie auch den Java-Tipp zu diesem Lernprogramm, in dem Sie erfahren, warum verschachtelte Klassen anfällig für Speicherverluste sind.

Statische Klassen in Java

In meinem Java 101- Tutorial Klassen und Objekte in Java haben Sie gelernt, wie Sie statische Felder und statische Methoden als Mitglieder einer Klasse deklarieren. In der Klassen- und Objektinitialisierung in Java haben Sie gelernt, wie statische Initialisierer als Mitglieder einer Klasse deklariert werden. Jetzt lernen Sie, wie Sie statische Klassen deklarieren . Formal als statische Elementklassen bezeichnet , sind dies verschachtelte Klassen, die Sie mit dem staticSchlüsselwort auf derselben Ebene wie diese anderen statischen Entitäten deklarieren. Hier ist ein Beispiel für eine statische Deklaration von Mitgliedsklassen:

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

In diesem Beispiel wird eine Klasse der obersten Ebene Cmit einem statischen Feld f, einer statischen Methode m(), einem statischen Initialisierer und einer statischen Elementklasse vorgestellt D. Beachten Sie, dass Dein Mitglied von ist C. Das statische Feld f, die statische Methode m()und der statische Initialisierer sind ebenfalls Mitglieder von C. Da alle diese Elemente zur Klasse gehören C, wird sie als einschließende Klasse bezeichnet . Klasse Dist als beiliegende Klasse bekannt .

Gehäuse- und Zugriffsregeln

Obwohl es eingeschlossen ist, kann eine statische Mitgliedsklasse nicht auf die Instanzfelder der einschließenden Klasse zugreifen und ihre Instanzmethoden aufrufen. Es kann jedoch auf die statischen Felder der einschließenden Klasse zugreifen und ihre statischen Methoden aufrufen, selbst die deklarierten Mitglieder private. Zur Veranschaulichung deklariert Listing 1 ein EnclosingClassmit einem verschachtelten SMClass.

Listing 1. Deklarieren einer statischen Memberklasse (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(); } } } 

Listing 1 deklariert eine Klasse der obersten Ebene mit dem Namen EnclosingClassKlassenfeld s, Klassenmethoden m1()und m2()und statische Elementklasse SMClass. SMClassdeklariert Klassenmethode accessEnclosingClass()und Instanzmethode accessEnclosingClass2(). Beachten Sie das Folgende:

  • m2()Für den Aufruf der Methode von SMClass'ist accessEnclosingClass()das SMClass.Präfix erforderlich, da accessEnclosingClass()deklariert ist static.
  • accessEnclosingClass()kann auf EnclosingClassdas sFeld zugreifen und seine m1()Methode aufrufen , obwohl beide deklariert wurden private.

Listing 2 zeigt die Source - Code zu einer SMCDemoAnwendungsklasse , die zeigt , wie aufrufen SMClass‚s - accessEnclosingClass()Methode. Außerdem wird gezeigt, wie SMClassdie accessEnclosingClass2()Instanzmethode instanziiert und aufgerufen wird.

Listing 2. Aufrufen der Methoden einer statischen Mitgliedsklasse (SMCDemo.java)

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

Wie in Listing 2 gezeigt, müssen Sie dem Namen der eingeschlossenen Klasse den Namen der umschließenden Klasse voranstellen, wenn Sie die Methode einer Klasse der obersten Ebene innerhalb einer eingeschlossenen Klasse aufrufen möchten. Um eine eingeschlossene Klasse zu instanziieren, müssen Sie dem Namen dieser Klasse den Namen ihrer einschließenden Klasse voranstellen. Sie können die Instanzmethode dann auf normale Weise aufrufen.

Stellen Sie die Listen 1 und 2 wie folgt zusammen:

 javac *.java 

Wenn Sie eine einschließende Klasse kompilieren, die eine statische Elementklasse enthält, erstellt der Compiler eine Klassendatei für die statische Elementklasse, deren Name aus dem Namen der einschließenden Klasse, einem Dollarzeichen und dem Namen der statischen Elementklasse besteht. In diesem Fall führt das Kompilieren zu EnclosingClass$SMCClass.classund EnclosingClass.class.

Führen Sie die Anwendung wie folgt aus:

 java SMCDemo 

Sie sollten die folgende Ausgabe beachten:

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

Beispiel: Statische Klassen und Java 2D

Die Standardklassenbibliothek von Java ist eine Laufzeitbibliothek von Klassendateien, in der kompilierte Klassen und andere Referenztypen gespeichert sind. Die Bibliothek enthält zahlreiche Beispiele für statische Elementklassen, von denen einige in den im java.awt.geomPaket enthaltenen geometrischen Java 2D-Formklassen enthalten sind. (Weitere Informationen zu Paketen finden Sie im nächsten Java 101- Lernprogramm.)

Die in Ellipse2Dgefundene Klasse java.awt.geombeschreibt eine Ellipse, die durch ein Rahmenrechteck in Form einer (x, y) oberen linken Ecke zusammen mit den Ausmaßen Breite und Höhe definiert wird. Das folgende Codefragment zeigt , dass diese Architektur der Klasse basiert auf Floatund Doublestatischen Elementklassen, die beide Unterklassen 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 } 

Die Klassen Floatund Doubleerweitern sich Ellipse2Dund bieten Gleitkommaimplementierungen mit Gleitkomma- und doppelter Genauigkeit Ellipse2D. Entwickler Floatreduzieren den Speicherverbrauch, insbesondere weil Sie möglicherweise Tausende oder mehr dieser Objekte benötigen, um eine einzelne 2D-Szene zu erstellen. Wir verwenden, Doublewenn eine größere Genauigkeit erforderlich ist.

Sie können die abstrakte Ellipse2DKlasse nicht instanziieren , aber Sie können entweder Floatoder instanziieren Double. Sie können auch erweitern Ellipse2D, um eine benutzerdefinierte Form zu beschreiben, die auf einer Ellipse basiert.

Angenommen, Sie möchten eine Circle2DKlasse einführen , die nicht im java.awt.geomPaket enthalten ist. Das folgende Codefragment zeigt, wie Sie ein Ellipse2DObjekt mit einer Gleitkommaimplementierung erstellen würden :

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

Das nächste Codefragment zeigt, wie Sie ein Ellipse2DObjekt mit einer Gleitkommaimplementierung mit doppelter Genauigkeit erstellen würden :

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

Sie können jetzt jede der in Floatoder Doubledurch Aufrufen der Methode für die zurückgegebene Ellipse2DReferenz deklarierten Methoden aufrufen (z e2d.getX(). B. ). Auf die gleiche Weise können Sie alle Methoden aufrufen, die für Floatund gemeinsam Doublesind und in denen deklariert sind Ellipse2D. Ein Beispiel ist:

 e2d.contains(2.0, 3.0) 

Damit ist die Einführung in statische Elementklassen abgeschlossen. Als nächstes betrachten wir innere Klassen, die nicht statische Mitgliedsklassen, lokale Klassen oder anonyme Klassen sind. Sie lernen, wie Sie mit allen drei inneren Klassentypen arbeiten.

download Code herunterladen Laden Sie den Quellcode herunter, um Beispiele in diesem Tutorial zu erhalten. Erstellt von 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.

HashMapBietet eine keySet()Methode, die KeySetbei Bedarf instanziiert und diese Instanz oder eine zwischengespeicherte Instanz zurückgibt. Hier ist die vollständige Methode: