Javaプログラミングでは、秘密のキーワードを使用してメンバーが変更されます。このメンバーが配置されているクラスとこのクラスの方法を使用することができ、他のクラスはこのプライベートメンバーにアクセスできません。
上記は、プライベート修飾子の基本機能について説明しています。今日は、私的機能の失敗の状況を研究しましょう。
Java内部クラス
Javaでは、多くの人が内部クラスを使用していると思います。 Javaでは、1つのクラスで別のクラスを定義できます。クラスのクラスは、ネストされたクラスとも呼ばれる内部クラスです。単純な内部クラスの実装は次のとおりです
クラスアウタークラス{クラスインナークラス{}}今日の問題はJava内部クラスに関連しており、この記事の研究に関連する内部クラスの知識のみが含まれます。 Java内部クラスに関する次の記事を紹介します。
初めて失敗したのは?
プログラミングでよく使用するシナリオは、内部クラスの外部クラスのプライベートメンバー変数またはメソッドにアクセスすることです。これは問題ありません。次のコードで実装されています。
パブリッククラスのアウタークラス{private string言語= "en";プライベートストリング領域= "US"; public class innerclass {public void printouterclassprivatefields(){string fields = "Language =" + language + "; region =" + region; System.out.println(fields); }} public static void main(string [] args){outerclass outer = new outourclass(); outerclass.innerclass inner = outer.new innerclass(); inner.printouterclassprivatefields(); }}なぜこれがなぜですか?プライベート修正されたメンバーは、メンバーが説明したクラスによってのみアクセスできませんか?プライベートは本当に無効ですか?
コンパイラはいじり回していますか?
Javapコマンドを使用して、生成された2つのクラスファイルを表示します
アウタークラスの逆コンパイル結果
15:30 $ javap -c outercompiled from "outouterclass.java"からcomplass complass public class outerclassはjava.lang.object {public outourclass();コード:0:ALOAD_0 1:Invokespecial#11; //メソッドJava/Lang/Object。 "<init>" :()v 4:aload_0 5:ldc#13; //文字列EN 7:Putfield#15; //フィールド言語:ljava/lang/string; 10:ALOAD_0 11:LDC#17; //文字列US 13:Putfield#19; //フィールド領域:ljava/lang/string; 16:returnpublic static void main(java.lang.string []);コード:0:新しい#1; //クラスアウタークラス3:DUP 4:Invokespecial#27; // method "<init>" :()v 7:store_1 8:new#28; //クラスアウタークラス$ innerclass 11:dup 12:aload_1 13:dup 14:invokevirtual#30; //メソッドjava/lang/object.getclass :()ljava/lang/class; 17:POP 18:Invokespecial#34; //メソッドアウタークラス$ innerclass。 "<init>" :(ルータークラス;)v 21:store_2 22:aload_2 23:invokevirtual#37; //メソッドアウタークラス$ innerclass.printouterclassprivatefields :()v 26:returnstatic java.lang.stringアクセス$ 0(outourclass);コード:0:ALOAD_0 1:GetField#15; //フィールド言語:ljava/lang/string; 4:areturnstatic java.lang.stringアクセス$ 1(outourclass);コード:0:ALOAD_0 1:GetField#19; //フィールド領域:ljava/lang/string; 4:areturn}はぁ?いいえ、これら2つの方法をアウタークラスで定義しません
static java.lang.stringアクセス$ 0(outourclass);コード:0:ALOAD_0 1:GetField#15; //フィールド言語:ljava/lang/string; 4:areturnstatic java.lang.stringアクセス$ 1(outourclass);コード:0:ALOAD_0 1:GetField#19; //フィールド領域:ljava/lang/string; 4:areturn}
指定されたコメントから判断すると、Access $ 0はアウタークラスの言語属性を返します。アクセス$ 1は、アウタークラスの地域属性を返します。両方の方法は、アウタークラスのインスタンスをパラメーターとして受け入れます。なぜこれらの2つの方法が生成され、それらの機能は何ですか?内部クラスの逆コンパイル結果を見てみましょう。
分解結果アウタークラス$インナークラスの結果
15:37 $ javap -c outourclass/$ innerclasscompiled from "outouterclass.java" public class outerclass $ innerclassはjava.lang.object {final outourclass this $ 0; public outourclass $ innerclass(outourclass);コード:0:ALOAD_0 1:ALOAD_1 2:Putfield#10; //この$ 0:ルータークラス。 5:Aload_0 6:Invokespecial#12; //メソッドJava/Lang/Object。 "<init>" :()v 9:ReturnPublic void printouterclassprivatefields();コード:0:新しい#20; //クラスJava/Lang/StringBuilder 3:DUP 4:LDC#22; //文字列言語= 6:Invokespecial#24; //メソッドJava/Lang/StringBuilder。 "<init>" :( ljava/lang/string;)v 9:aload_0 10:getfield#10; //この$ 0:ルータークラス。 13:Invokestatic#27; //メソッドoutourclass.access $ 0 :(ルータークラス;)ljava/lang/string; 16:InvokeVirtual#33; //メソッドJava/Lang/StringBuilder.Append:(ljava/Lang/String;)ljava/lang/stringbuilder; 19:LDC#37; // string; region = 21:invokevirtual#33; //メソッドJava/Lang/StringBuilder.Append:(ljava/Lang/String;)ljava/lang/stringbuilder; 24:ALOAD_0 25:GetField#10; //この$ 0:ルータークラス。 28:Invokestatic#39; //メソッドoutourclass.access $ 1 :(ルータークラス;)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; //フィールドJava/lang/system.out:ljava/io/printstream; 41:ALOAD_1 42:InvokeVirtual#52; //メソッドJava/io/printstream.println :( ljava/lang/string;)v 45:return}次のコードは、Auterclassの言語私有財産を取得する目的で、Access $ 0コードを呼び出します。
13:Invokestatic#27; //メソッドoutourclass.access $ 0 :(ルータークラス;)ljava/lang/string;
次のコードは、アウタークラスの地域の私有財産を取得する目的で、アクセス1ドルのコードを呼び出します。
28:Invokestatic#39; //メソッドoutourclass.access $ 1 :(ルータークラス;)ljava/lang/string;
注:内側のクラスを構築する場合、外側クラスへの参照が渡され、内部クラスのプロパティとして使用されるため、内部クラスはその外側クラスへの参照を保持します。
この$ 0は、内部クラスが保持する外部クラスの参照であり、参照を通過し、コンストラクターを介して値を割り当てます。
最後のアウタークラスこの$ 0; public outerclass $ innerclass(outerclass);コード:0:ALOAD_0 1:ALOAD_1 2:Putfield#10; //この$ 0:ルータークラス。 5:Aload_0 6:Invokespecial#12; //メソッドJava/Lang/Object。 "<init>" :()v 9:return
まとめ
プライベートのこの部分は無効であるように見えますが、内側のクラスが外部クラスの私有地を呼び出す場合、その実際の実行は、コンパイラーによって生成された属性の静的メソッド(つまり、ACESS $ 0、アクセス$ 1など)を呼び出すことであるため、これらの属性値を取得するためです。これはすべて、コンパイラの特別な取り扱いです。
今回は無効ですか?
上記の執筆方法が非常に一般的に使用されている場合、この書き込み方法はめったに露出しませんが、実行できます。
public class anotherouterclass {public static void main(string [] args){innerclass inenter = new anotherouterclass()。new innerclass(); system.out.println( "innerclass filed =" + inner.x); } class innerclass {private int x = 10; }}上記のように、Javapを使用して逆コンピールして見てください。しかし今回は、最初にインナークラスの結果を見ています
16:03 $ javap -c anotherouterclass/$ innerclasscompiled from "anotherouterclass.java" class anotherouterclass $ innerclassはjava.lang.objectを拡張します{final otherouterclass this $ 0; anotherouterclass $ inneralclass(anotherouterclass);コード:0:ALOAD_0 1:ALOAD_1 2:Putfield#12; //この$ 0:lanotherouterclass; 5:ALOAD_0 6:Invokespecial#14; //メソッドJava/Lang/Object。 "<init>" :()v 9:aload_0 10:bipush 10 12:putfield#17; //フィールドX:i 15:Returnstatic Int Access $ 0(anotherouterclass $ innerclass);コード:0:ALOAD_0 1:GetField#17; //フィールドX:I 4:Ireturn}再び表示され、コンパイラは自動的にバックドアメソッドを生成してプライベート属性を取得し、Xの値を取得するために$ 0にアクセスします。
anotherouterclass.classの逆コンパイルの結果
16:08 $ javap -c aontherouterclasscompiled "anotherouterclass.java"からcompiled other copult autherouterclassはjava.lang.object {public anotherouterclass();コード:0:ALOAD_0 1:Invokespecial#8; //メソッドJava/Lang/Object。 "<init>" :()v 4:ReturnPublic Static void Main(java.lang.String []);コード:0:新しい#16; //クラスアナーズクラス$ innerclass 3:dup 4:new#1; // class AnotherouterClass 7:DUP 8:Invokespecial#18; // method "<init>" :()v 11:dup 12:invokevirtual#19; //メソッドjava/lang/object.getclass :()ljava/lang/class; 15:ポップ16:Invokespecial#23; //メソッドAnotherouterClass $ InnerClass。 "<init>" :( lanotherouterclass;)v 19:Store_1 20:GetStatic#26; //フィールドJava/lang/system.out:ljava/io/printstream; 23:新しい#32; //クラスJava/Lang/StringBuilder 26:DUP 27:LDC#34; // string innerclass filed = 29:invokespecial#36; //メソッドJava/Lang/StringBuilder。 "<init>" :( ljava/lang/string;)v 32:aload_1 33:Invokestatic#39; //メソッドAnotherouterClass $ 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; //メソッドAnotherouterClass $ InnerClass.Access $ 0 :( Lanotherouterclass $ innerclass;)i
別の要約をしましょう
公式のJava文書には文があります
メンバーまたはコンストラクターがプライベートと宣言されている場合、メンバーまたはコンストラクターの宣言を囲むトップレベルクラス(§7.6)内で発生する場合にのみアクセスが許可されます。
(内部クラス)メンバーとコンストラクターがプライベート修飾子として設定されている場合、それは外部クラスがアクセスする場合にのみ許可されます。
内部クラスの民間メンバーが外部からアクセスされないようにする方法
上記の2つの部分を読んだ後、内部クラスの民間メンバーが外部クラスからアクセスされないようにすることは難しいと思うでしょう。誰がコンパイラを「むら」することができますか?実際に行うことができます。つまり、匿名の内部クラスを使用することです。
mrunnableオブジェクトのタイプは実行可能であるため、匿名の内側クラスのタイプではなく(通常は取得できません)、runanbleにxプロパティはありません。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 filed = "+ p.mrunnable.x); //許可されていませんp.mrunnable.run(); // 許可された }}最終要約
この記事では、プライベートは表面上で無効であるように見えますが、実際にはそうではありません。代わりに、私有地は、呼び出されたときに間接的な方法によって取得されます。
Javaの内部クラス構造は、外部クラスへのアプリケーションを保持していますが、C ++はそうではありません。これはC ++とは異なります。
Javaの詳細に深く入り込む本
Javaプログラミングのアイデア
Sun Company's Core Technologyシリーズ:効果的なJava中国語バージョンJava Virtual Machineを深く理解する:JVMの高度な機能とベストプラクティス
上記は、Java Private Modifiersに関する情報の編集です。今後も関連情報を追加し続けます。このサイトへのご支援ありがとうございます!