欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的private修飾符失效了?

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

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

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

Java內(nèi)部類

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

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

class OuterClass {
    class InnerClass{
    }
}

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

第一次失效?

一個(gè)我們?cè)诰幊讨薪?jīng)常用到的場(chǎng)景,就是在一個(gè)內(nè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修飾的成員只能被成員所述的類才能訪問么?難道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中我們并沒有定義這兩個(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

}


從給出來的注釋來看,access$0返回outerClass的language屬性;access$1返回outerClass的region屬性。并且這兩個(gè)方法都接受OuterClass的實(shí)例作為參數(shù)。這兩個(gè)方法為什么生成呢,有什么作用呢?我們看一下內(nè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)部類構(gòu)造的時(shí)候,會(huì)將外部類的引用傳遞進(jìn)來,并且作為內(nèi)部類的一個(gè)屬性,所以內(nèi)部類會(huì)持有一個(gè)其外部類的引用。

this$0就是內(nèi)部類持有的外部類引用,通過構(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í)際上并沒有失效,因?yàn)楫?dāng)內(nèi)部類調(diào)用外部類的私有屬性時(shí),其真正的執(zhí)行是調(diào)用了編譯器生成的屬性的靜態(tài)方法(即acess$0,access$1等)來獲取這些屬性值。這一切都是編譯器的特殊處理。

這次也失效?

如果說上面的寫法很常用,那么這樣的寫法是不是很少接觸,但是卻可以運(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反編譯看一下。不過這次我們先看一下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è)獲取私有屬性的后門方法access$0一次來獲取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)用就是外部類通過內(nèi)部類的實(shí)例獲取私有屬性x的操作
復(fù)制代碼 代碼如下:

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

再來個(gè)總結(jié)

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

復(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)部類的)成員和構(gòu)造方法設(shè)定成了私有修飾符,當(dāng)且僅當(dāng)其外部類訪問時(shí)是允許的。

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

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

由于mRunnable對(duì)象的類型為Runnable,而不是匿名內(nèi)部類的類型(我們無法正常拿到),而Runanble中沒有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í)際上是沒有的,而是在調(diào)用時(shí)通過間接的方法來獲取私有的屬性。
Java的內(nèi)部類構(gòu)造時(shí)持有對(duì)外部類的應(yīng)用,C++不會(huì),這一點(diǎn)和C++不一樣。

相關(guān)文章

  • Log4j新手快速入門教程

    Log4j新手快速入門教程

    這篇文章主要給大家介紹了關(guān)于Log4j新手入門的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Log4j具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 關(guān)于@EnableGlobalMethodSecurity注解的用法解讀

    關(guān)于@EnableGlobalMethodSecurity注解的用法解讀

    這篇文章主要介紹了關(guān)于@EnableGlobalMethodSecurity注解的用法解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java操作oracle數(shù)據(jù)庫(kù)示例

    java操作oracle數(shù)據(jù)庫(kù)示例

    這篇文章主要介紹了java操作oracle數(shù)據(jù)庫(kù)示例,需要的朋友可以參考下
    2014-04-04
  • java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)

    java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)

    本篇文章主要介紹了java實(shí)現(xiàn)excel和txt文件互轉(zhuǎn)的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-04-04
  • 淺談fastjson的常用使用方法

    淺談fastjson的常用使用方法

    下面小編就為大家?guī)硪黄獪\談fastjson的常用使用方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-08-08
  • 詳解在Java的Struts2框架中配置Action的方法

    詳解在Java的Struts2框架中配置Action的方法

    這篇文章主要介紹了詳解在Java的Struts2框架中配置Action的方法,講解了包括struts.xml中的action配置及基于注解方式Action配置的兩個(gè)方式,需要的朋友可以參考下
    2016-03-03
  • 圖解Java經(jīng)典算法冒泡選擇插入希爾排序的原理與實(shí)現(xiàn)

    圖解Java經(jīng)典算法冒泡選擇插入希爾排序的原理與實(shí)現(xiàn)

    冒泡排序是一種簡(jiǎn)單的排序算法,它也是一種穩(wěn)定排序算法。其實(shí)現(xiàn)原理是重復(fù)掃描待排序序列,并比較每一對(duì)相鄰的元素,當(dāng)該對(duì)元素順序不正確時(shí)進(jìn)行交換。一直重復(fù)這個(gè)過程,直到?jīng)]有任何兩個(gè)相鄰元素可以交換,就表明完成了排序
    2022-09-09
  • Dubbo+Nacos服務(wù)啟動(dòng)報(bào)錯(cuò),返回unknown user的問題

    Dubbo+Nacos服務(wù)啟動(dòng)報(bào)錯(cuò),返回unknown user的問題

    這篇文章主要介紹了Dubbo+Nacos服務(wù)啟動(dòng)報(bào)錯(cuò),返回unknown user的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • spring boot如何使用AOP統(tǒng)一處理web請(qǐng)求

    spring boot如何使用AOP統(tǒng)一處理web請(qǐng)求

    這篇文章主要介紹了spring boot如何使用AOP統(tǒng)一處理web請(qǐng)求,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • SpringBoot項(xiàng)目的測(cè)試類實(shí)例解析

    SpringBoot項(xiàng)目的測(cè)試類實(shí)例解析

    這篇文章主要介紹了SpringBoot項(xiàng)目的測(cè)試類實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12

最新評(píng)論