天天看点

【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();
       }
}