In Java programming, a member is modified by using the private keyword. Only the class where this member is located and the method of this class can be used, and other classes cannot access this private member.
The above describes the basic functions of the private modifier. Today, let’s study the situation of private function failure.
Java internal classes
In Java, I believe many people have used internal classes. Java allows defining another class in one class. The class in the class is an internal class, also called a nested class. A simple internal class implementation can be as follows
class OuterClass { class InnerClass{ }}Today's problem is related to Java internal classes, and only involves some internal class knowledge related to this article's research. We will introduce the following articles on Java internal classes.
The first time it failed?
A scenario that we often use in programming is to access private member variables or methods of external classes in an internal class, which is OK. As implemented in the following code.
public class OuterClass { private String language = "en"; private String region = "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 OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); inner.printOuterClassPrivateFields(); }}Why is this? Isn't a private modified member accessible only by the class described by the member? Is private really invalid?
The compiler is messing around?
We use the javap command to view the generated two class files
Decompilation results of OuterClass
15:30 $ javap -c OuterClassCompiled from "OuterClass.java"public class OuterClass extends java.lang.Object{public OuterClass(); Code: 0: aload_0 1: invokespecial #11; //Method java/lang/Object."<init>":()V 4: aload_0 5: ldc #13; //String en 7: putfield #15; //Field language:Ljava/lang/String; 10: aload_0 11: ldc #17; //String US 13: putfield #19; //Field region:Ljava/lang/String; 16: returnpublic static void main(java.lang.String[]); Code: 0: new #1; //class OuterClass 3: dup 4: invokespecial #27; //Method "<init>":()V 7: store_1 8: new #28; //class OuterClass$InnerClass 11: dup 12: aload_1 13: dup 14: invokevirtual #30; //Method java/lang/Object.getClass:()Ljava/lang/Class; 17: pop 18: invokespecial #34; //Method OuterClass$InnerClass."<init>":(LOuterClass;)V 21: store_2 22: aload_2 23: invokevirtual #37; //Method OuterClass$InnerClass.printOuterClassPrivateFields:()V 26: returnstatic java.lang.String access$0(OuterClass); Code: 0: aload_0 1: getfield #15; //Field language:Ljava/lang/String; 4: areturnstatic java.lang.String access$1(OuterClass); Code: 0: aload_0 1: getfield #19; //Field region:Ljava/lang/String; 4: areturn}Huh? No, we do not define these two methods in OuterClass
static java.lang.String access$0(OuterClass); Code: 0: aload_0 1: getfield #15; //Field language:Ljava/lang/String; 4: areturnstatic java.lang.String access$1(OuterClass); Code: 0: aload_0 1: getfield #19; //Field region:Ljava/lang/String; 4: areturn}
Judging from the comments given, access$0 returns the language attribute of outerClass; access$1 returns the region attribute of outerClass. And both methods accept an instance of OuterClass as a parameter. Why are these two methods generated and what are their functions? Let’s look at the decompilation results of the internal class.
Decompilation result of OuterClass$InnerClass
15:37 $ javap -c OuterClass/$InnerClassCompiled from "OuterClass.java"public class OuterClass$InnerClass extends java.lang.Object{final OuterClass this$0;public OuterClass$InnerClass(OuterClass); Code: 0: aload_0 1: aload_1 2: putfield #10; //Field this$0:LOuterClass; 5: aload_0 6: invokespecial #12; //Method java/lang/Object."<init>":()V 9: returnpublic void printOuterClassPrivateFields(); Code: 0: new #20; //class java/lang/StringBuilder 3: dup 4: ldc #22; //String language= 6: invokespecial #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 9: aload_0 10: getfield #10; //Field this$0:LOuterClass; 13: invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String; 16: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: ldc #37; //String ;region= 21: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: aload_0 25: getfield #10; //Field this$0:LOuterClass; 28: invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String; 31: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: invokevirtual #42; //Method 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; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 45: return}The following code calls the access$0 code, with the purpose of obtaining the language private property of OuterClass.
13: invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;
The following code calls the access$1 code, with the purpose of obtaining the region private property of OuterClass.
28: invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;
Note: When constructing an inner class, the reference to the outer class will be passed in and used as a property of the inner class, so the inner class will hold a reference to its outer class.
This$0 is the external class reference held by the inner class, which passes the reference and assigns the value through the constructor.
final OuterClass this$0;public OuterClass$InnerClass(OuterClass); Code: 0: aload_0 1: aload_1 2: putfield #10; //Field this$0:LOuterClass; 5: aload_0 6: invokespecial #12; //Method java/lang/Object."<init>":()V 9: return
summary
This part of private seems to be invalid, but it is not invalid, because when the inner class calls the private properties of the outer class, its real execution is to call the static methods of the attributes generated by the compiler (i.e. acess$0, access$1, etc.) to obtain these attribute values. All of this is a special handling of the compiler.
This time it's invalid?
If the above writing method is very commonly used, then is this writing method rarely exposed, but it can be run.
public class AnotherOuterClass { public static void main(String[] args) { InnerClass inner = new AnotherOuterClass().new InnerClass(); System.out.println("InnerClass Filed = " + inner.x); } class InnerClass { private int x = 10; }}Like above, use javap to decompile and take a look. But this time we first look at the results of InnerClass
16:03 $ javap -c AnotherOuterClass/$InnerClassCompiled from "AnotherOuterClass.java"class AnotherOuterClass$InnerClass extends java.lang.Object{final AnotherOuterClass this$0;AnotherOuterClass$InnerClass(AnotherOuterClass); Code: 0: aload_0 1: aload_1 2: putfield #12; //Field this$0:LAnotherOuterClass; 5: aload_0 6: invokespecial #14; //Method java/lang/Object."<init>":()V 9: aload_0 10: bipush 10 12: putfield #17; //Field x:I 15: returnstatic int access$0(AnotherOuterClass$InnerClass); Code: 0: aload_0 1: getfield #17; //Field x:I 4: ireturn}It appears again, and the compiler automatically generates a backdoor method to obtain private attributes access$0 once to get the value of x.
AnotherOuterClass.class decompilation results
16:08 $ javap -c AnotherOuterClassCompiled from "AnotherOuterClass.java"public class AnotherOuterClass extends java.lang.Object{public AnotherOuterClass(); Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: new #16; //class AnotherOuterClass$InnerClass 3: dup 4: new #1; //class AnotherOuterClass 7: dup 8: invokespecial #18; //Method "<init>":()V 11: dup 12: invokevirtual #19; //Method java/lang/Object.getClass:()Ljava/lang/Class; 15: pop 16: invokespecial #23; //Method AnotherOuterClass$InnerClass."<init>":(LAnotherOuterClass;)V 19: store_1 20: getstatic #26; //Field java/lang/System.out:Ljava/io/PrintStream; 23: new #32; //class java/lang/StringBuilder 26: dup 27: ldc #34; //String InnerClass Filed = 29: invokespecial #36; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 32: aload_1 33: invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I 36: invokevirtual #43; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 39: invokevirtual #47; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 42: invokevirtual #51; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 45: return}This call is the operation of the external class to obtain private attribute x through an instance of the internal class.
33: invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I
Let's have another summary
There is a sentence in the official java document
if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.
Meaning if (inner class) members and constructors are set as private modifiers, which are allowed if and only if their external class accesses.
How to prevent private members of internal classes from being accessed by external
I believe that after reading the above two parts, you will feel that it is difficult for private members of internal classes to avoid being accessed by external classes. Who can make the compiler "messing nosy"? It can actually be done. That is to use anonymous inner classes.
Since the type of mRunnable object is Runnable, not the type of anonymous inner class (we cannot get it normally), and there is no x property in Runanble, mRunnable.x is not allowed.
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); //not allowed p.mRunnable.run(); // allowed }}Final summary
In this article, private appears to be invalid on the surface, but in fact it does not. Instead, private properties are obtained through indirect methods when called.
Java's internal class construction holds applications to external classes, but C++ does not, which is different from C++.
Books that go deep into Java details
Java programming ideas
Sun Company's Core Technology Series: Effective Java Chinese Version Deeply Understanding Java Virtual Machine: Advanced Features and Best Practices of JVM
The above is a compilation of the information on Java private modifiers. We will continue to add relevant information in the future. Thank you for your support for this site!