细说 | "失效"的private修饰符

2020-10-08  本文已影响0人  是阿胖胖吖

本篇将会说到失效的private修饰符

前言

在Java编程里,使用private关键字修饰了一个成员,只有成员内部可以访问,其余成员都不可访问,今天说明一下private功能失效的问题。

整理了一份Java核心知识点。覆盖了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。基本上涵盖了Java架构所有的技术知识点的资料,还覆盖了大厂面试题,同时也有一些springboot等项目源码分享给大家

由于资料图片太多就不一一的展示出来了

如果需要获取到这个文档的话帮忙转发一下然后再关注我私信回复“架构资料”得到获取方式吧!

失效之Java内部类

在一个内部类里访问外部类的private成员变量或者方法。

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();

  }

}

查看原因

使用javap命令查看一下生成的class文件

15:30 $ 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

}

在这里有一个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

}

根据注释,可以知道access1返回outerClass的region属性,并且这两个方法都接受OuterClass的实例作为参数, 对这两个方法进行反编译。

15:37 $ 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

}

下面代码调用access$0的代码,其目的是得到OuterClass的language 私有属性。

13:   invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;

下面代码调用了access$1的代码,其目的是得到OutherClass的region 私有属性。

28:   invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;

即,在内部类构造的时候,会有外部类的引用传递进来,并且作为内部类的一个属性,所以内部类会持有一个其外部类的应用。this$0就是内部类持有的外部类引用,通过构造方法传递引用并赋值。

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 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;

  }

}

和上面一样,使用Javap反编译一下

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

}

编译器自动生成了一个access$0一次来获取x的值 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

}

其中这句话,直接说明通过内部类的实例,获取到私有属性x的操作。

33:   invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I

在官网文档中是这样说道的,如果(内部类的)成员和构造方法设定成了私有修饰符,当且仅当其外部类访问时是允许的。

如何保证不被访问

使用的方法相当简单,使用匿名内部类的方法实现

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

  }

}

上一篇 下一篇

猜你喜欢

热点阅读