1. 程式人生 > >JAVA訪問許可權控制(JAVA Access Control)

JAVA訪問許可權控制(JAVA Access Control)

概述

背景

問題(動機、需求)

訪問控制(或隱藏具體實現)與“最初實現並不恰當”有關。 —— 摘自JAVA程式設計思想
1. 程式碼會被重構。最初實現的程式碼並不是最好的(可維護性角度考慮)。
2. 消費者(客戶端程式設計師)需要程式碼在某些方面不變。內在實現可變,但是介面呼叫方式不變,即不破壞消費者的程式碼。

要解決以上辦法,就得知道哪些介面,哪些域已經被消費者使用。

解決辦法

使用許可權控制。

正文

理論知識

訪問許可權修飾詞的等級(Accesss level modifiers)決定了其他類是否可以訪問到某個特殊的變數或者函式可將許可權控制劃分為兩個等級:

一. 最高級別(At the top level)——public或者no-modifier。
如果一個類(top-level)被宣告為public,意味著該類對其它類都可見;如果一個類沒有訪問修飾詞(no-modifier or the default or package-private),那麼這個類只能被同一個包內的其他類訪問

二.成員級別(At the member level)—— public,private,protected,no-modifier。
用修飾詞public和no-modifier修飾成員或top-level class的含義是一樣的;

如果用private修飾成員,則該成員只在同一個類內可見;如果用protected修飾成員,則該成員在包內可見,此外還可被處於任意包內的子類所訪問到

簡言之
1. Visible to the package, the default(no-modifier).
2. Visible to the class only (private).
3. Visible to the world (public).
4. Visible to the package and all subclasses (protected).

看錶理解

Modifier Class Package Subclass World
public Yes Yes Yes Yes
protected Yes Yes Yes No
no modifier Yes Yes No No
private Yes No No No

這張表描述了成員在各個【訪問許可權修飾詞】的修飾下的可見性

  • Class那一列:表示在同一類內,該類總是可以訪問它自己的成員。
  • Package那一列:表示在同一個包內,成員的可見性。
  • Subclass那一列: 表示不同包內,子類可以訪問父類的public、protected成員。
  • World那一列: 表示所有類都可以訪問某個類的public成員。

如果還不懂,可以看下面例子理解。

看例子理解

這裡寫圖片描述

Modifier Alpha Beta AlphaSub Gamma
public Yes Yes Yes Yes
protected Yes Yes Yes No
no modifier Yes Yes No No
private Yes No No No

包結構如下

├─java
    │
    ├─one
    │  └─ Alpha.java
    │  └─ Beta.java
    └─two
        └─ AlphaSub.java
        └─ Gamma.java
package one;
/**
 * 測試同一個類內的成員訪問許可權
 * public > protected > no-modifier > private
 * 結果: 在同一個類內,所有成員都可見
 */
public class Alpha {
    /**成員變數*/
    public String publicStr;
    protected String protectedStr;
    String noModifierStr;
    private String privateStr;
    /**成員函式*/
    public void publicMethod() {}
    protected void protectedMethod() {}
    void noModifierMethod() {}
    private void privateMethod() {}
    /**測試方法*/
    public void test(){
        //成員變數
        System.out.println( this.publicStr );
        System.out.println( this.protectedStr );
        System.out.println( this.noModifierStr );
        System.out.println( this.privateStr );
        //成員函式
        this.publicMethod();
        this.protectedMethod();
        this.noModifierMethod();
        this.privateMethod();
    }
}

package one;
/**
 * 同一個包內,Alpha成員僅private修飾不可見
 */
public class Beta {

    public void test(){
        //Alpha例項能訪問Alpha哪些成員
        Alpha alpha = new Alpha();
        //成員變數
        System.out.println( alpha.publicStr );
        System.out.println( alpha.protectedStr );
        System.out.println( alpha.noModifierStr );
        //成員函式
        alpha.publicMethod();
        alpha.protectedMethod();
        alpha.noModifierMethod();

    }
}


package two;
import one.Alpha;
/**
 * pre-codition: 不同包,但是是Alpha的子類
 * AlphaSub可以訪問Alpha的public、protected成員
 */
public class AlphaSub extends Alpha {
    public void test(){
        //成員變數
        System.out.println( super.publicStr );
        System.out.println( super.protectedStr );
        //成員函式
        super.publicMethod();
        super.protectedMethod();
    }
}

package two;
import one.Alpha;
/**
 * 不同包內,Gamma只能訪問Alpha的public成員
 */
public class Gamma {
    public void test(){
        Alpha alpha = new Alpha();
        //成員變數
        System.out.println( alpha.publicStr );
        //成員函式
        alpha.publicMethod();
    }
}

一些建議

如果介面對外開發,而你又不想因消費方的濫用而出現問題,訪問許可權修飾詞可以幫你
- 使用最嚴格的訪問許可權修飾詞private,除非你有不那麼用的更好的理由。
- 避免成員變數使用public修飾。因為public成員會和你的實現聯絡起來,這不利於你靈活地改變程式碼,而且可能該成員變數還被消費者隨意更改。

注意的地方

  • 父類宣告的方法是public,在子類重寫(Override)的話也必須宣告為public.
  • 父類宣告的方法是protected,如果子類重寫,必須宣告為protected或者public,不能為private。
  • 父類宣告的方法是private,這不存在繼承,是否不用考慮任何限制。
    簡言之就是子類重寫方法的訪問許可權修飾詞必須比父類的等級要低,即要求不能更加嚴格。

訪問許可權修飾詞並不是萬能的

JAVA反射機制可以繞過訪問許可權修飾詞的作用,比如

public class FooBar {

    private String mutableMember;
    public String getMutableMember() {
        return mutableMember;
    }
    public static void main(String[] args) {
        try {
            Field field = FooBar.class.getDeclaredField("mutableMember");
            field.setAccessible(true);
            /**即使mutableMember是private,也可以改變其值*/
            FooBar fooBar = new FooBar();
            field.set(fooBar, "無中生有的值");
            System.out.println(fooBar.getMutableMember());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

output
無中生有的值

參考來源