黑马程序员技术交流社区

标题: 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;  
  
    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 {}  

Java代码  
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();  
    }  
}  



作者: 陈振兴    时间: 2012-7-15 17:25
内容比较长,分开贴了!建议都测试一下!
如果你看到这里,想法与程序一致的话,说明你理解了,如果不理解,那看看我的理解吧:

定义规则前,我这里约定有三个类,一个是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同包即可;


另外,写到这里我想到了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,则还是只能被同一包访问)。

以上这些就是我对protected限制的一种理解,肯定还有遗漏的地方,希望大家给我指出来,我来完善它。如果对你有帮助,请支持一下!

刚刚发现的一个小问题,一并附上《protected,这个错了吗?》

非常感谢大家,看了一下大家的理解,我上面的理解确实比较复杂,这正是因为把继承访问与非继承访问混在一起了就复杂了。其实这么理解就最简单了,还是以那句话,protected修饰的成员只允许子类与包访问!只不过子类可访问是指继承访问,即在子类的实例中直接访问继承过来的protected成员,而不是在子类中通过创建父类实例的方式来访问;而包访问就是指通过“XXX x = new XXX(); x.YYY;”的方式来访问,即通过实例的引用来访问,能不能访问就是看访问的代码所在的类是否与类XXX 在同一包中。但包访问要注意一点的是:如果XXX中只是继承了父类中的protected成员,而没有重写(方法)或重新定义(属性)这些成员,则能不能访问就要看当前访问的代码所在的类是否与类XXX的父类在同一包;另外如果类XXX重写与重新定义过这些protected成员,则就直接看访问代码所在的类与类XXX是否在同一包即可。
最后要注意的是静态的受保护成员比较特殊,因为protected static 成员即使被子类重写(严格的讲不叫重写)或重新定义后,还是会被继承下来,即在子类会有两份这样的static protected成员,只是他们的所属域(类)不同而已,所以在子类中可以通过父类的类型来访问所属于父的这些成员而不管子类与否与父类在同一包中,这与非静态的是不一样的。
作者: 陈振兴    时间: 2012-7-15 17:26
这个就是比较深奥了,只要这个看懂了,你的多态、包的关系就掌握了!
作者: 韦念欣    时间: 2012-7-16 21:08
楼主,你能够使用自己话,总结一下吗?
作者: 陈振兴    时间: 2012-7-17 12:55
本帖最后由 陈振兴 于 2012-7-17 13:12 编辑
韦念欣 发表于 2012-7-16 21:08
楼主,你能够使用自己话,总结一下吗?

发表此贴的初衷!版主可要明察,如果认为这是……那我就不需要公告了,只因人家讲的比透彻,所以才贴的。
不然下次我自个先学会再说吧!省的做
作者: 陈振兴    时间: 2012-7-17 13:00
陈振兴 发表于 2012-7-17 12:55
发表此贴的初衷!

原帖C:\Documents and Settings\user\2.png桌面\
我的回复C:\Documents and Settings\user\桌面\1.png
作者: 陈振兴    时间: 2012-7-17 13:07
陈振兴 发表于 2012-7-17 13:00
原帖
我的回复

C:\Documents and Settings\user\桌面\2.jpg
C:\Documents and Settings\user\桌面\1.jpg







欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2