詳解Java中的訪問控制修飾符(public, protected, default, private)


Java中的訪問控制修飾符已經困惑筆者多時,其中較復雜的情況一直不能理解透徹。今天下定決心,系統、全面地研究Java中的訪問控制修飾符的所有方面,並整理成這篇文章,希望有同樣疑惑的讀者讀完后能有所收獲。如果文章中出現錯誤,歡迎評論指出,共同交流~

 

說在前面:這篇文章只研究Java中訪問控制修飾符聲明類的變量/方法的情況。

 

先拋出結論:

* 成員變量/方法的訪問權限

*                                        private        default        protected        public

* 自己包自己類                          √               √                  √                √

* 自己包別的類                                           √                  √                √

* 別的包別的類有繼承關系②                                            ①               √

* 別的包別的類無繼承關系                                                                 √

①:子類可以繼承,但是不能訪問父類的成員變量/方法(一般來說,可以訪問就可以繼承)。

②:有繼承關系說明訪問對象所在的類是父類。

 

1. 讓我們來看一下Java中訪問控制修飾符的定義。

Java中,可以使用訪問控制符來保護對類、變量、方法和構造方法的訪問。

 

訪問的形式有以下四種:

· 某個類的成員變量訪問某個類的成員變量

· 某個類的成員變量訪問某個類的成員方法

· 某個類的成員方法訪問某個類的成員變量

· 某個類的成員方法訪問某個類的成員方法

ps:以下代碼均以第三種形式為例,其他形式基本一致。

 

根據訪問對象的不同,訪問的方式又可划分為兩大類:

 

· 訪問對象在同一個類,此時可以通過[成員變量/方法的名字]直接訪問。

class A {
    int a = 10;

    void printA() {
        System.out.println(a);
    }
}

printA()要訪問a,因為它們在同一個類,所以可以通過a直接訪問。

 

· 訪問對象在不同類(假設訪問對象在類B),此時可以通過聲明、初始化B的一個對象,通過[對象名.成員變量/方法的名字]進行訪問。

ps:這種情況僅限於成員方法訪問成員變量/方法。

class A {
    void printB() {
        B ob = new B();
        System.out.println(ob.b);
    }
}

class B {
    int b = 10;
}

A中的printB()要訪問B中的b,因為它們不在同一個類,所以可以在printB()中聲明、初始化B的一個對象ob,通過ob.b進行訪問。

 

此外,當訪問對象為靜態變量/方法時,可以通過[訪問對象所在類的類名.成員變量/方法的名字]進行訪問。

class A {
    static int a = 10;

    int doubleA = A.a * 2;

    void printB() {
        System.out.println(B.b);
    }
}

class B {
    static int b = 10;
}

doubleA要訪問a,由於a為靜態變量,因此可以通過A.a進行訪問。

A中的printB()要訪問B中的b,由於b為靜態變量,因此可以通過B.b進行訪問。

 

2.結論中提到了包,我們來看一下Java中包的定義和作用。

為了更好地組織類,Java提供了包機制,用於區別類名的命名空間。

包的作用

  • 1 把功能相似或相關的類或接口組織在同一個包中,方便類的查找和使用。
  • 2 如同文件夾一樣,包也采用了樹形目錄的存儲方式。同一個包中的類名字是不同的,不同的包中的類的名字是可以相同的,當同時調用兩個不同包中相同類名的類時,應該加上包名加以區別。因此,包可以避免名字沖突。
  • 3 包也限定了訪問權限,擁有包訪問權限的類才能訪問某個包中的類。

Java使用包(package)這種機制是為了防止命名沖突,訪問控制,提供搜索和定位類(class)、接口、枚舉(enumerations)和注釋(annotation)等。

關於包的使用方法,請參考Java教程 包(package),在此不詳細贅述。

值得注意的是,import關鍵字引入的是class文件,而非java文件。

 

3.結論中還提到了繼承,我們來看一下Java中繼承的定義。

繼承是java面向對象編程技術的一塊基石,因為它允許創建分等級層次的類。繼承可以理解為一個對象從另一個對象獲取屬性的過程。

關於繼承的細節,請參照Java教程 繼承,在此不詳細贅述。

需要理解的是,子類繼承父類的成員變量/方法時,是先訪問再繼承。因此上面訪問權限的規則同樣適用於繼承。

 

在同一個包里,如果父類的某個成員變量/方法可以被訪問,則該成員變量/方法可以被繼承。即如果在子類成員方法中,聲明、初始化父類的一個對象后,可以通過[對象名.成員變量/方法a]訪問a,則聲明、初始化子類的一個對象后,也一定可以通過[對象名.成員變量/方法a]訪問a。

class A extends B {
  void printB() {
    B ob = new B();
    System.out.println(ob.b);
    A ob2 = new A();
    System.out.println(ob2.b);
  }
}

class B {
    int b = 10;
}

A繼承B,因此A繼承B的成員變量b。由於A在printB()中,聲明、初始化B的一個對象ob后,可以通過ob.b訪問b,則聲明、初始化A的一個對象ob2后,可能通過ob2.b訪問b。(可以訪問則可以繼承)。

 

然而,在不同包里,子類繼承父類時,子類只能訪問父類的public型成員變量/方法,卻能繼承父類的protected和public型成員變量/方法。(請看下面的例子)

 

值得注意的是,子類繼承父類的成員變量/方法,並不意味着這些成員變量/方法存在於子類,因此不能通過[成員變量/方法的名字]直接訪問。可以理解為繼承而來的成員變量/方法進入了子類的異次元(霧)。

當然,如果繼承而來的成員變量/方法被重寫,這些成員變量/方法就存在於子類了,此時可以通過[成員變量/方法的名字]直接訪問。

 

此處不討論多態的情況,請參照Java教程 多態

 

 

回到結論,讓我們來一層層地驗證Java中的訪問控制修飾符。

/* Stark.java */

package winter.is.coming;

public class Stark {
    private boolean ned;
    boolean robb;
    protected boolean sansa;
    public boolean arya;

    void howIsNed() {
        System.out.println(ned);
    }
}

class Snow {
    void whoseBastard() {
        Stark stark = new Stark();
        // System.out.println(stark.ned); 不可訪問
        System.out.println(stark.robb);
    }
}

/* Greyjoy.java */

import winter.is.coming.Stark;

public class Greyjoy extends Stark {
    void betray() {

        Stark stark = new Stark();
        // System.out.println(stark.robb); 不可訪問
        // System.out.println(stark.sansa); 不可訪問

        Greyjoy greyjoy = new Greyjoy();
        // System.out.println(greyjoy.robb); 不可訪問
        System.out.println(greyjoy.sansa);

    }
}

/* Bolton.java */

import winter.is.coming.Stark;

public class Bolton {
    void flay() {
        Stark stark = new Stark();
        System.out.println(stark.arya);
    }
}

 

① 自己包自己類 -- private可訪問

Stark中的howIsNed()可以訪問Stark中private型的ned。

 

② 自己包別的類 -- default可訪問

Snow中的whoseBastard()可以訪問Stark中default型的robb,不可以訪問Stark中private型的ned。

 

③ 別的包別的類有繼承關系 -- protected可繼承,不可訪問

Greyjoy中的betray()可以繼承Stark中protected型的sansa,不可以訪問Stark中protected型的sansa,也不可以繼承和訪問Stark中default型的robb。

 

④ 別的包別的類無繼承關系 -- public可訪問

Bolton中的flay()可以訪問Stark中public型的arya。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM