程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java:掉效的private潤飾符

Java:掉效的private潤飾符

編輯:關於JAVA

Java:"掉效"的private潤飾符。本站提示廣大學習愛好者:(Java:"掉效"的private潤飾符)文章只能為提供參考,不一定能成為您想要的結果。以下是Java:"掉效"的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++紛歧樣。

深刻Java細節的書本

Java編程思惟
Sun 公司焦點技巧叢書:Effective Java中文版
深刻懂得Java虛擬機:JVM高等特征與最好理論

以上就是對Java private 潤飾符的材料整頓,後續持續彌補相干材料,感謝年夜家對本站的支撐!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved