1. 程式人生 > >Java 訪問權限控制:你真的了解 protected keyword嗎?

Java 訪問權限控制:你真的了解 protected keyword嗎?

可見 讀者 警告 except 控制 知識 繼承關系 shc line

摘要:

  在一個類的內部,其成員(包含成員變量和成員方法)是否能被其它類所訪問,取決於該成員的修飾詞;而一個類是否能被其它類所訪問,取決於該類的修飾詞。Java的類成員訪問權限修飾詞有四類:private,無(默認情況下。包訪問權限),protected 和 public,而當中僅僅有包訪問權限和public才幹修飾一個類(內部類除外)。特別地,非常多的介紹Java的書籍對protected介紹的比較籠統,經常會對大家造成誤解。

因此,本文重點揭示了 protected 關鍵字的內涵和使用方法,並介紹了一些其它的修飾符。


版權聲明:

本文原創作者:書呆子Rico
作者博客地址:http://blog.csdn.net/justloveyou_/


一. Package

  關於包的使用,我們僅僅需註意一點:在一個項目中,不能夠有同樣的兩個包名。也就是說,我們的包名不能和項目中其它的包名反復。這裏不但包含自己定義包名也包含項目所引用的類庫的包名。看以下樣例:

package java.lang;

public class MyObject {
    public static void main(String[] args) throws CloneNotSupportedException {
        Object o = new Object();
        System.out.println(o.hashCode());
    }
}

  我們給自己的程序的包名是 java.lang。其實。我們知道 java.lang 是JDK使用的包名。

程序能夠正常編譯,但當我們執行程序時會有包沖突警告並拋出 “java.lang.SecurityException: Prohibited package name: java.lang” 異常。例如以下圖所看到的。

              技術分享

          技術分享

  此外。我們須要註意:假設我們在程序中使用了Package語句,那麽它必須是文件裏除凝視外第一句程序代碼,否則不能通過編譯。


二. Java訪問權限概述

   在一個類的內部。其成員(包含成員變量和成員方法)是否能被其它類所訪問,取決於該成員的修飾詞。Java的類成員訪問權限修飾詞有四類:private,無(默認情況下,包訪問權限),protected 和 public。

其權限控制例如以下表所看到的:

              技術分享

  特別要註意的是,對於Java中的 類(不是其內部成員,兩者要區分開)其訪問權限修飾詞僅有 public 和 “無”(即包訪問權)兩種。而沒有 private 和 protected(有一個特例,僅僅有內部類能夠是private或protected的,關於內部類進一步了解請見我的博客《Java 內部類綜述》)。

因此,對於非內部類。我們僅僅能賦予其包訪問權限或是 public 。假設你不希望其它不論什麽人對該類擁有訪問權,你能夠把全部的構造器都指定為 private。從而阻止不論什麽人創建該類的對象。

這個時候,該類的對象就僅僅能在其 static 成員內部進行創建。這樣的情形有點像單例模式,比如像以下的樣例那樣:

 class Test {
       // private Constructor!
       private Test() {}
       // Allow creation via static method:
       public static Test getTest() {
           return new Test();
       }
    }

  在上面所提到的四種修飾詞中。除 protected 外,都非常好理解和掌握。我們在此進行簡述:

  • public :被public修飾的類成員能被全部的類直接訪問;

  • private:被public修飾的類成員僅僅能在定義它的類中被訪問,其它類都訪問不到。特別地,我們一般建議將成員變量設為private的,並為外界提供 getter/setter 去對成員變量進行訪問,這樣的做法充分體現了Java面向對象的四大特性(封裝,多態。繼承,抽象)中的封裝思想;

  • 包訪問權限:包訪問權限就是Java中的默認的權限,具有包訪問權限的類成員僅僅能被同一包中的類訪問。

      因為 protected 關鍵字的真正內涵不太easy理解。我們將在下一節專門介紹 protected 關鍵字。


三. protected 關鍵字的真正內涵

  非常多的有關介紹Java語言的書籍 (包含《Java編程思想》)。都對protected介紹的比較的簡單。基本都是一句話。就是:被protected修飾的成員對於本包和其子類可見。

這樣的說法有點太過含糊,經常會對大家造成誤解。對於protected的成員,要分子類和超類是否在同一個包中兩種情況看待,現以 protected方法的調用為例進行說明,protected的成員變量相似。

  實質上,protected方法的調用是否合法(編譯是否通過)關鍵是要看被調用的protected方法從根源上看所在的類相應的包與調用代碼所在的類相應的包是否同樣。若同樣,則合法;否則,不合法。

當然。不管怎樣,子類是能夠訪問繼承而來的屬於它自己的受保護方法的。

  我們能夠看以下樣例進行了解。


1). 第一種情形:子類與基類不在同一個包中

//演示樣例一
package p1;
public class Father1 {
    protected void f() {}   // 父類Father1中的protected方法
}

package p1;
public class Son1 extends Father1 {}

package p11;
public class Son11 extends Father1{}

package p1;
public class Test1 {
    public static void main(String[] args) {
        Son1 son1 = new Son1();
        son1.f(); // Compile OK,protected方法f()來自於Father1類,與 Test1類 在同一包p1中
        son1.clone(); // Compile Error。protected方法clone()來自於Object類,與 Test1類不在同一包中

        Son11 son = new Son11();
        son11.f(); // Compile OK。盡管Son11類在包p11中。但protected方法f()來自於Father1類,與 Test1類在同一包p1中
        son11.clone(); // Compile Error,protected方法clone()來自於Object類,與 Test1類不在同一包中
    }
}

  在上面的演示樣例中,類Father1、Son1 和 Test1 在同一個包p1下,類Son11在包p11下。

可是我們知道,不管Son1類還是Son11類,它們的protected方法f()在根源上都來自於p1包中的類Father1。而因為Test1也在p1包中。因此f()方法對Test1類可見,編譯通過。但因為Son1類和Son11類中的clone()方法在根源上均來自於java.lang包下的類Object,與 Test1類不在同一包中,因此clone()方法對Test1類不可見。編譯不通過。


//演示樣例二

package p2;
class MyObject2 {
protected Object clone() throws CloneNotSupportedException {
       return super.clone();
    }
}

package p22;
public class Test2 extends MyObject2 {
    public static void main(String args[]) {
       MyObject2 obj = new MyObject2();
       obj.clone(); // Compile Error,protected方法clone()來自於MyObject2類,與 Test2類 不在同一包p1中

       Test2 tobj = new Test2();
       tobj.clone();// Complie OK。盡管 protected方法clone()來自於MyObject2類,與 Test2類 不在同一包p1中,但Test2類作為MyObject2類的子類,是能夠訪問繼承而來的屬於它自己的受保護方法的。

} }

  在上面的演示樣例中,類MyObject2 和 類Test2 分別在包 p2 和 p22 下。

因此,在類Test2中通過MyObject2的引用調用MyObject2的protected方法clone()時,因為類MyObject2 和 類Test2 不在同一包中而編譯不通過。

可是我們知道,盡管 類Test2 的protected方法clone()在根源上也來源於 類MyObject2。可是Test2類作為MyObject2類的子類。是能夠訪問繼承而來的屬於它自己的受保護方法的。


//演示樣例三

package p3;
class MyObject3 extends Test3 {
}

package p33;
public class Test3 {
  public static void main(String args[]) {
    MyObject3 obj = new MyObject3();
    obj.clone(); // Compile OK,protected方法clone()來自於Object類,而如今正是在Object的子類Test3類中訪問該方法,所以編通過。註意:
  }
}

  在上面的演示樣例中。類MyObject3 和 類Test3 分別在包 p3 和 p33 下。可是因為 MyObject3類的protected方法clone()在根源上來自於類Object中。而如今正是在Object類的子類Test3類中訪問該方法。因此編譯通過。原理與演示樣例一相似。

須要註意的是,MyObject3 直接繼承於Test3 ,而Test3又直接繼承於Object,並在Test3 中訪問來自於Object類protected方法clone()。與例一不同的是,盡管Son1的protected方法clone()也來自於Object類,類Test1也是類Object的子類,但這三者不是直接繼承關系,因此“son1.clone();“在例一中編譯不通過。


//演示樣例四

package p4;
class MyObject4 extends Test4 {
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

package p44;
public class Test4 {
  public static void main(String args[]) {
    MyObject4 obj = new MyObject4();
    obj.clone(); // Compile Error。protected方法clone()來自於MyObject4類,而Test4類與MyObject4類不在同一個包中
  }
}

  該演示樣例與演示樣例三非常相似,唯一不同的是 類MyObject4 重寫了從 類Test4 中繼承過來的protected方法clone()。這樣,MyObject4 的 protected方法clone()在根源上來自於類本身而非Test4類。而類MyObject4 和 類Test4 又不在同一包下,因此編譯不通過。


2). 另外一種情形:子類與基類在同一個包中

//演示樣例五

package p5;

class MyObject5 {
    protected Object clone() throws CloneNotSupportedException {
       return super.clone();
    }
}
public class Test5 {
    public static void main(String[] args) throws CloneNotSupportedException {
       MyObject5 obj = new MyObject5();
       obj.clone(); // Compile OK,protected方法clone()來自於MyObject5類,而Test5類與MyObject5類又在同一個包中
    }
}

  該演示樣例與演示樣例四非常相似。唯一不同的是 類MyObject5 與 類Test5在同一個包p5中。正因為二者在同一包中,因此編譯通過。


//演示樣例六

package p6;

class MyObject6 extends Test6{}
public class Test6 {
  public static void main(String[] args) {
    MyObject6 obj = new MyObject6();
    obj.clone();        // Compile OK
  }
}

  在本演示樣例中,因為類MyObject中的protected方法clone()從根源上來自於Test6類,而如今正是在 Test6 中調用protected方法clone(),因此編譯通過。


//演示樣例七

package p7;

class MyObject7 extends Test7 {
    public static void main(String[] args) {
        Test7 test = new Test7();
        test.clone(); // Compile Error.
  }
}

public class Test {
}

  在本演示樣例中。盡管類MyObject7與Test7類在同一個包p7中。可是因為 類Test7 的protected方法clone()從根源上來自於 java.lang.Object類。而其又與MyObject7不在同一個包中。因此編譯不通過。


四. 其它的修飾符

static:修飾變量和內部類(不能修飾常規類),當中所修飾變量稱為類變量或靜態變量。靜態變量是和類存在一起的,每一個實例共享這個靜態變量。在類載入時初始化。

final:被聲明為final的變量必須在聲明時給定初值(當然。空白 final 情形除外)。並且被修飾的變量不能改動值。

當修飾類時,該類不能派生出子類;修飾方法時,該方法不能被子類覆蓋。若讀者想對 final 有一個更深刻的了解,請移步我的博文 《Java 繼承、多態與類的復用》。

abstract:修飾類和方法。

當修飾類時,該類不能創建對象。修飾方法時。為抽象方法。

類僅僅要有一個abstract方法,類就必須定義為abstract,但abstract類不一定非要有abstract方法不可。


五. 總結

  在一個類的內部。其成員(包含成員變量和成員方法)是否能被其它類所訪問,取決於該成員的修飾詞;而一個類是否能被其它類所訪問,取決於該類的修飾詞。

Java的類成員訪問權限修飾詞有四類:private。無(默認情況下,包訪問權限)。protected 和 public,而當中僅僅有包訪問權限和public才幹修飾一個類(內部類除外)。特別地。本文重點揭示了 protected 關鍵字的內涵和使用方法,並介紹了一些其它的修飾符。


六. 說明

  在綜述《Java 訪問權限控制:你真的了解 protected 關鍵字嗎?》的過程中,我們涉及到了非常多知識點。當中有一些我們已經在其它博文中專門提到過,因此沒有作很多其它具體的闡述,這裏給出相應的鏈接:

 若讀者想深入了解 Java 內部類,請移步我的博文《Java 內部類綜述》;
 若讀者想深入了解 final關鍵字。請移步我的博文《Java 繼承、多態與類的復用》。


 若讀者想深入了解 Java 克隆,請移步我的博文《 Java String 綜述(下篇)》,本文用一個小節專門闡述了在Java中克隆的原理和使用方式,並揭示了String對象在克隆過程中的特殊性。


引用

JAVA中的protected(具體解釋),以及和clone()方法有關的一些問題
java的訪問權限
Java基礎具體解釋 (一)Java的類成員訪問權限修飾詞(以及類訪問權限)

Java 訪問權限控制:你真的了解 protected keyword嗎?