في برمجة Java ، يتم تعديل العضو باستخدام الكلمة الرئيسية الخاصة. فقط الفصل الذي يوجد فيه هذا العضو ويمكن استخدام طريقة هذه الفئة ، ولا يمكن للفصول الأخرى الوصول إلى هذا العضو الخاص.
يصف ما ورد أعلاه الوظائف الأساسية للمعدل الخاص. اليوم ، دعونا ندرس وضع فشل الوظيفة الخاصة.
جافا الطبقات الداخلية
في جافا ، أعتقد أن الكثير من الناس استخدموا الطبقات الداخلية. يسمح Java بتحديد فصل آخر في فصل واحد. الفصل في الفصل هو فئة داخلية ، تسمى أيضًا فئة متداخلة. يمكن أن يكون تطبيق الطبقة الداخلية البسيطة على النحو التالي
الفئة الخارجية {class innerclass {}}ترتبط مشكلة اليوم بالفصول الداخلية Java ، وتتضمن فقط بعض المعرفة الداخلية من الطبقة الداخلية المتعلقة ببحث هذه المقالة. سنقدم المقالات التالية على فصول Java الداخلية.
المرة الأولى التي فشلت فيها؟
سيناريو نستخدمه غالبًا في البرمجة هو الوصول إلى متغيرات الأعضاء الخاصة أو طرق الفئات الخارجية في فئة داخلية ، وهو أمر جيد. كما تم تنفيذها في الكود التالي.
الفئة العامة الخارجية {private string language = "en" ؛ سلسلة السلسلة الخاصة = "الولايات المتحدة" ؛ الفئة العامة innerclass {public void printoUterClassPrivatefields () {string fields = "language =" + language + "؛ region =" + region ؛ system.out.println (الحقول) ؛ }} public static void main (string [] args) {OuterClass Outer = new Outerclass () ؛ outerclass.innerclass inner = outer.new innerclass () ؛ inner.printouterClassPrivatefields () ؛ }}لماذا هذا؟ ألا يمكن الوصول إلى العضو المعدل الخاص إلا من خلال الفصل الذي وصفه العضو؟ هل خاص غير صالح حقًا؟
المترجم يعبث؟
نستخدم الأمر javap لعرض ملفين فئة تم إنشاؤه
نتائج فك الشفافة الخارجية
15:30 $ JAVAP -C OUTRCLASSCOMPLIDER من "Outerclass.java" الطبقة العامة الموسعة تمتد java.lang.object {public OuterClass () ؛ الكود: 0: aload_0 1: invokespecial #11 ؛ // method java/lang/object. // string en 7: putfield #15 ؛ // لغة الحقل: ljava/lang/string ؛ 10: aload_0 11: LDC #17 ؛ // String US 13: Putfield #19 ؛ // منطقة الحقل: ljava/lang/string ؛ 16: عودة الفراغ الثابتة الرئيسية (java.lang.string []) ؛ الكود: 0: جديد #1 ؛ // Class Outerclass 3: DUP 4: Invokespecial #27 ؛ // الطريقة "<Ing>" :() V 7: Store_1 8: New 28 ؛ // Class Outerclass $ Innerclass 11: DUP 12: aload_1 13: DUP 14: InvokeVirtual #30 ؛ // طريقة java/lang/object.getClass :() ljava/lang/class ؛ 17: POP 18: Invokespecial #34 ؛ // Method Outerclass $ innerclass. // الطريقة الخارجية $ innerclass.printOuterClassPrivatefields :() v 26: returnstatic java.lang.String Access $ 0 (Outerclass) ؛ الكود: 0: aload_0 1: getfield #15 ؛ // لغة الحقل: ljava/lang/string ؛ 4: areturnstatic java.lang.String Access 1 $ (Outerclass) ؛ الكود: 0: aload_0 1: getfield #19 ؛ // منطقة الحقل: ljava/lang/string ؛ 4: Areturn}هاه؟ لا ، نحن لا نحدد هاتين الطريقتين في الخارجي
static java.lang.String Access $ 0 (Outerclass) ؛ الكود: 0: aload_0 1: getfield #15 ؛ // لغة الحقل: ljava/lang/string ؛ 4: areturnstatic java.lang.String Access 1 $ (Outerclass) ؛ الكود: 0: aload_0 1: getfield #19 ؛ // منطقة الحقل: ljava/lang/string ؛ 4: Areturn}
انطلاقًا من التعليقات المقدمة ، إرجاع $ 0 سمة اللغة للكرة الخارجية ؛ الوصول إلى $ 1 إرجاع سمة المنطقة من CONCLASS. وكلا الطريقتين يقبلان مثيلًا للكلمة الخارجية كمعلمة. لماذا يتم إنشاء هاتين الطريقتين وما هي وظائفهما؟ دعونا نلقي نظرة على نتائج فك الطبقة الداخلية.
نتيجة فك الارتباط للكلمة الخارجية $ innerclass
15:37 $ JAVAP -C OUTERCLASS/$ innerclasscompiled من "Outerclass.java" الطبقة العامة العامة $ $ innerclass java.lang.object {Final Outerclass this $ 0 ؛ Outerclass $ innerclass (Outerclass) ؛ الكود: 0: aload_0 1: aload_1 2: putfield #10 ؛ // حقل هذا $ 0: louterclass ؛ 5: aload_0 6: invokespecial #12 ؛ // method java/lang/object. الكود: 0: جديد #20 ؛ // Class Java/Lang/StringBuilder 3: DUP 4: LDC #22 ؛ // string language = 6: invokespecial #24 ؛ // method java/lang/stringbuilder. // حقل هذا $ 0: louterclass ؛ 13: Invokestatic #27 ؛ // method Outerclass.Access $ 0: (LouterClass ؛) ljava/lang/string ؛ 16: InvokeVirtual #33 ؛ // طريقة java/lang/stringbuilder.append: (ljava/lang/string ؛) ljava/lang/stringBuilder ؛ 19: LDC #37 ؛ // string ؛ المنطقة = 21: InvokeVirtual #33 ؛ // طريقة java/lang/stringbuilder.append: (ljava/lang/string ؛) ljava/lang/stringBuilder ؛ 24: aload_0 25: Getfield #10 ؛ // حقل هذا $ 0: louterclass ؛ 28: Invokestatic #39 ؛ // Method Outerclass.Access $ 1: (LouterClass ؛) ljava/lang/string ؛ 31: InvokeVirtual #33 ؛ // طريقة java/lang/stringbuilder.append: (ljava/lang/string ؛) ljava/lang/stringBuilder ؛ 34: InvokeVirtual #42 ؛ // طريقة java/lang/stringbuilder.toString :() ljava/lang/string ؛ 37: store_1 38: getStatic #46 ؛ // Field Java/lang/system.out: ljava/io/printstream ؛ 41: aload_1 42: InvokeVirtual #52 ؛ // الطريقة java/io/printstream.println: (ljava/lang/string ؛) v 45: return}يستدعي الرمز التالي رمز الوصول 0 $ ، بهدف الحصول على خاصية اللغة الخاصة للكلية الخارجية.
13: Invokestatic #27 ؛ // method Outerclass.Access $ 0: (LouterClass ؛) ljava/lang/string ؛
يستدعي الرمز التالي رمز الوصول إلى $ 1 ، بهدف الحصول على الممتلكات الخاصة للمنطقة الخاصة بالكلاس الخارجي.
28: Invokestatic #39 ؛ // Method Outerclass.Access $ 1: (LouterClass ؛) ljava/lang/string ؛
ملاحظة: عند بناء فئة داخلية ، سيتم تمرير الإشارة إلى الفئة الخارجية واستخدامها كخاصية للفئة الداخلية ، وبالتالي فإن الفئة الداخلية ستعقد إشارة إلى فئةها الخارجية.
هذا $ 0 هو مرجع الفئة الخارجية التي تحتفظ بها الفئة الداخلية ، والتي تمرر المرجع وتعيين القيمة من خلال المُنشئ.
COLCLASS النهائي هذا 0 دولار ؛ $ class $ $ innerclass (conclass) ؛ الكود: 0: aload_0 1: aload_1 2: putfield #10 ؛ // حقل هذا $ 0: louterclass ؛ 5: aload_0 6: invokespecial #12 ؛ // الطريقة java/lang/objec
ملخص
يبدو أن هذا الجزء من القطاع الخاص غير صالح ، لكنه ليس غير صالح ، لأنه عندما تستدعي الفئة الداخلية الخصائص الخاصة للفئة الخارجية ، فإن تنفيذها الحقيقي هو استدعاء الطرق الثابتة للسمات التي تم إنشاؤها بواسطة المترجم (أي acess $ 0 ، الوصول إلى $ 1 ، وما إلى ذلك) للحصول على قيم السمة هذه. كل هذا هو معالجة خاصة للمترجم.
هذه المرة غير صالح؟
إذا كانت طريقة الكتابة أعلاه شائعة الاستخدام ، فهل نادراً ما يتم عرض طريقة الكتابة هذه ، ولكن يمكن تشغيلها.
الطبقة العامة otherouterclass {public static void main (string [] args) {innerclass inner = new OtherOterClass (). new Innerclass () ؛ system.out.println ("innerclass filed =" + inner.x) ؛ } class innerclass {private int x = 10 ؛ }}مثل أعلاه ، استخدم javap لإلغاء إلقاء نظرة. لكن هذه المرة نلقي نظرة أولاً على نتائج الداخلية
16:03 $ JAVAP -C otherouterclass/$ innerclasscompiled من "otherouterclass.java" class otheroUterclass $ innerclass يمتد java.lang.object {Final OtherOterClass this $ 0 ؛ الكود: 0: aload_0 1: aload_1 2: putfield #12 ؛ // حقل هذا $ 0: lanotherouterClass ؛ 5: aload_0 6: invokespecial #14 ؛ // method java/lang/object. // Field X: I 15: ReturnStatic int Access $ 0 (OtherouterClass $ innerclass) ؛ الكود: 0: aload_0 1: getfield #17 ؛ // Field X: I 4: Ireturn}يظهر مرة أخرى ، ويقوم المترجم تلقائيًا بإنشاء طريقة خلفية للوصول إلى سمات خاصة للوصول إلى 0 دولار مرة واحدة للحصول على قيمة x.
نتائج إلغاء التثبيت الأخرى
16:08 $ javap -c otherouterclasscomplized من "otherouterclass.java" الطبقة العامة الأخرى تمتد java.lang.object {publicoUterClass () ؛ الكود: 0: aload_0 1: invokespecial #8 ؛ // method java/lang/object. الكود: 0: جديد #16 ؛ // class OtherouterClass $ Innerclass 3: DUP 4: New 1 ؛ // Class OtherouterClass 7: DUP 8: Invokespecial #18 ؛ // الطريقة "<Ing>" :() V 11: DUP 12: InvokeVirtual #19 ؛ // طريقة java/lang/object.getClass :() ljava/lang/class ؛ 15: Pop 16: vokespecial #23 ؛ // method otherouterclass $ innerclass. // Field Java/lang/system.out: ljava/io/printstream ؛ 23: جديد #32 ؛ // Class Java/Lang/StringBuilder 26: DUP 27: LDC #34 ؛ // string innerclass filed = 29: invokespecial #36 ؛ // method java/lang/stringbuilder. // method otherouterclass $ innerclass.Access $ 0: (lanotherouterClass $ Innerclass ؛) i 36: invokevirtual #43 ؛ // طريقة java/lang/stringbuilder.append: (i) ljava/lang/stringBuilder ؛ 39: InvokeVirtual #47 ؛ // طريقة java/lang/stringbuilder.toString :() ljava/lang/string ؛ 42: InvokeVirtual #51 ؛ // الطريقة java/io/printstream.println: (ljava/lang/string ؛) v 45: return}هذه المكالمة هي تشغيل الفئة الخارجية للحصول على السمة الخاصة X من خلال مثيل للفئة الداخلية.
33: Invokestatic #39 ؛ // method otherouterclass $ innerclass.access $ 0: (lanotherouterClass $ innerclass ؛) i
دعونا نمتلك ملخصًا آخر
هناك جملة في وثيقة جافا الرسمية
إذا تم الإعلان عن العضو أو المنشئ الخاص ، فسيتم السماح بالوصول إذا حدث وفقط إذا حدث داخل جسم فئة المستوى الأعلى (الفقرة 7.6) الذي يحيط بالإعلان عن العضو أو المنشئ.
بمعنى ما إذا تم تعيين أعضاء (فئة داخلية) ومقدموها كمعدلات خاصة ، والتي يُسمح بها إذا وصول فئة الخارجية الخاصة بهم فقط.
كيفية منع الأعضاء الخاصة من الفصول الداخلية من الوصول إلى الخارج
أعتقد أنه بعد قراءة الجزأين أعلاه ، ستشعر أنه من الصعب على الأعضاء الخاصين في الطبقات الداخلية تجنب الوصول إليه بواسطة فصول خارجية. من يمكنه جعل المترجم "العبث الفضولي"؟ يمكن أن يتم في الواقع. وهذا هو استخدام فصول داخلية مجهولة.
نظرًا لأن نوع الكائن القابل للتطبيق يمكن تشغيله ، وليس نوع الفئة الداخلية المجهولة (لا يمكننا الحصول عليه بشكل طبيعي) ، ولا يوجد أي خاصية X في runanble ، mrunnable.x غير مسموح به.
Public Class PrivatetOouter {runnable mrunnable = new RunNable () {private int x = 10 ؛ Override public void run () {system.out.println (x) ؛ }} ؛ public static void main (string [] args) {privatetoouter p = new privatetoouter () ؛ //system.out.println("anonymous class private = "+ p.mrunnable.x) ؛ // غير مسموح به p.mrunnable.run () ؛ // مسموح }}الملخص النهائي
في هذه المقالة ، يبدو أن الخاص غير صالح على السطح ، ولكن في الواقع لا يفعل ذلك. بدلاً من ذلك ، يتم الحصول على الخصائص الخاصة من خلال طرق غير مباشرة عند استدعاءها.
يحتفظ إنشاء فئة Java الداخلية بتطبيقات على الفئات الخارجية ، ولكن C ++ لا ، وهو ما يختلف عن C ++.
الكتب التي تتعمق في تفاصيل جافا
أفكار برمجة جافا
سلسلة التكنولوجيا الأساسية لشركة Sun Company: النسخة الصينية Java الفعالة فهم العميق Java Virtual Machine: الميزات المتقدمة وأفضل ممارسات JVM
ما سبق هو مجموعة من المعلومات على المعدلات الخاصة Java. سنستمر في إضافة المعلومات ذات الصلة في المستقبل. شكرا لك على دعمك لهذا الموقع!