Statička nastava i unutarnja nastava u Javi

Ugnježdene klase su klase koje su deklarirane kao članovi drugih klasa ili opsega. Umetanje klasa jedan je od načina za bolju organizaciju koda. Na primjer, recimo da imate ugniježđenu klasu (poznatu i kao klasa najviše razine ) koja pohranjuje objekte u polju s promjenjivom veličinom, nakon čega slijedi klasa iteratora koja vraća svaki objekt. Umjesto da zagađujete prostor imena klase najviše razine, klasu iteratora možete proglasiti članom klase kolekcije polja s promjenjivom veličinom. To djeluje jer su to dvoje usko povezani.

U Javi su ugniježđene klase kategorizirane ili kao statičke klase članova ili kao unutarnje klase . Unutarnje klase su nestatične klase članova, lokalne klase ili anonimne klase. U ovom uputstvu naučit ćete kako raditi sa statičkim klasama članova i tri vrste unutarnjih klasa u vašem Java kodu.

Izbjegavajte curenje memorije u ugniježđenim klasama

Također pogledajte Java savjet povezan s ovim uputstvom, gdje ćete naučiti zašto su ugniježđene klase ranjive na curenje memorije.

Statička nastava u Javi

U mom Java 101 tutorialu Klase i objekti na Javi naučili ste kako deklarirati statička polja i statičke metode kao članove klase. U inicijalizaciji klase i objekta u Javi naučili ste kako deklarirati statičke inicijalizatore kao članove klase. Sada ćete naučiti kako se deklariraju statičke klase . Formalno poznate kao statičke klase članova , to su ugniježđene klase koje deklarirate na istoj razini kao i ove ostale statičke cjeline, koristeći staticključnu riječ. Evo primjera statičke deklaracije klase člana:

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

Ovaj primjer uvodi klasu najviše razine Csa statičkim poljem f, statičkom metodom m(), statičkim pokretačem i statičnom klasom člana D. Primijetite da Dje član C. Statičko polje f, statička metoda m()i statički inicijalizator također su članovi C. Budući da svi ti elementi pripadaju klasi C, poznata je kao klasa koja obuhvaća . Klasa Dje poznata kao zatvorena klasa .

Pravila o ograđivanju i pristupu

Iako je zatvorena, klasa statičkog člana ne može pristupiti poljima instance instance koja uključuje i poziva svoje metode instance. Međutim, može pristupiti statičkim poljima klase koja se zatvara i pozvati svoje statičke metode, čak i one članove koji su deklarirani private. Za demonstraciju, popis 1 deklarira EnclosingClasss ugniježđenim SMClass.

Popis 1. Deklaracija statičke klase člana (EnclosingClass.java, verzija 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(); } } } 

Popis 1 deklarira klasu najviše razine imenovanu EnclosingClasss poljem sklase, metodama klase m1()i m2()i statičnom klasom člana SMClass. SMClassdeklarira metodu klase i metodu accessEnclosingClass()instance accessEnclosingClass2(). Imajte na umu sljedeće:

  • m2()„S zazivanje SMClass” s accessEnclosingClass()metodom zahtijeva SMClass.prefiks jer accessEnclosingClass()je proglašen static.
  • accessEnclosingClass()je u mogućnosti to pristup EnclosingClass„s sterena i poziva svoje m1()metode, iako oboje su proglašeni private.

Oglas 2 prikazuje izvorni kod na SMCDemoprijavu razredu koji pokazuje kako da se pozove SMClass„s accessEnclosingClass()metodu. Također pokazuje kako se instancira SMClassi poziva njegova accessEnclosingClass2()metoda instance.

Popis 2. Pozivanje metoda klase statičnog člana (SMCDemo.java)

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

Kao što je prikazano u Popisu 2, ako želite pozvati metodu klase najviše razine unutar zatvorene klase, morate priložiti ime zatvorene klase ispred imena njene klase. Isto tako, da biste napravili instancu zatvorene klase, morate toj klasi dodati prefiks s imenom njene klase. Tada možete pozvati metodu instance na uobičajeni način.

Sastavite popise 1 i 2 kako slijedi:

 javac *.java 

Kada kompajlirate klasu koja obuhvata, koja sadrži klasu statičkog člana, kompajler kreira datoteku klase za klasu statičkog člana čije se ime sastoji od imena klase koja ga obuhvata, znaka dolara i imena klase statičnog člana. U ovom slučaju, sastavljanje rezultira u EnclosingClass$SMCClass.classi EnclosingClass.class.

Pokrenite aplikaciju na sljedeći način:

 java SMCDemo 

Trebali biste promatrati sljedeći rezultat:

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

Primjer: Statičke klase i Java 2D

Java standardna knjižnica klasa je runtime knjižnica datoteka klasa, koja pohranjuje prevedene klase i druge referentne tipove. Biblioteka uključuje brojne primjere statičnih klasa članova, od kojih se neke nalaze u Java 2D razredima geometrijskih oblika koji se nalaze u java.awt.geompaketu. (O paketima ćete saznati u sljedećem uputstvu za Java 101. )

Ellipse2DKlasa naći u java.awt.geomopisuje elipsu, koji je definiran okvirni pravokutnik u smislu jednog (x, y) gornji lijevi kut uz širine i visine opsezima. Sljedeći fragment koda pokazuje da se arhitektura ove klase temelji na Floati Doublestatičkim klasama članova, koje su podrazred 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 } 

FloatI Doublenastava produžiti Ellipse2D, pružajući plutajuće točke i dvostruka preciznost s pomičnim zarezom Ellipse2Dimplementacije. Programeri koriste Floatza smanjenje potrošnje memorije, osobito zato što će vam trebati tisuće ili više ovih objekata za izradu jedne 2D scene. Koristimo Doublekada je potrebna veća točnost.

Apstraktnu Ellipse2Dklasu ne možete instancirati, ali možete instantirati bilo Floatili Double. Također možete proširiti Ellipse2Dopisivanje prilagođenog oblika koji se temelji na elipsi.

Kao primjer, recimo da želite predstaviti Circle2Dklasu koja nije prisutna u java.awt.geompaketu. Sljedeći fragment koda pokazuje kako biste stvorili Ellipse2Dobjekt s implementacijom s pomičnom zarezom:

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

Sljedeći fragment koda pokazuje kako biste stvorili Ellipse2Dobjekt s implementacijom dvostruke preciznosti s pomičnom zarezom:

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

Sada možete pozvati bilo koju metodu deklariranu u Floatili Doublepozivom metode na vraćenoj Ellipse2Dreferenci (npr. e2d.getX()). Na isti način, mogli pozvati na bilo koji od načina koji su zajednički za Floatte Double, i koji su proglašeni u Ellipse2D. Primjer je:

 e2d.contains(2.0, 3.0) 

To završava uvod u statičke klase članova. Dalje ćemo se osvrnuti na unutarnje klase, koje su nestatične klase članova, lokalne klase ili anonimne klase. Naučit ćete kako raditi sa sve tri vrste unutarnjeg razreda.

preuzimanje Preuzmite kod Preuzmite izvorni kod za primjere u ovom vodiču. Stvorio Jeff Friesen za 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.

HashMappruža keySet()metodu koja instancira KeySetkada je to potrebno i vraća ovu instancu ili predmemoriranu instancu. Evo kompletne metode: