天天看點

【Java程式設計思想筆記】第六章-通路權限控制

  • 要學會把變動的代碼與保持不變的代碼區分開來。
  • 如果有必要,你盡可能将一切方法都定為private。
  • 非public類在其它包中是通路不到的。
  • 所有預設包的類都是屬于同一個包,盡管它們在不同的檔案夾下面。
  • private,隻允許本類所有對象可通路,其他任何類都不能通路,哪怕是它的子類或同一包中的類都不行。
  • 如果一類繼承自不同包中的類,則該子類不能繼承與通路父類中的“包通路權限或預設通路權限”的屬性與方法,隻能繼承與通路protected或public的屬性與方法。
  • 設計一個類時一般按照public、protected、預設、private的順序來定義屬性與方法,這樣讓人最先注意到公開的,當然,我們一般把接口與實作分離更好,這樣使用者隻關心他所需要的接口,而對實作不必理會。
  • 控制對成員的通路權限有兩個原因:第一是為了使使用者不必關心那此不必關心的private部分,而隻關心類提供了哪些服務接口,即public修飾的部分。第二就是讓類庫設計者可以更改類的内部工作方式,而不必擔心這樣會客戶程式産生重大的影響。

關于protected修飾符

定義規則前,我這裡約定有三個類,一個是Base類,一個是Base類的子類Sub類,一個是Sub類的子類SubSub類,另一個是Other類且與Base、Sub、SubSub沒有繼承關系,并假設Base中有protected方法與屬性,都叫YYY吧。

  • 先看看protected規則:首先要搞清楚什麼叫通路?這裡在講到的通路是有二種的:
    • 一、就是在類中通過“XXX x = new XXX(); x.YYY;”的形式來通路(不妨叫此種形式為“外部通路”吧,此種通路形式除了可以應用到自己與子類中外,還可以應用在其他類中通路,其中XXX表示定義的類型,這裡可為Base與Sub、SubSub,YYY為方法或屬性);
    • 二、就是this.YYY的形式來通路(不妨叫此種形式為“内部通路”吧,不過這種通路形式隻能應用在在自己的類或是子類中)。
  • protected方法與屬性可通路的地方有三個:
  • 1.在自己的類Base中:上面的“XXX x = new XXX(); x.YYY;”與“this.YYY”兩種通路形式都可以通路的到自己定義的portected方法或屬性;
  • 2.二是子類Sub、SubSub中,這要分三種通路方式:
    • a.在Sub、SubSub 中的“this.YYY”内部通路形式:在此種方式形式下,不管是否重寫或重新定義過父類Base中protected方法與屬性,子類Sub、SubSub一定可以通路的。
    • b.在Sub、SubSub 中“

      Base x = new XXX (); x.YYY;

      ”外部通路形式:此種形式就不一定的能通路的到了,這要看父類Base與子類Sub、SubSub是否在同一包(注意,此時與是否重寫或重新定義過這些protedted方法與屬性沒有關系);
    • c.在SubSub 中“

      Sub x = new XXX (); x.YYY;

      ” 外部通路形式:此種通路形式能否通路關鍵看Sub是否重寫或重新定義過Base的屬性與方法:

      i.如果重寫或重新定義過,則看Sub與SubSub是否在同包中

      ii.如果沒有,則看Base與SubSub是否在同包中

  • 3.在其他類Other中:此時隻支援外部通路形式,不過到底是要求Other與Base同包還是要求Other與Sub同包,則要依你通路方式而定了:
    • a.如果是通過父類引用“

      Base x = new XXX (); x.YYY;

      ”形式來通路的,則要求Other與Base同包;
    • b.如果是通過子類引用“

      Sub x = new Sub (); x.YYY;

      ”形式來通路的,情況又會比較複雜了,此時關鍵是看子類Sub是否重寫或重新定義過父類Base中的protected方法與屬性:

      i.如果重寫或重新定義過了,則要求Other與Sub同包即可;

      ii.如果沒有重寫或重新定義過了,則要求Other與Base同包即可;

規則總結肯定有遺漏的地方,不過我隻想到這些,希望大家一起看看。看文字比較繞,下面來應用上面的規則看看這些執行個體也許會好了解一些:

package pk1.a;
public class Base {
       protected int i = 1;
       protected void protect() {
              System.out.println("Base::protect");
       }
}           
package pk1.a;
import pk1.b.Sub;
public class SubSub extends Sub {
       void g() {
              Sub s = new SubSub();
              //!! s.protect();//規則2.c.i
              System.out.println(s.i);//規則2.c.ii
       }
}           
package pk1.b;
import pk1.a.Base;
public class Sub extends Base {
       private void prt() {}
       protected void protect() {
              System.out.println("Base::protect");
       }
       void f() {
              //規則2.a
              this.protect();
              this.i = 2;

              //規則2.b
              Base a2 = new Sub();
              //!! a2.protect();
              //!! System.out.println(a2.i);

              //規則1
              Sub b = new Sub();
              b.protect();
              b.i = 1;
              b.prt();
       }
}           
package pk1.b;
public class SubSub extends Sub {
       void g() {
              Sub s = new SubSub();
              s.protect();//規則2.c.i
              //!! System.out.println(s.i);//規則2.c.ii
       }
}           
package pk1.c;
import pk1.a.Base;
import pk1.b.Sub;
public class SubSub extends Sub {
       void g() {
              this.protect();//規則2.a

              //規則2.b
              Base b = new SubSub();
              //!! b.protect();
              //!! System.out.println(b.i);

              //規則2.b
              Sub s = new SubSub();
              //!! s.protect();
              //!! System.out.println(s.i);

       }
}           
package pk2.a;
public class Base {
       protected int i = 1;

       protected void protect() {
              System.out.println("Base::protect");
       }
}           
package pk2.a;
import pk2.b.Sub;
public class Other {
       void g() {
              //規則3.a
              Base b = new Sub();
              b.protect();
              System.out.println(b.i);

              //規則3.b.ii
              Sub s = new Sub();
              s.protect();
              System.out.println(s.i);
       }
}           
package pk2.b;
import pk2.a.Base;
public class Other {
       void g() {
              //規則3.a
              Base b = new Sub();
              //!! b.protect();
              //!! System.out.println(b.i);

              //規則3.b.ii
              Sub s = new Sub();
              //!! s.protect();
              //!! System.out.println(s.i);
       }
}           
package pk2.b;
import pk2.a.Base;
public class Sub extends Base {}           
package pk3.a;
import pk3.b.Sub;
public class Base {
       protected int i = 1;
       protected void protect() {
              System.out.println("Base::protect");
       }

       static protected int i_s = 1;
       static protected void protect_s() {
              System.out.println("Static:Base::protect");
       }

       void f() {
              //!! Sub.i_s = 2; //規則3.b.i
              Sub.protect_s();//規則3.b.ii
       }
}           
package pk3.a;
import pk3.b.Sub;
public class Other {
       void g() {
              Sub s = new Sub();
              //!! s.protect();//規則3.b.i
              System.out.println(s.i);//規則3.b.ii
       }

       void f() {

              //!! Sub.i_s = 2; //規則3.b.i
              Sub.protect_s();//規則3.b.ii

              Base.i_s = 2;//規則3.a
              Base.protect_s();//規則3.a

       }
}           
package pk3.b;
import pk3.a.Base;
public class Other {
       void f() {
              Sub.i_s = 2;//規則3.b.i
              //!! Sub.protect1();//規則3.b.ii

              //!! Base.i1 = 2;//規則3.a
              //!! Base.protect1();//規則3.a
       }
}           
package pk3.b;
import pk3.a.Base;
public class Sub extends Base {
       protected void protect() {
              System.out.println("Base::protect");
       }
       static protected int i_s = 2;

       void f() {

              /*
               * 在子類中可能通過子類類型或父類類型來來通路父類中protected靜态
               * 成員,而不管子類與父類是否在同一包中,或是子類重新定義了這些成員
               *
               * 注,在父類或子類中通路時後面的規則不再适用
               */
              System.out.println(Sub.i_s);//2
              Sub.protect_s();

              System.out.println(Base.i_s);//1
              Base.protect_s();
       }
}           
  • 最後,通過上面的規我們可以很好的解釋

    Object.clone()

    類似的問題:

    Object.clone()

    通路修飾符為protected,如果某個類沒有重寫此方法,則Object中的clone()方法除被自己與子類能調用方法外,其他不管與這個類在同一包還是不同包都是不可見的,因為未重寫,還是屬于Object中的方法,又Object在java.lang包中,與我們定義的包又不在java.lang包中,是以不能通路到(這也與你在在程式裡定義了

    Object o = new Object();

    你還是不能在目前類中調用o.clone();一樣)。是以如果要能被不同包中的非子類克隆,則需重寫Object.clone()并設定通路權限為public(如果重寫後還是protected,則還是隻能被同一包通路)。
package a;
public class A {
       protected void f() {}

       void g() {}

       private void p(A a) {}

       private void m(A a) {
              p(a);
       }

       public static void main(String[] args) {
              A a = new A();
              a.m(a);
       }
}           
package b;
import a.A;
public class B extends A {
       protected void f() {}

       private void p() {}

       public void call1(B b) {
              b.f();// 此種通路方式屬于通路同類中方式,是沒有問題的
              f();// 屬于通路自已的方法        
              // !! g();// 父類中的包通路權限方法不能被子類繼承通路
              b.p();//在同一類中是可以通路私有方法的,通路控制是針對整個類來說的,而不是某個對象
       }

       public void call12(A a) {
              /*
               * 即使是f()是父類中的protected方法,并且子類還重寫過了 ,但
               * 是這種以父類類型執行個體通路父類中的protected方式屬于不同包非
               * 子 類通路方式,是以不能通路,即使通路的代碼在子類中!
               */
              // !! a.f();
              A a1 = new B();
              // 此種通路與上面是一樣也不能編譯
              // !! a1.f();


       }

       public static void main(String[] args) {
              B b = new B();
              b.f();

              A a = new B();
              // 與上面 call2方法中的調用一樣,也是不能通路的
              // !! a.f();
       }
}