标题: protected,你真的理解了吗? [打印本页] 作者: 陈振兴 时间: 2012-7-15 17:24 标题: protected,你真的理解了吗? 源于论坛里提出此问题,我看了半个多小时,都没弄明白是什么原因,在ITeye上找到了答案!内容比较长,细心看!
Java中的访问控制修饰符有四个级别,但属protected最复杂。如果问大家,protected修饰符作用,大家会说“在子类与同包中可以访问这个成员”,当然在自己类中也是可的,但你真的理解了吗?不信,先看看下面这些实例,看你是否也是这样想的(其中注释掉的是不能编译的,大家最好把这些代码放在eclipse中再看,不然你会晕的^_^):
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);
}
}
Java代码
package pk2.a;
public class Base {
protected int i = 1;
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;
在理解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同包即可;
另外,写到这里我想到了Object中的clone方法,为什么要求具有克隆能力的类要求实现Cloneable接口与clone方法呢:Object.clone()访问修饰符为protected,如果某个类没有重写此方法,则Object中的clone()方法除被自己与子类能调用方法外,其他不管与这个类在同一包还是不同包都是不可见的,因为未重写,还是属于Object中的方法,又Object在java.lang包中,与我们定义的包又不在java.lang包中,所以不能访问到(这也与你在在程序里定义了Object o = new Object();你还是不能在当前类中调用o.clone();一样),这也恰好符合上面 3.b.ii 这条规则。所以如果要能被不同包中的非子类克隆,则需重写Object.clone()并设置访问权限为public(如果重写后还是protected,则还是只能被同一包访问)。