שיעורים סטטיים ושיעורים פנימיים בג'אווה

כיתות מקוננות הן כיתות המוצהרות כחברות בכיתות או בהיקפים אחרים. שיעורי קינון היא אחת הדרכים לארגן טוב יותר את הקוד שלך. לדוגמא, נניח שיש לך מחלקה לא מקוננת (המכונה גם מחלקה ברמה העליונה ) המאחסנת אובייקטים במערך הניתן לשינוי גודל, ואחריה מחלקת איטרטר המחזירה כל אובייקט. במקום לזהם את מרחב השמות של המחלקה העליונה, אתה יכול להכריז על מחלקת האיטרטור כחבר במחלקת אוסף המערכים הניתנת לשינוי. זה עובד מכיוון שהשניים קשורים זה לזה.

ב- Java, מחלקות מקוננות מסווגות כשיעורי חברים סטטיים או כיתות פנימיות . השיעורים הפנימיים הם שיעורי חברים שאינם סטטיים, שיעורים מקומיים או שיעורים אנונימיים. במדריך זה תלמד כיצד לעבוד עם שיעורי חברים סטטיים ושלושת סוגי המחלקות הפנימיות בקוד ה- Java שלך.

הימנע מדליפות זיכרון בשיעורים מקוננים

ראה גם את טיפ Java המשויך להדרכה זו, שם תלמד מדוע שיעורים מקוננים פגיעים לדליפות זיכרון.

שיעורים סטטיים בג'אווה

במדריך Java 101 שלי שיעורים ואובייקטים ב- Java למדת כיצד להכריז על שדות סטטיים ושיטות סטטיות כחברים בכיתה. באתחול כיתות ואובייקטים ב- Java למדת כיצד להכריז על אתחול סטטי כחברים בכיתה. כעת תלמד כיצד להכריז על שיעורים סטטיים . ידועים רשמית כשיעורי חבר סטטיים , אלה הם מחלקות מקוננות שאתה מצהיר באותה רמה כמו ישויות סטטיות אחרות אלה, באמצעות staticמילת המפתח. הנה דוגמה להצהרה על כיתת חברים סטטית:

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

דוגמה זו מציגה מחלקה ברמה העליונה Cעם שדה fסטטי m(), שיטה סטטית, אתחול סטטי ומחלקת חברים סטטית D. שים לב Dשהוא חבר ב- C. השדה הסטטי f, השיטה m()הסטטית והמתח סטטי הם גם חברים C. מכיוון שכל האלמנטים הללו שייכים למעמד C, זה ידוע כמעמד הסוגר . הכיתה Dידועה ככיתה הסגורה .

כללי סגירה וגישה

למרות שהוא סגור, מחלקת חברים סטטית אינה יכולה לגשת לשדות המופע של המחלקה הסוגרת ולהפעיל את שיטות המופע שלה. עם זאת, היא יכולה לגשת לשדות הסטטיים של הכיתה הסוגרת ולהפעיל את השיטות הסטטיות שלה, אפילו לחברים המוצהרים private. כדי להדגים, רישום 1 מכריז על EnclosingClassעם מקונן SMClass.

רישום 1. הצהרת כיתת חברים סטטית (EnclosingClass.java, גרסה 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 מצהיר בכיתה ברמה עליונה בשם EnclosingClassעם שדה בכיתה s, שיטות בכיתה m1()ו m2(), ומעמד חבר סטטי SMClass. SMClassמצהיר על שיטת מחלקה ושיטת accessEnclosingClass()מופע accessEnclosingClass2(). שימו לב לדברים הבאים:

  • m2()שיטת ההפעלה של SMClass' accessEnclosingClass()מחייבת את SMClass.הקידומת כי accessEnclosingClass()היא מוכרזת static.
  • accessEnclosingClass()הוא מסוגל גישה EnclosingClassשל sשדה ולקרוא שלה m1()שיטה, למרות שניהם הוכרזו private.

רישום 2 מציג את קוד המקור למחלקת SMCDemoיישומים המדגימה כיצד להפעיל SMClassאת accessEnclosingClass()השיטה. זה גם מדגים כיצד SMClassלהפעיל ולהפעיל את accessEnclosingClass2()שיטת המופע שלה .

רישום 2. הפעלת שיטות של כיתת חברים סטטיים (SMCDemo.java)

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

כפי שמוצג ברשימה 2, אם ברצונך להפעיל שיטה של ​​מחלקה עליונה מתוך מחלקה סגורה, עליך להקדים את שם המחלקה הסגורה עם שם המחלקה הסוגרת שלה. כמו כן, על מנת לייצר כיתה סגורה עליך להקדים את שם הכיתה עם שם הכיתה הסוגרת שלה. לאחר מכן תוכל להפעיל את שיטת המופע בצורה הרגילה.

ערוך רישומים 1 ו -2 באופן הבא:

 javac *.java 

כאשר אתה מחבר מחלקה סגורה המכילה מחלקת חברים סטטית, המהדר יוצר קובץ מחלקה עבור מחלקת החברים הסטטיים ששמה מורכב משמו המחלקה הסוגר, תו סימן דולר ושם מחלקת החברים הסטטיים. במקרה זה, הידור תוצאות ב- EnclosingClass$SMCClass.classו- EnclosingClass.class.

הפעל את היישום באופן הבא:

 java SMCDemo 

עליכם להתבונן בפלט הבא:

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

דוגמה: שיעורים סטטיים ו- Java 2D

ספריית המחלקות הסטנדרטית של ג'אווה היא ספריית זמן ריצה של קבצי מחלקה, המאחסנת שיעורים מורכבים וסוגי ייחוס אחרים. הספרייה כוללת דוגמאות רבות לשיעורי חברים סטטיים, שחלקם נמצאים בשיעורי הצורה הגיאומטרית של Java 2D שנמצאים java.awt.geomבחבילה. (תלמד על חבילות במדריך הבא של Java 101 ).

Ellipse2Dבכיתה למצוא java.awt.geomמתאר אליפסה, אשר מוגדר על ידי מלבן מסגור במונחים של (x, y) השמאלית העליונה לפינה יחד עם ובמידות רוחב וגובה. המופעים מקטע הקוד הבא כי הארכיטקטורה של מעמד זה מבוסס על Floatו Doubleכיתות חבר סטטי, אשר שניהם תת 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 } 

Floatו Doubleכיתות להאריך Ellipse2D, מתן נקודה צפה ו נקודה צפה בדיוק כפול Ellipse2Dמימושים. מפתחים משתמשים Floatבכדי להפחית את צריכת הזיכרון, במיוחד מכיוון שתזדקק לאלפי או יותר מאובייקטים אלה כדי לבנות סצנה דו-ממדית אחת. אנו משתמשים Doubleכאשר נדרש דיוק רב יותר.

אתה לא יכול להסדיר את Ellipse2Dהכיתה המופשטת , אבל אתה יכול להסדיר את אחד Floatאו את הדברים Double. ניתן גם להרחיב Ellipse2Dלתיאור צורה מותאמת אישית המבוססת על אליפסה.

לדוגמא, נניח שאתה רוצה להציג Circle2Dשיעור שאינו קיים java.awt.geomבחבילה. קטע הקוד הבא מראה כיצד תיצור Ellipse2Dאובייקט עם יישום נקודה צפה:

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

שבר הקוד הבא מראה כיצד תיצור Ellipse2Dאובייקט עם יישום נקודה צפה עם דיוק כפול:

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

כעת תוכל להפעיל כל אחת מהשיטות המוצהרות Floatאו על Doubleידי הפעלת השיטה Ellipse2Dבהפניה המוחזרת (למשל e2d.getX()). באותו אופן, אתה יכול להפעיל כל אחת משיטות המשותפים Floatו Double, ואשר הכריז Ellipse2D. דוגמה היא:

 e2d.contains(2.0, 3.0) 

זה משלים את ההקדמה לשיעורי חברים סטטיים. לאחר מכן נבחן שיעורים פנימיים, שהם כיתות חברים לא סטטיות, כיתות מקומיות או כיתות אנונימיות. תלמד כיצד לעבוד עם כל שלושת סוגי הכיתות הפנימיות.

הורד קבל את הקוד הורד את קוד המקור לדוגמאות במדריך זה. נוצר על ידי ג'ף פרייזן עבור JavaWorld.

כיתות פנימיות, סוג 1: כיתות חברים לא סטטיות

למדת בעבר בסדרת Java 101 כיצד להכריז על שדות, מופעים ובונים לא סטטיים (מופעים) כחברים בכיתה. ניתן גם להכריז על כיתות חבר שאינן סטטיות , שהן מחלקות מקוננות שאינן סטטיות שאתה מצהיר באותה רמה כמו שדות מופע, שיטות ובונים. שקול דוגמה זו:

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

כאן אנו מציגים מחלקה ברמה העליונה Cעם שדה fמופע m(), שיטת מופע , קונסטרוקטור ומחלקת חברים לא סטטית D. כל הישויות הללו הם בני הכיתה C, אשר סוגרים אותם. עם זאת, שלא כמו בדוגמה הקודמת, ישויות מופע אלה קשורות למופעיםC ולא Cלמחלקה עצמה.

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.

HashMapמספק keySet()שיטה שמתקיימת KeySetבמידת הצורך ומחזירה מופע זה או מופע במטמון. הנה השיטה המלאה: