Dalam pemrograman Java, anggota dimodifikasi dengan menggunakan kata kunci pribadi. Hanya kelas tempat anggota ini berada dan metode kelas ini dapat digunakan, dan kelas lain yang tidak dapat mengakses anggota pribadi ini.
Di atas menjelaskan fungsi dasar dari pengubah pribadi. Hari ini, mari kita pelajari situasi kegagalan fungsi pribadi.
Kelas Internal Java
Di Java, saya percaya banyak orang telah menggunakan kelas internal. Java memungkinkan mendefinisikan kelas lain dalam satu kelas. Kelas di kelas adalah kelas internal, juga disebut kelas bersarang. Implementasi kelas internal yang sederhana dapat sebagai berikut
class outerclass {class innerclass {}}Masalah saat ini terkait dengan kelas internal Java, dan hanya melibatkan beberapa pengetahuan kelas internal yang terkait dengan penelitian artikel ini. Kami akan memperkenalkan artikel berikut tentang kelas internal Java.
Pertama kali gagal?
Skenario yang sering kita gunakan dalam pemrograman adalah untuk mengakses variabel anggota pribadi atau metode kelas eksternal di kelas internal, yang OK. Seperti yang diimplementasikan dalam kode berikut.
kelas publik outerclass {private string language = "en"; Private String Region = "US"; kelas publik innerclass {public void printouterclassprivateFields () {string fields = "language =" + language + "; region =" + region; System.out.println (bidang); }} public static void main (string [] args) {Outerclass Outer = new OuterClass (); Outerclass.innerclass inner = outer.new innerclass (); Inner.printouterclassprivateFields (); }}Mengapa ini? Bukankah anggota yang dimodifikasi pribadi hanya dapat diakses oleh kelas yang dijelaskan oleh anggota? Apakah pribadi benar -benar tidak valid?
Kompiler sedang main -main?
Kami menggunakan perintah javap untuk melihat file dua kelas yang dihasilkan
Hasil dekompilasi kelas luar
15:30 $ JAVAP -C OutClass yang Dikompil dari "OutterClass.java" Kelas Publik OutClass memperluas java.lang.Object {public Outerclass (); Kode: 0: aload_0 1: Invokespecial #11; // metode java/lang/objek. "<inin>" :() v 4: aload_0 5: ldc #13; // String EN 7: Putfield #15; // Bahasa lapangan: ljava/lang/string; 10: ALOAD_0 11: LDC #17; // String US 13: Putfield #19; // Wilayah Lapangan: Ljava/Lang/String; 16: returnpublic static void main (java.lang.string []); Kode: 0: Baru #1; // Kelas OutClass 3: DUP 4: Invokespecial #27; // Metode "<inin>" :() v 7: Store_1 8: Baru #28; // Kelas OutClass $ InnerClass 11: DUP 12: Aload_1 13: Dup 14: Invokevirtual #30; // metode java/lang/object.getclass :() ljava/lang/class; 17: Pop 18: Invokespecial #34; // Metode outerclass $ innerclass. "<inin>" :( LOGTERCLASS;) v 21: Store_2 22: Aload_2 23: Invokevirtual #37; // Metode Outerclass $ INNERCLASS.PRINTOUTERCLASSPRIVATEDFIELDS :() V 26: returnstatic java.lang.String Access $ 0 (OutterClass); Kode: 0: Aload_0 1: GetField #15; // Bahasa lapangan: ljava/lang/string; 4: areturnstatic java.lang.String akses $ 1 (OutterClass); Kode: 0: aload_0 1: getfield #19; // Wilayah Lapangan: Ljava/Lang/String; 4: areturn}Hah? Tidak, kami tidak mendefinisikan dua metode ini di kelas luar
Static Java.lang.String Access $ 0 (OutterClass); Kode: 0: Aload_0 1: GetField #15; // Bahasa lapangan: ljava/lang/string; 4: areturnstatic java.lang.String akses $ 1 (OutterClass); Kode: 0: aload_0 1: getfield #19; // Wilayah Lapangan: Ljava/Lang/String; 4: areturn}
Menilai dari komentar yang diberikan, akses $ 0 Mengembalikan atribut bahasa OutterClass; Akses $ 1 Mengembalikan atribut wilayah OutClass. Dan kedua metode menerima instance dari OutClass sebagai parameter. Mengapa kedua metode ini dihasilkan dan apa fungsinya? Mari kita lihat hasil dekompilasi dari kelas internal.
Dekompilasi Hasil Outclass $ InnerClass
15:37 $ JAVAP -C OUTERClass/$ InnerClass yang Dikompilasi dari "OutterClass.java" Kelas Publik Outclass $ InnerClass memperluas java.Lang.Object {Final OutClass ini $ 0; Public Outerclass $ InnerClass (OuterClass); Kode: 0: Aload_0 1: Aload_1 2: Putfield #10; // bidang ini $ 0: LOGTERCLASS; 5: aload_0 6: Invokespecial #12; // metode java/lang/objek. "<inin>" :() v 9: returnPublic void printouterClassprivateFields (); Kode: 0: baru #20; // kelas java/lang/stringbuilder 3: dup 4: ldc #22; // bahasa string = 6: InvokeSpecial #24; // Metode Java/Lang/StringBuilder. "<inin>" :( ljava/lang/string;) v 9: aload_0 10: getfield #10; // bidang ini $ 0: LOGTERCLASS; 13: Invokestatic #27; // Metode OuterClass.Access $ 0: (LOTORCLASS;) Ljava/Lang/String; 16: Invokevirtual #33; // metode java/lang/stringbuilder.append: (ljava/lang/string;) ljava/lang/stringbuilder; 19: LDC #37; // string; region = 21: Invokevirtual #33; // metode java/lang/stringbuilder.append: (ljava/lang/string;) ljava/lang/stringbuilder; 24: Aload_0 25: Getfield #10; // bidang ini $ 0: LOGTERCLASS; 28: Invokestatic #39; // Metode OuterClass.Access $ 1: (LOTORCLASS;) Ljava/Lang/String; 31: Invokevirtual #33; // metode java/lang/stringbuilder.append: (ljava/lang/string;) ljava/lang/stringbuilder; 34: Invokevirtual #42; // metode java/lang/stringbuilder.tostring :() ljava/lang/string; 37: Store_1 38: Getstatic #46; // bidang java/lang/system.out: ljava/io/printstream; 41: aload_1 42: Invokevirtual #52; // metode java/io/printStream.println: (ljava/lang/string;) v 45: return}Kode berikut memanggil kode ACCESS $ 0, dengan tujuan memperoleh bahasa Private Properti Private of OutClass.
13: Invokestatic #27; // Metode OuterClass.Access $ 0: (LOTORCLASS;) Ljava/Lang/String;
Kode berikut memanggil kode ACCESS $ 1, dengan tujuan mendapatkan properti pribadi wilayah dari Outclass.
28: Invokestatic #39; // Metode OuterClass.Access $ 1: (LOTORCLASS;) Ljava/Lang/String;
Catatan: Saat membangun kelas dalam, referensi ke kelas luar akan diteruskan dan digunakan sebagai properti dari kelas dalam, sehingga kelas dalam akan memiliki referensi ke kelas luarnya.
Ini $ 0 adalah referensi kelas eksternal yang dipegang oleh kelas dalam, yang melewati referensi dan memberikan nilai melalui konstruktor.
Final OutClass ini $ 0; Outclass Publik $ InnerClass (OutterClass); Kode: 0: Aload_0 1: Aload_1 2: Putfield #10; // bidang ini $ 0: LOGTERCLASS; 5: aload_0 6: Invokespecial #12; // metode java/lang/objek. "<inin>" :() v 9: return
ringkasan
Bagian pribadi ini tampaknya tidak valid, tetapi tidak valid, karena ketika kelas dalam memanggil properti pribadi dari kelas luar, eksekusi sebenarnya adalah memanggil metode statis atribut yang dihasilkan oleh kompiler (mis. Acess $ 0, akses $ 1, dll.) Untuk mendapatkan nilai atribut ini. Semua ini adalah penanganan khusus kompiler.
Kali ini tidak valid?
Jika metode penulisan di atas sangat umum digunakan, maka metode penulisan ini jarang terekspos, tetapi dapat dijalankan.
kelas publik OtherOutterClass {public static void main (string [] args) {innerclass inner = new anotheroTerClass (). new innerclass (); System.out.println ("InnerClass Filed =" + Inner.x); } class innerclass {private int x = 10; }}Seperti di atas, gunakan Javap untuk mendekompilasi dan lihat. Tapi kali ini pertama -tama kita melihat hasil kelas dalam
16:03 $ javap -C OtherOutterClass/$ InnerClass yang dikompilasi dari "OtherouterClass.java" kelas lainClass $ innerclass memperluas java.Lang.Object {final OtherClass ini $ 0 ini; OtherOutterClass $ Innerclass (OtherOutterClass); Kode: 0: Aload_0 1: Aload_1 2: Putfield #12; // bidang ini $ 0: lanotherouterclass; 5: aload_0 6: Invokespecial #14; // metode java/lang/objek. "<inin>" :() v 9: aload_0 10: bipush 10 12: putfield #17; // Field X: I 15: Returnstatic Int Access $ 0 (OtherOutterClass $ InnerClass); Kode: 0: Aload_0 1: Getfield #17; // Field X: I 4: IRETURN}Tampaknya lagi, dan kompiler secara otomatis menghasilkan metode backdoor untuk mendapatkan akses atribut pribadi $ 0 sekali untuk mendapatkan nilai x.
OtherouterClass. Hasil dekompilasi kelas
16:08 $ JAVAP -C LAINNYA LAINNYACLASS Dikompilasi dari "OtherOutterClass.java" Kelas Publik OtherOutClass memperluas java.lang.Object {public OtherouterClass (); Kode: 0: aload_0 1: Invokespecial #8; // metode java/lang/objek. "<inin>" :() v 4: returnPublic static void main (java.lang.string []); Kode: 0: baru #16; // kelas lainClass $ innerclass 3: dup 4: baru #1; // Kelas OtherOutterClass 7: DUP 8: Invokespecial #18; // Metode "<inin>" :() v 11: dup 12: Invokevirtual #19; // metode java/lang/object.getclass :() ljava/lang/class; 15: pop 16: Invokespecial #23; // Metode OtherOutterClass $ InnerClass. "<inin>" :( lanotherouterclass;) v 19: store_1 20: getstatic #26; // bidang java/lang/system.out: ljava/io/printstream; 23: baru #32; // kelas java/lang/stringbuilder 26: dup 27: ldc #34; // String InnerClass Filed = 29: InvokeSpecial #36; // Metode Java/Lang/StringBuilder. "<inin>" :( ljava/lang/string;) v 32: aload_1 33: Invokestatic #39; // Metode OtherOutterClass $ InnerClass.Access $ 0: (LanotherouterClass $ InnerClass;) I 36: Invokevirtual #43; // metode java/lang/stringbuilder.append: (i) ljava/lang/stringBuilder; 39: Invokevirtual #47; // metode java/lang/stringbuilder.tostring :() ljava/lang/string; 42: Invokevirtual #51; // metode java/io/printStream.println: (ljava/lang/string;) v 45: return}Panggilan ini adalah pengoperasian kelas eksternal untuk mendapatkan atribut pribadi x melalui instance dari kelas internal.
33: Invokestatic #39; // Metode OtherOutterClass $ InnerClass.Access $ 0: (LanotherouterClass $ InnerClass;) i
Ayo memiliki ringkasan lain
Ada kalimat dalam dokumen Java resmi
Jika anggota atau konstruktor dinyatakan pribadi, maka akses diizinkan jika dan hanya jika itu terjadi di dalam badan kelas tingkat atas (§7.6) yang melampirkan deklarasi anggota atau konstruktor.
Artinya jika anggota (kelas dalam) dan konstruktor ditetapkan sebagai pengubah pribadi, yang diizinkan jika dan hanya jika kelas eksternal mereka mengakses.
Bagaimana mencegah anggota pribadi kelas internal diakses oleh eksternal
Saya percaya bahwa setelah membaca dua bagian di atas, Anda akan merasa sulit bagi anggota pribadi kelas internal untuk menghindari diakses oleh kelas eksternal. Siapa yang bisa membuat kompiler "berantakan usil"? Itu sebenarnya bisa dilakukan. Yaitu menggunakan kelas dalam anonim.
Karena jenis objek Mrunnable dapat dijalankan, bukan jenis kelas dalam anonim (kita tidak bisa mendapatkannya secara normal), dan tidak ada properti x di runanble, mrunnable.x tidak diizinkan.
kelas publik 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 file = "+ p.mrunnable.x); // tidak diizinkan p.mrunnable.run (); // diizinkan }}Ringkasan terakhir
Dalam artikel ini, pribadi tampaknya tidak valid di permukaan, tetapi sebenarnya tidak. Sebaliknya, properti pribadi diperoleh melalui metode tidak langsung saat dipanggil.
Konstruksi kelas internal Java memegang aplikasi ke kelas eksternal, tetapi C ++ tidak, yang berbeda dari C ++.
Buku yang masuk jauh ke dalam detail Java
Ide Pemrograman Java
Seri Teknologi Inti Sun Company: Versi Cina Java Efektif Memahami Java Virtual Machine: Fitur Lanjutan dan Praktik Terbaik JVM
Di atas adalah kompilasi informasi tentang pengubah pribadi Java. Kami akan terus menambahkan informasi yang relevan di masa mendatang. Terima kasih atas dukungan Anda untuk situs ini!