轉載自:https://www.cnblogs.com/tuhooo/p/9048761.html
首先來一張圖:
對於一個類而言訪問控制符只有一個public和默認無修飾符。其他的幾個訪問修飾符對於變量和方法都可以使用。
下面介紹具體的使用。
1. 公有訪問控制符(public)
Java的類是通過包的概念來組織的,包是類的一個松散的集合。處於同一個包中的類可以不需要任何說明二方便地相互訪問和引用,而對於不同包中的類,則不行。
但如果一個類被聲明為public時,它就具有了被其他包中的類訪問的可能性,只要這些其他包中的類在程序中使用了import語句引入了public類,就可以訪問和引用這個類。
每個Java程序的主類必須是public類,也是基於相同的原因。
用public修飾的類變量稱為公共變量。如果公共變量屬於公共類,則它能被所有的其他類所引用。public修飾符會造成安全性的數據封裝性下降,所以一般減少public域的使用。
2. 私有訪問控制符(private)
用private修飾的變量或方法只能被該類自身所訪問和修改,而且不能被其他任何類(包括該類的子類)來獲取和引用。private修飾符用來聲明那些類的私有成員,它提供了最高的保護級別。
3. 保護訪問控制符(protected)
用protected修飾的成員變量可以被3種類所引用:該類自身、與它在同一個包中的其他類、在其他包中該類的子類。使用protected修飾符的主要作用是允許其他包中該類的子類來訪問父類的特定屬性。
4. 默認訪問控制符
默認訪問控制權規定,該類只能被同一個包中的類訪問和引用,而不可以被其他包中的類使用,這種訪問特性又稱為包訪問性。
同樣道理,類內的變量或方法如果沒有訪問控制符來規定,也就是具有包訪問性。簡單地說,定義在同一個程序中的所有類屬於一個包。
5. 總結
簡單總結一下,按它們訪問范圍由大到小排列如下:
public:任何地方均可訪問
protected:同一包和子類可見
默認:同一包中可見
private:僅該類部可見
6. 問題
6.1 com.tuhooo和com.tuhooo.test這兩個包有什么關系么?
目測沒啥關系,因為包起到的是命名空間的作用,似乎並沒有父包和子包的概念。
6.2 實際用protect這個訪問控制符我用得比較少,倒是看見很多框架源碼中用的是這個protected。
6.3 java中的訪問控制符還是挺好理解的,那么在字節碼層面是如何實現訪問控制符的呢?
7. 訪問控制符的底層探索
這里用一個比較簡單的類Student.java作為示例,在這個類中四種訪問控制符都用到了。
package com.tuhooo.demo.test;public class Student {
private int age;
protected double salary;
public String name;
char sex;
}
通過如下兩種命令,先編譯然后得到底層的字節碼。
javac Student.java javap -verbose Student.class > info.log
Classfile /C:/Users/tuhooo/IdeaProjects/demo/src/com/tuhooo/demo/test/Student.class Last modified 2018-5-16; size 302 bytes MD5 checksum 92caa220b2c7efcdda2a68e7e53c63e7 Compiled from "Student.java" public class com.tuhooo.demo.test.Student minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #3.#18 // java/lang/Object."<init>":()V #2 = Class #19 // com/tuhooo/demo/test/Student #3 = Class #20 // java/lang/Object #4 = Utf8 age #5 = Utf8 I #6 = Utf8 salary #7 = Utf8 D #8 = Utf8 name #9 = Utf8 Ljava/lang/String; #10 = Utf8 sex #11 = Utf8 C #12 = Utf8 <init> #13 = Utf8 ()V #14 = Utf8 Code #15 = Utf8 LineNumberTable #16 = Utf8 SourceFile #17 = Utf8 Student.java #18 = NameAndType #12:#13 // "<init>":()V #19 = Utf8 com/tuhooo/demo/test/Student #20 = Utf8 java/lang/Object { protected double salary; descriptor: D flags: ACC_PROTECTEDpublic java.lang.String name;
descriptor: Ljava/lang/String;
flags: ACC_PUBLICchar sex;
descriptor: C
flags:public com.tuhooo.demo.test.Student();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>"😦)V
4: return
LineNumberTable:
line 3: 0
}
SourceFile: "Student.java"
主版本號(major)下面有兩個字節代表訪問標志(access_flags),這個標志用於識別一些類或接口層次的訪問信息,包括:這個Class是類還是接口;是否定義為public類型;是否定義為abstract類型;如果是類的話是否聲明為final等。具體的標志位以及標志位的含義如下表:
access_flags中一共有16個標志位可以使用,當前只定義了其中的8個,沒有使用到的標志位要求一律為0。以當前的Student類為例,這是一個普通的類,不是接口、枚舉或者注解,被public關鍵字修飾但並沒被聲明為fiana和abstract,並且用了JDK1.2之后的編譯器進行編譯,因此它的ACC_PUBLIC、ACC_SUPER標志應當為真,而上圖中的其他8個標志為假,所以它的access_flags的值應該為:0x0001|0x0020=0x0021。
在大括號中分別描述了除了private之外的3中訪問控制符對應的字段,就是flags對應的。話說為啥要用flags呢,不是一個字段只有一種訪問控制符么?
這里只是趁着熟悉訪問控制符偷看了一下字節碼,不過感覺不深入。還是有幾個問題:
1. 反射的時候為私有屬性設置值的時候是怎么做到的?
2. 運行時怎么進行訪問控制符的檢查的呢?