Type 1 access rights
1.1 analysis of four access rights
Java has four access rights, three of which have access rights modifiers, namely private, public and protected, and one without any modifiers
Four access rights:
- Private: the narrowest modifier in the Java language that restricts access rights. It is generally called private. The modified classes, attributes and methods can only be accessed by the objects of the class, and their subclasses cannot be accessed, let alone cross package access.
- Default: that is, it does not add any access modifier. It is usually called the default access mode. In this mode, access is only allowed in the same package.
- protect: an access modifier between public and private. It is generally called protection. The modified classes, attributes and methods can only be accessed by the methods and subclasses of the class itself, even if the subclasses can be accessed in different packages.
- Public: the modifier with the widest access restriction in the Java language, which is generally called public. The modified classes, properties and methods can be accessed not only across classes, but also across package s.
The following table shows the similarities and differences between the four access permissions, which will be more vivid. The table is as follows:
Same class | Same package | Subclasses of different packages | Non subclasses of different packages | |
---|---|---|---|---|
Private | √ | |||
Default | √ | √ | ||
Protected | √ | √ | √ | |
Public | √ | √ | √ | √ |
1.2 Protected analysis
Suppose there are two classes, AccessControlDemo and Base, under the package accesscontrol, where protected double price; It is a member variable of the Base class. Because the two classes are in the same package, you can directly access System.out.println(base.price) in the AccessControlDemo class; Specific examples are as follows:
accesscontrol.AccessControlDemo
package accesscontrol; public class AccessControlDemo { public static void main(String[] args) { // TODO Auto-generated method stub Base base=new Base("123-1",120.1); System.out.println(base.price); } }
accesscontrol.Base
package accesscontrol; public class Base { private String isbn; protected double price; //Default constructor public Base() {} //Constructor. If only the constructor with parameters is defined and the default constructor is not defined, the subclass of Base must define an explicit constructor public Base(String isbn, double price) { this.isbn = isbn; this.price = price; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
However, if we move the AccessControlDemo class to the test package, we will find that there are errors in eclipse and the compilation cannot pass, because the protected member variables are not visible in the test package.
Suppose we create a subclass Bulk of the Base class in the test package, that is, Bulk is a subclass of different packages of the Base class. Then protected double price can be accessed directly in the Bulk class; This is a member variable from the Base class. Examples are as follows:
test.AccessControlDemo
package test; public class AccessControlDemo { public static void main(String[] args) { Bulk bulk=new Bulk("123-1",120.1); bulk.print(); } }
test.Bulk
package test; import accesscontrol.Base; public class Bulk extends Base { public Bulk() { super(); } public Bulk(String isbn, double price) { super(isbn, price); } public void print() { System.out.println(this.price); } }
1.3 private failure
In Java programming, the private keyword is used to modify a member. Only the class and methods of the member can be used, and other classes cannot access the private member.
1.3.1 Java internal classes
I believe many people have used inner classes in Java. Java allows you to define another class in one class. The class in a class is an inner class, also known as nested class. A simple inner class implementation can be as follows
class OuterClass { class InnerClass{ } }
A scenario we often use in programming is to access private member variables or methods of external classes in an internal class, which is OK. As the following code implementation.
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? Is a member not decorated with private accessible only by the class described by the member? Does private really fail?
Use the javap command to view the two generated class files
Decompile result of OuterClass
$ javap -c OuterClass Compiled 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: return public static void main(java.lang.String[]); Code: 0: new #1; //class OuterClass 3: dup 4: invokespecial #27; //Method "<init>":()V 7: astore_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: astore_2 22: aload_2 23: invokevirtual #37; //Method OuterClass$InnerClass.printOuterClassPrivateFields:()V 26: return static java.lang.String access$0(OuterClass); Code: 0: aload_0 1: getfield #15; //Field language:Ljava/lang/String; 4: areturn static java.lang.String access$1(OuterClass); Code: 0: aload_0 1: getfield #19; //Field region:Ljava/lang/String; 4: areturn }
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: areturn static java.lang.String access$1(OuterClass); Code: 0: aload_0 1: getfield #19; //Field region:Ljava/lang/String; 4: areturn }
From the comments given, access access $0 returns the language attribute of outerclass; access returns the region attribute of outerclass. And both methods accept instances of outerclass as parameters. Why are these two methods generated and what are their functions? Let's take a look at the decompilation results of the inner class.
Decompile result of OuterClass$InnerClass
$ javap -c OuterClass\$InnerClass Compiled 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: return public 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: astore_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 code of access access $0 to get the language private attribute of OuterClass.
13: invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;
The following code calls the code of access to get the region private attribute of OutherClass.
28: invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;
Note: when constructing an internal class, the reference of the external class will be passed in as an attribute of the internal class, so the internal class will hold a reference to its external class.
thisThis $0 is the external class reference held by the internal class. The reference is passed and assigned by 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
This part of private seems to fail, but it doesn't fail in fact, because when the internal class calls the private attributes of the external class, its real execution is to call the static methods of the attributes generated by the compiler (i.e. acess acess $0, access $1,,access , etc.) to obtain these attribute values. All this is a special treatment of the compiler.
If the above writing method is very common, does it have little contact, but it can 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; } }
As above, use javap decompile to take a look. But this time, let's take a look at the results of InnerClass
16:03 $ javap -c AnotherOuterClass\$InnerClass Compiled 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: return static int access$0(AnotherOuterClass$InnerClass); Code: 0: aload_0 1: getfield #17; //Field x:I 4: ireturn }
Again, the compiler automatically generates a backdoor method access access $0 to get private properties to get the value of x once.
Decompile result of AnotherOuterClass.class
16:08 $ javap -c AnotherOuterClass Compiled 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: return public 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: astore_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 that the external class obtains the private property x through the instance of the internal class
33: invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I
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.
This means that if members and constructors (of inner classes) are set to private modifiers, they are allowed if and only if their outer classes are accessed.
So how to make the private members of the inner class not to be accessed externally is to use the anonymous inner class.
As follows, mRunnable.x is not allowed because the type of mRunnable object is Runnable, not the type of anonymous inner class (we can't get it normally), and there is no x attribute in Runnable.
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 } }
Finally, it is concluded that private looks invalid on the surface, but it is not. Instead, it obtains private properties through indirect methods when calling.