一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Java中的private修飾符失效了?

Java中的private修飾符失效了?

2019-12-08 20:52junjie JAVA教程

這篇文章主要介紹了Java中的private修飾符失效了?本文討論在一個內部類里面可以訪問外部類的private成員變量或者方法的一種現象,需要的朋友可以參考下

Java編程中,使用private關鍵字修飾了某個成員,只有這個成員所在的類和這個類的方法可以使用,其他的類都無法訪問到這個private成員。

上面描述了private修飾符的基本職能,今天來研究一下private功能失效的情況。

Java內部類

在Java中相信很多人都用過內部類,Java允許在一個類里面定義另一個類,類里面的類就是內部類,也叫做嵌套類。一個簡單的內部類實現可以如下

復制代碼代碼如下:

class OuterClass {
    class InnerClass{
    }
}


今天的問題和Java內部類相關,只涉及到部分和本文研究相關的內部類知識,具體關于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();
  }
}


這是為什么呢,不是private修飾的成員只能被成員所述的類才能訪問么?難道private真的失效了么?

 

編譯器在搗鬼?

我們使用javap命令查看一下生成的兩個class文件

OuterClass的反編譯結果

復制代碼代碼如下:


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

}


從給出來的注釋來看,access$0返回outerClass的language屬性;access$1返回outerClass的region屬性。并且這兩個方法都接受OuterClass的實例作為參數。這兩個方法為什么生成呢,有什么作用呢?我們看一下內部類的反編譯結果就知道了。

 

OuterClass$InnerClass的反編譯結果

復制代碼代碼如下:


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

 

小結

這部分private看上去失效可,實際上并沒有失效,因為當內部類調用外部類的私有屬性時,其真正的執行是調用了編譯器生成的屬性的靜態方法(即acess$0,access$1等)來獲取這些屬性值。這一切都是編譯器的特殊處理。

這次也失效?

如果說上面的寫法很常用,那么這樣的寫法是不是很少接觸,但是卻可以運行。

復制代碼代碼如下:


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反編譯看一下。不過這次我們先看一下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

}


又出現了,編譯器又自動生成了一個獲取私有屬性的后門方法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


再來個總結

 

其中java官方文檔 有這樣一句話

復制代碼代碼如下:

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.


意思是 如果(內部類的)成員和構造方法設定成了私有修飾符,當且僅當其外部類訪問時是允許的。

 

如何讓內部類私有成員不被外部訪問

相信看完上面兩部分,你會覺得,內部類的私有成員想不被外部類訪問都很困難吧,誰讓編譯器“愛管閑事”呢,其實也是可以做到的。那就是使用匿名內部類。

由于mRunnable對象的類型為Runnable,而不是匿名內部類的類型(我們無法正常拿到),而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); //not allowed
      p.mRunnable.run(); // allowed
  }
}

 

最后總結

在本文中,private表面上看上去失效了,但實際上是沒有的,而是在調用時通過間接的方法來獲取私有的屬性。
Java的內部類構造時持有對外部類的應用,C++不會,這一點和C++不一樣。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成人欧美一区二区三区 | 国产午夜永久福利视频在线观看 | 日产乱码卡1卡2卡三免费 | 久热在线这里只有精品7 | 国产日韩精品欧美一区 | 免费看视频网站 | 91久久福利国产成人精品 | 398av影院视频在线 | 女人张开腿 让男人桶个爽 免费观看 | 女人狂吮男人命根gif视频 | 欧亚精品一区二区三区 | 波多野结在线观看 | 天天天天天天天操 | 免费视频 | 天堂成人在线视频 | 美女叽叽 | 1986葫芦兄弟全集免费观看第十集 | 国产午夜大片 | 办公室恋情在线 | 国产视频播放 | 熟睡中的麻麻大白屁股小说 | 全黄h全肉细节修仙玄幻文 全彩调教侵犯h本子全彩妖气he | 久久黄色免费 | 五月婷婷丁香在线视频 | 亚洲高清视频在线观看 | 国产人成激情视频在线观看 | 亚洲精品私拍国产福利在线 | 色鬼网 | 丰腴尤物贵妇浪荡小说 | 好吊色青青青国产综合在线观看 | 91制片厂制作传媒网站破解 | 亚洲国产天堂 | 免费在线看片网站 | 538免费精品视频搬运工 | 日韩毛片在线视频 | 国产成人亚洲综合a∨婷婷 国产成人亚洲精品乱码在线观看 | 成人国产精品 | 欧美香蕉视频 | 日韩高清在线免费观看 | 日韩欧美一区黑人vs日本人 | 精品久久久久香蕉网 |