Java class access

catalogue

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.

Tags: Java

Posted on Sat, 06 Nov 2021 19:22:51 -0400 by Shuriken1