成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

細(xì)話Java:帶你認(rèn)識(shí)"失效"的private修飾符

開發(fā) 后端
在本文中,private表面上看上去失效了,但實(shí)際上是沒(méi)有的,而是在調(diào)用時(shí)通過(guò)間接的方法來(lái)獲取私有的屬性。

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

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

Java內(nèi)部類

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

  1. class OuterClass { 
  2.     class InnerClass{ 
  3.     } 

今天的問(wèn)題和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)部類里面訪問(wèn)外部類的private成員變量或者方法,這是可以的。如下面的代碼實(shí)現(xiàn)。

  1. public class OuterClass { 
  2.   private String language = "en"
  3.   private String region = "US"
  4.  
  5.   public class InnerClass { 
  6.       public void printOuterClassPrivateFields() { 
  7.           String fields = "language=" + language + ";region=" + region; 
  8.           System.out.println(fields); 
  9.       } 
  10.   } 
  11.  
  12.   public static void main(String[] args) { 
  13.       OuterClass outer = new OuterClass(); 
  14.       OuterClass.InnerClass inner = outer.new InnerClass(); 
  15.       inner.printOuterClassPrivateFields(); 
  16.   } 

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

編譯器在搗鬼?

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

OuterClass的反編譯結(jié)果

  1. 15:30 $ javap -c  OuterClass 
  2. Compiled from "OuterClass.java" 
  3. public class OuterClass extends java.lang.Object{ 
  4. public OuterClass(); 
  5.   Code: 
  6.    0:  aload_0 
  7.    1:  invokespecial    #11//Method java/lang/Object."<init>":()V 
  8.    4:  aload_0 
  9.    5:  ldc  #13//String en 
  10.    7:  putfield #15//Field language:Ljava/lang/String; 
  11.    10: aload_0 
  12.    11: ldc  #17//String US 
  13.    13: putfield #19//Field region:Ljava/lang/String; 
  14.    16return 
  15.  
  16. public static void main(java.lang.String[]); 
  17.   Code: 
  18.    0:  new  #1//class OuterClass 
  19.    3:  dup 
  20.    4:  invokespecial    #27//Method "<init>":()V 
  21.    7:  astore_1 
  22.    8:  new  #28//class OuterClass$InnerClass 
  23.    11: dup 
  24.    12: aload_1 
  25.    13: dup 
  26.    14: invokevirtual    #30//Method java/lang/Object.getClass:()Ljava/lang/Class; 
  27.    17: pop 
  28.    18: invokespecial    #34//Method OuterClass$InnerClass."<init>":(LOuterClass;)V 
  29.    21: astore_2 
  30.    22: aload_2 
  31.    23: invokevirtual    #37//Method OuterClass$InnerClass.printOuterClassPrivateFields:()V 
  32.    26return 
  33.  
  34. static java.lang.String access$0(OuterClass); 
  35.   Code: 
  36.    0:  aload_0 
  37.    1:  getfield #15//Field language:Ljava/lang/String; 
  38.    4:  areturn 
  39.  
  40. static java.lang.String access$1(OuterClass); 
  41.   Code: 
  42.    0:  aload_0 
  43.    1:  getfield #19//Field region:Ljava/lang/String; 
  44.    4:  areturn 
  45.  

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

  1. static java.lang.String access$0(OuterClass); 
  2.   Code: 
  3.    0:  aload_0 
  4.    1:  getfield #15//Field language:Ljava/lang/String; 
  5.    4:  areturn 
  6.  
  7. static java.lang.String access$1(OuterClass); 
  8.   Code: 
  9.    0:  aload_0 
  10.    1:  getfield #19//Field region:Ljava/lang/String; 
  11.    4:  areturn 
  12.  

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

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

  1. 15:37 $ javap -c OuterClass\$InnerClass 
  2. Compiled from "OuterClass.java" 
  3. public class OuterClass$InnerClass extends java.lang.Object{ 
  4. final OuterClass this$0
  5.  
  6. public OuterClass$InnerClass(OuterClass); 
  7.   Code: 
  8.    0:  aload_0 
  9.    1:  aload_1 
  10.    2:  putfield #10//Field this$0:LOuterClass; 
  11.    5:  aload_0 
  12.    6:  invokespecial    #12//Method java/lang/Object."<init>":()V 
  13.    9:  return 
  14.  
  15. public void printOuterClassPrivateFields(); 
  16.   Code: 
  17.    0:  new  #20//class java/lang/StringBuilder 
  18.    3:  dup 
  19.    4:  ldc  #22//String language= 
  20.    6:  invokespecial    #24//Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 
  21.    9:  aload_0 
  22.    10: getfield #10//Field this$0:LOuterClass; 
  23.    13: invokestatic #27//Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String; 
  24.    16: invokevirtual    #33//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
  25.    19: ldc  #37//String ;region= 
  26.    21: invokevirtual    #33//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
  27.    24: aload_0 
  28.    25: getfield #10//Field this$0:LOuterClass; 
  29.    28: invokestatic #39//Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String; 
  30.    31: invokevirtual    #33//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
  31.    34: invokevirtual    #42//Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
  32.    37: astore_1 
  33.    38: getstatic    #46//Field java/lang/System.out:Ljava/io/PrintStream; 
  34.    41: aload_1 
  35.    42: invokevirtual    #52//Method java/io/PrintStream.println:(Ljava/lang/String;)V 
  36.    45return 

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

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

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

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

注意:在內(nèi)部類構(gòu)造的時(shí)候,會(huì)將外部類的引用傳遞進(jìn)來(lái),并且作為內(nèi)部類的一個(gè)屬性,所以內(nèi)部類會(huì)持有一個(gè)其外部類的引用。
this$0就是內(nèi)部類持有的外部類引用,通過(guò)構(gòu)造方法傳遞引用并賦值。

  1. final OuterClass this$0
  2.  
  3. public OuterClass$InnerClass(OuterClass); 
  4.   Code: 
  5.    0:  aload_0 
  6.    1:  aload_1 
  7.    2:  putfield #10//Field this$0:LOuterClass; 
  8.    5:  aload_0 
  9.    6:  invokespecial    #12//Method java/lang/Object."<init>":()V 
  10.    9:  return 

小結(jié)

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

這次也失效?

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

  1. public class AnotherOuterClass { 
  2.   public static void main(String[] args) { 
  3.       InnerClass inner = new AnotherOuterClass().new InnerClass(); 
  4.       System.out.println("InnerClass Filed = " + inner.x); 
  5.   } 
  6.  
  7.   class InnerClass { 
  8.       private int x = 10
  9.   } 
  10.  

和上面一樣,使用javap反編譯看一下。不過(guò)這次我們先看一下InnerClass的結(jié)果

  1. 16:03 $ javap -c AnotherOuterClass\$InnerClass 
  2. Compiled from "AnotherOuterClass.java" 
  3. class AnotherOuterClass$InnerClass extends java.lang.Object{ 
  4. final AnotherOuterClass this$0
  5.  
  6. AnotherOuterClass$InnerClass(AnotherOuterClass); 
  7.   Code: 
  8.    0:  aload_0 
  9.    1:  aload_1 
  10.    2:  putfield #12//Field this$0:LAnotherOuterClass; 
  11.    5:  aload_0 
  12.    6:  invokespecial    #14//Method java/lang/Object."<init>":()V 
  13.    9:  aload_0 
  14.    10: bipush   10 
  15.    12: putfield #17//Field x:I 
  16.    15return 
  17.  
  18. static int access$0(AnotherOuterClass$InnerClass); 
  19.   Code: 
  20.    0:  aload_0 
  21.    1:  getfield #17//Field x:I 
  22.    4:  ireturn 
  23.  

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

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

  1. 16:08 $ javap -c AnotherOuterClass 
  2. Compiled from "AnotherOuterClass.java" 
  3. public class AnotherOuterClass extends java.lang.Object{ 
  4. public AnotherOuterClass(); 
  5.   Code: 
  6.    0:  aload_0 
  7.    1:  invokespecial    #8//Method java/lang/Object."<init>":()V 
  8.    4:  return 
  9.  
  10. public static void main(java.lang.String[]); 
  11.   Code: 
  12.    0:  new  #16//class AnotherOuterClass$InnerClass 
  13.    3:  dup 
  14.    4:  new  #1//class AnotherOuterClass 
  15.    7:  dup 
  16.    8:  invokespecial    #18//Method "<init>":()V 
  17.    11: dup 
  18.    12: invokevirtual    #19//Method java/lang/Object.getClass:()Ljava/lang/Class; 
  19.    15: pop 
  20.    16: invokespecial    #23//Method AnotherOuterClass$InnerClass."<init>":(LAnotherOuterClass;)V 
  21.    19: astore_1 
  22.    20: getstatic    #26//Field java/lang/System.out:Ljava/io/PrintStream; 
  23.    23new  #32//class java/lang/StringBuilder 
  24.    26: dup 
  25.    27: ldc  #34//String InnerClass Filed = 
  26.    29: invokespecial    #36//Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 
  27.    32: aload_1 
  28.    33: invokestatic #39//Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I 
  29.    36: invokevirtual    #43//Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 
  30.    39: invokevirtual    #47//Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
  31.    42: invokevirtual    #51//Method java/io/PrintStream.println:(Ljava/lang/String;)V 
  32.    45return 
  33.  

其中這句調(diào)用就是外部類通過(guò)內(nèi)部類的實(shí)例獲取私有屬性x的操作

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

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

其中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.

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

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

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

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

  1. public class PrivateToOuter { 
  2.   Runnable mRunnable = new Runnable(){ 
  3.       private int x=10
  4.       @Override 
  5.       public void run() { 
  6.           System.out.println(x); 
  7.       } 
  8.   }; 
  9.  
  10.   public static void main(String[] args){ 
  11.       PrivateToOuter p = new PrivateToOuter(); 
  12.       //System.out.println("anonymous class private filed= "+ p.mRunnable.x); //not allowed 
  13.       p.mRunnable.run(); // allowed 
  14.   } 

***總結(jié)

在本文中,private表面上看上去失效了,但實(shí)際上是沒(méi)有的,而是在調(diào)用時(shí)通過(guò)間接的方法來(lái)獲取私有的屬性。

Java的內(nèi)部類構(gòu)造時(shí)持有對(duì)外部類的應(yīng)用,C++不會(huì),這一點(diǎn)和C++不一樣。

原文鏈接:http://droidyue.com/blog/2014/10/02/the-private-modifier-in-java/

 

責(zé)任編輯:張偉 來(lái)源: 技術(shù)小黑屋
相關(guān)推薦

2009-08-24 16:49:39

C#修飾符

2009-06-12 14:46:05

static修飾符Java教程

2009-06-12 13:37:47

訪問(wèn)權(quán)限修飾符Java教程

2015-08-18 09:25:11

Java修飾符關(guān)鍵詞

2011-06-02 14:51:07

JAVA修飾符

2023-12-29 09:01:27

SwiftUI視圖修飾符

2009-08-27 11:04:08

C# extern修飾

2009-08-27 13:06:13

C# new修飾符

2009-09-02 17:14:28

C#修飾符

2009-08-21 13:58:06

C# virtual修

2009-08-27 11:12:03

C# abstract

2009-09-02 17:04:35

C# Extern修飾

2009-08-27 11:16:40

C# sealed修飾

2009-09-04 11:06:40

C#訪問(wèn)修飾符

2009-06-19 10:51:39

Scalapackage訪問(wèn)修飾符

2021-08-02 12:33:26

Swift修飾符視圖

2011-06-28 09:29:11

C#修飾符

2010-01-11 18:46:15

VB.NET修飾符

2011-07-20 16:48:22

C++static

2011-07-20 16:57:05

C++const
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: av永久| 欧产日产国产精品视频 | 日日骚视频 | 精品国产精品国产偷麻豆 | 高清久久久 | 国产精品一区视频 | 丝袜一区二区三区 | 国产资源在线播放 | 黄色毛片免费看 | 久久不卡 | 久久久不卡网国产精品一区 | 国产精品久久 | 中文字幕国产一区 | 亚洲欧美日韩电影 | 日本精品在线观看 | 美国av片在线观看 | 在线国产视频 | 欧美成视频 | 午夜网站视频 | 中文在线视频 | 精品一区二区在线观看 | 中文字幕在线免费观看 | 国产成人高清 | aaaaaaa片毛片免费观看 | 日日操网站 | 免费黄色网址视频 | 365夜爽爽欧美性午夜免费视频 | 成人av网站在线观看 | 亚洲毛片| 噜久寡妇噜噜久久寡妇 | 狠狠干网站 | 91精品国产色综合久久 | 亚洲午夜视频 | www.久久.com | 亚洲成人黄色 | 国产精品久久久久久影院8一贰佰 | 日本不卡一区 | 天堂成人国产精品一区 | 国产成人免费视频网站视频社区 | 在线看无码的免费网站 | 亚洲精品大片 |