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

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

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

服務(wù)器之家 - 編程語言 - JAVA教程 - Java:"失效"的private修飾符

Java:"失效"的private修飾符

2020-06-09 11:54技術(shù)小黑屋 JAVA教程

本文主要介紹Java 失效的private修飾符,這里整理了相關(guān)資料說明private 修飾符的作用,如何使用并與C++ 做比較,有興趣的小伙伴可以參考下

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

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

Java內(nèi)部類

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

?
1
2
3
4
class OuterClass {
  class InnerClass{
  }
}

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

第一次失效?

一個我們在編程中經(jīng)常用到的場景,就是在一個內(nèi)部類里面訪問外部類的private成員變量或者方法,這是可以的。如下面的代碼實現(xiàn)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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的反編譯結(jié)果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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中我們并沒有定義這兩個方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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的實例作為參數(shù)。這兩個方法為什么生成呢,有什么作用呢?我們看一下內(nèi)部類的反編譯結(jié)果就知道了。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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 私有屬性。

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
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看上去失效可,實際上并沒有失效,因為當內(nèi)部類調(diào)用外部類的私有屬性時,其真正的執(zhí)行是調(diào)用了編譯器生成的屬性的靜態(tài)方法(即acess$0,access$1等)來獲取這些屬性值。這一切都是編譯器的特殊處理。

這次也失效?

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

?
1
2
3
4
5
6
7
8
9
10
11
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é)果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)了,編譯器又自動生成了一個獲取私有屬性的后門方法access$0一次來獲取x的值。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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)部類的實例獲取私有屬性x的操作

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

再來個總結(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è)定成了私有修飾符,當且僅當其外部類訪問時是允許的。

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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表面上看上去失效了,但實際上是沒有的,而是在調(diào)用時通過間接的方法來獲取私有的屬性。

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

深入Java細節(jié)的書籍

Java編程思想
Sun 公司核心技術(shù)叢書:Effective Java中文版
深入理解Java虛擬機:JVM高級特性與最佳實踐

以上就是對Java private 修飾符的資料整理,后續(xù)繼續(xù)補充相關(guān)資料,謝謝大家對本站的支持!

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲国产精品自在现线让你爽 | 久久久精品成人免费看 | 免费超级乱淫视频播放性 | 日韩精品一区二区三区毛片 | 嫩草蜜桃| free性丰满hd性欧美厨房 | 五月天婷婷亚洲 | 全黄一级裸片视频免费 | 亚洲H成年动漫在线观看不卡 | 久久久久999| 日本视频在线免费看 | 国产成人亚洲综合网站不卡 | 日韩免费观看 | 国产成+人+综合+亚洲不卡 | 胸大的姑娘中文字幕视频 | 亚洲狼人综合干 | 青苹果乐园影院在线播放 | 亚洲第一色视频 | 欧美一区二区三区免费高 | 交换余生在线播放免费 | 国产男技师 | 亚洲高清国产拍精品影院 | 日本一区免费观看 | 日本伊人久久 | 精品久久久久久综合网 | dasd817黑人在线播放 | 日韩毛片大全免费高清 | 国产香蕉97碰碰在线视频 | 毛片亚洲毛片亚洲毛片 | 欧美日韩国产一区二区三区不卡 | 波多野结衣在线观看中文字幕 | 日韩免费在线观看 | 美女扒开胸罩露出奶了无遮挡免费 | 国产精品福利一区二区亚瑟 | 丁香婷婷在线视频 | 亚洲精品国产乱码AV在线观看 | 护士伦理片 | 6080伦理久久精品亚洲 | 国产精品露脸国语对白99 | 成人18网址在线观看 | 免费观看国产精品 |