最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的private修飾符失效了?

 更新時(shí)間:2015年01月15日 09:18:05   投稿:junjie  
這篇文章主要介紹了Java中的private修飾符失效了?本文討論在一個(gè)內(nèi)部類(lèi)里面可以訪(fǎng)問(wèn)外部類(lèi)的private成員變量或者方法的一種現(xiàn)象,需要的朋友可以參考下

在Java編程中,使用private關(guān)鍵字修飾了某個(gè)成員,只有這個(gè)成員所在的類(lèi)和這個(gè)類(lèi)的方法可以使用,其他的類(lèi)都無(wú)法訪(fǎng)問(wèn)到這個(gè)private成員。

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

Java內(nèi)部類(lèi)

在Java中相信很多人都用過(guò)內(nèi)部類(lèi),Java允許在一個(gè)類(lèi)里面定義另一個(gè)類(lèi),類(lèi)里面的類(lèi)就是內(nèi)部類(lèi),也叫做嵌套類(lèi)。一個(gè)簡(jiǎn)單的內(nèi)部類(lèi)實(shí)現(xiàn)可以如下

復(fù)制代碼 代碼如下:

class OuterClass {
    class InnerClass{
    }
}

今天的問(wèn)題和Java內(nèi)部類(lèi)相關(guān),只涉及到部分和本文研究相關(guān)的內(nèi)部類(lèi)知識(shí),具體關(guān)于Java內(nèi)部類(lèi)后續(xù)的文章會(huì)介紹。

第一次失效?

一個(gè)我們?cè)诰幊讨薪?jīng)常用到的場(chǎng)景,就是在一個(gè)內(nèi)部類(lèi)里面訪(fǎng)問(wèn)外部類(lèi)的private成員變量或者方法,這是可以的。如下面的代碼實(shí)現(xiàn)。

復(fù)制代碼 代碼如下:

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修飾的成員只能被成員所述的類(lèi)才能訪(fǎng)問(wèn)么?難道private真的失效了么?

編譯器在搗鬼?

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

OuterClass的反編譯結(jié)果

復(fù)制代碼 代碼如下:

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

}
咦?不對(duì),在OuterClass中我們并沒(méi)有定義這兩個(gè)方法

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

}


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

OuterClass$InnerClass的反編譯結(jié)果

復(fù)制代碼 代碼如下:

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
}


下面代碼調(diào)用access$0的代碼,其目的是得到OuterClass的language 私有屬性。
復(fù)制代碼 代碼如下:

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

下面代碼調(diào)用了access$1的代碼,其目的是得到OutherClass的region 私有屬性。
復(fù)制代碼 代碼如下:

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

注意:在內(nèi)部類(lèi)構(gòu)造的時(shí)候,會(huì)將外部類(lèi)的引用傳遞進(jìn)來(lái),并且作為內(nèi)部類(lèi)的一個(gè)屬性,所以?xún)?nèi)部類(lèi)會(huì)持有一個(gè)其外部類(lèi)的引用。

this$0就是內(nèi)部類(lèi)持有的外部類(lèi)引用,通過(guò)構(gòu)造方法傳遞引用并賦值。

復(fù)制代碼 代碼如下:

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

小結(jié)

這部分private看上去失效可,實(shí)際上并沒(méi)有失效,因?yàn)楫?dāng)內(nèi)部類(lèi)調(diào)用外部類(lèi)的私有屬性時(shí),其真正的執(zhí)行是調(diào)用了編譯器生成的屬性的靜態(tài)方法(即acess$0,access$1等)來(lái)獲取這些屬性值。這一切都是編譯器的特殊處理。

這次也失效?

如果說(shuō)上面的寫(xiě)法很常用,那么這樣的寫(xiě)法是不是很少接觸,但是卻可以運(yùn)行。

復(fù)制代碼 代碼如下:

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反編譯看一下。不過(guò)這次我們先看一下InnerClass的結(jié)果
復(fù)制代碼 代碼如下:

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

}


又出現(xiàn)了,編譯器又自動(dòng)生成了一個(gè)獲取私有屬性的后門(mén)方法access$0一次來(lái)獲取x的值。

AnotherOuterClass.class的反編譯結(jié)果

復(fù)制代碼 代碼如下:

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

}


其中這句調(diào)用就是外部類(lèi)通過(guò)內(nèi)部類(lèi)的實(shí)例獲取私有屬性x的操作
復(fù)制代碼 代碼如下:

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

再來(lái)個(gè)總結(jié)

其中java官方文檔 有這樣一句話(huà)

復(fù)制代碼 代碼如下:

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.

意思是 如果(內(nèi)部類(lèi)的)成員和構(gòu)造方法設(shè)定成了私有修飾符,當(dāng)且僅當(dāng)其外部類(lèi)訪(fǎng)問(wèn)時(shí)是允許的。

如何讓內(nèi)部類(lèi)私有成員不被外部訪(fǎng)問(wèn)

相信看完上面兩部分,你會(huì)覺(jué)得,內(nèi)部類(lèi)的私有成員想不被外部類(lèi)訪(fǎng)問(wèn)都很困難吧,誰(shuí)讓編譯器“愛(ài)管閑事”呢,其實(shí)也是可以做到的。那就是使用匿名內(nèi)部類(lèi)。

由于mRunnable對(duì)象的類(lèi)型為Runnable,而不是匿名內(nèi)部類(lèi)的類(lèi)型(我們無(wú)法正常拿到),而Runanble中沒(méi)有x這個(gè)屬性,所以mRunnable.x是不被允許的。

復(fù)制代碼 代碼如下:

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

最后總結(jié)

在本文中,private表面上看上去失效了,但實(shí)際上是沒(méi)有的,而是在調(diào)用時(shí)通過(guò)間接的方法來(lái)獲取私有的屬性。
Java的內(nèi)部類(lèi)構(gòu)造時(shí)持有對(duì)外部類(lèi)的應(yīng)用,C++不會(huì),這一點(diǎn)和C++不一樣。

相關(guān)文章

  • 一文搞懂Spring中的JavaConfig

    一文搞懂Spring中的JavaConfig

    這篇文章主要介紹了Spring中的JavaConfig知識(shí),包括事務(wù)注解驅(qū)動(dòng),properties配置文件加載方法,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • Spring中的Actuator使用詳解

    Spring中的Actuator使用詳解

    這篇文章主要介紹了Spring中的Actuator使用詳解,在生產(chǎn)環(huán)境中運(yùn)行的程序,并不總是穩(wěn)定、安靜、正確的,往往會(huì)遇到各式各樣的現(xiàn)場(chǎng)狀況,這個(gè)時(shí)候,就需要獲取該程序足夠多的運(yùn)行狀態(tài)信息,然后分析并對(duì)其進(jìn)行有效管理,需要的朋友可以參考下
    2023-09-09
  • 詳解Java ArrayList類(lèi)

    詳解Java ArrayList類(lèi)

    這篇文章主要介紹了Java ArrayList類(lèi)的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • 對(duì)spring task和線(xiàn)程池的深入研究

    對(duì)spring task和線(xiàn)程池的深入研究

    這篇文章主要介紹了對(duì)spring task和線(xiàn)程池的深入研究,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • python 與HFSS聯(lián)合仿真的教程講解

    python 與HFSS聯(lián)合仿真的教程講解

    這篇文章主要介紹了python 與HFSS聯(lián)合仿真的教程講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • 基于springboot與axios的整合問(wèn)題

    基于springboot與axios的整合問(wèn)題

    這篇文章主要介紹了springboot與axios的整合問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • JavaWeb實(shí)現(xiàn)學(xué)生管理系統(tǒng)的超詳細(xì)過(guò)程

    JavaWeb實(shí)現(xiàn)學(xué)生管理系統(tǒng)的超詳細(xì)過(guò)程

    學(xué)生信息管理系統(tǒng)是針對(duì)學(xué)校人事處的大量業(yè)務(wù)處理工作而開(kāi)發(fā)的管理軟件,主要用于學(xué)校學(xué)生信息管理,下面這篇文章主要給大家介紹了關(guān)于JavaWeb實(shí)現(xiàn)學(xué)生管理系統(tǒng)的超詳細(xì)過(guò)程,需要的朋友可以參考下
    2023-05-05
  • Spring?boot?admin?服務(wù)監(jiān)控利器詳解

    Spring?boot?admin?服務(wù)監(jiān)控利器詳解

    這篇文章主要介紹了Spring?boot?admin?服務(wù)監(jiān)控利器詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Kotlin傳遞可變長(zhǎng)參數(shù)給Java可變參數(shù)實(shí)例代碼

    Kotlin傳遞可變長(zhǎng)參數(shù)給Java可變參數(shù)實(shí)例代碼

    這篇文章主要介紹了Kotlin傳遞可變長(zhǎng)參數(shù)給Java可變參數(shù)實(shí)例代碼,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Spring Cloud中FeignClient實(shí)現(xiàn)文件上傳功能

    Spring Cloud中FeignClient實(shí)現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了Spring Cloud中FeignClient實(shí)現(xiàn)文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04

最新評(píng)論

海林市| 昌图县| 旌德县| 长治市| 杭锦后旗| 民勤县| 邯郸市| 合作市| 金塔县| 扶风县| 万载县| 宝山区| 锡林郭勒盟| 上杭县| 尚义县| 固镇县| 新安县| 海淀区| 德江县| 调兵山市| 裕民县| 新安县| 古蔺县| 任丘市| 襄垣县| 靖宇县| 长沙县| 郧西县| 石首市| 新乡市| 都昌县| 文山县| 洪湖市| 盐城市| 遂平县| 海阳市| 肇源县| 上饶县| 锡林郭勒盟| 新绛县| 平顶山市|