protected,你真的理解了吗?

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);

	}
}

 

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

 

如果你看到这里,想法与程序一致的话,说明你理解了,如果不理解,那看看我的理解吧:

 

定义规则前,我这里约定有三个类,一个是Base类,一个是Base类的子类Sub类,一个是Sub类的子类SubSub类,另一个是Other类且与BaseSubSubSub没有继承关系,并假设Base中有protected方法与属性,都叫YYY吧。

 

在理解protected规则:首先要搞清楚什么叫访问?这里在讲到的访问是有二种的:

一、就是在类中通过“XXX x = new XXX(); x.YYY;”的形式来访问(不妨叫此种形式为“外部访问”吧,此种访问形式除了可以应用到自己与子类中外,还可以应用在其他类中访问,其中XXX表示定义的类型,这里可为BaseSubSubSubYYY为方法或属性);

二、就是this.YYY的形式来访问(不妨叫此种形式为“内部访问”吧,不过这种访问形式只能应用在在自己的类或是子类中)。

 

protected方法与属性可访问的地方有三个:

1.         在自己的类Base中:上面的“XXX x = new XXX(); x.YYY;”与“this.YYY”两种访问形式都可以访问的到自己定义的portected方法或属性;

2.         二是子类SubSubSub中,这要分三种访问方式:

        a.         SubSubSub 中的“this.YYY”内部访问形式:在此种方式形式下,不管是否重写或重新定义过父类Baseprotected方法与属性,子类SubSubSub一定可以访问的。

        b.         SubSubSub 中“Base x = new XXX (); x.YYY;”外部访问形式:此种形式就不一定的能访问的到了,这要看父类Base与子类SubSubSub是否在同一包(注意,此时与是否重写或重新定义过这些protedted方法与属性没有关系);

        c.         SubSub 中“Sub x = new XXX (); x.YYY; 外部访问形式:此种访问形式能否访问关键看Sub是否重写或重新定义过Base的属性与方法:

                      i.              如果重写或重新定义过,则看SubSubSub是否在同包中

                      ii.             如果没有,则看BaseSubSub是否在同包中

3.         在其他类Other中:此时只支持外部访问形式,不过到底是要求OtherBase同包还是要求OtherSub同包,则要依你访问方式而定了:

        a.         如果是通过父类引用“Base x = new XXX (); x.YYY;”形式来访问的,则要求OtherBase同包;

        b.         如果是通过子类引用“Sub x = new Sub (); x.YYY;”形式来访问的,情况又会比较复杂了,此时关键是看子类Sub是否重写或重新定义过父类Base中的protected方法与属性:

                      i.              如果重写或重新定义过了,则要求OtherSub同包即可;

                      ii.             如果没有重写或重新定义过了,则要求OtherBase同包即可;

 

 

另外,写到这里我想到了Object中的clone方法,为什么要求具有克隆能力的类要求实现Cloneable接口与clone方法呢:Object.clone()访问修饰符为protected,如果某个类没有重写此方法,则Object中的clone()方法除被自己与子类能调用方法外,其他不管与这个类在同一包还是不同包都是不可见的,因为未重写,还是属于Object中的方法,又Objectjava.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成员,只是他们的所属域(类)不同而已,所以在子类中可以通过父类的类型来访问所属于父的这些成员而不管子类与否与父类在同一包中,这与非静态的是不一样的。

 

 补充:方法能重写,属性能重写吗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值