前言
之前以为Java和C#中访问修饰符差不多一样,后面才了解到还是有些差异,本节只讲解学习Java中访问修饰符一些需要注意的地方或者从概念上不太好理解我们会通过实际例子来说明,若有错误之处,还请批评指正。
访问修饰符
Java默认(Default)访问修饰符权限和C#中类似(在C#中准确的说嵌套在其他类中默认可以为私有,如果直接在命名空间声明的类或结构体可以是public,也可以是internal,但一定不是私有,这里请注意),如果不提供任何访问修饰符,那么该类将具有包访问权限,比如如下在包com.company下创建不带访问修饰符的Person,然后再在该包中创建Person1,在该类中我们可以初始化Person
package com.company; class Person { }
package com.company; public class Person1 { public void InitialPerson(){ new Person(); } }
但是接下来我们再创建一个包com.company1,在其包下通过Person2则不能初始化Person,此时必然会出现编译错误
package com.company1; import com.company.Person; public class Person2 { public void InitialPerson(){ new Person(); } }
对于private私有修饰符则没有太多要讲解的了,除了包含该成员的类外,其他任何类都无法访问此类成员,Java中比较难理解的是protected修饰符, 该访问修饰符无论是Java还是C#主要用来处理继承的概念,凭借我们对C#的理解,这里我们认为是本包任何类以及实现该类的子类(不管子类是否在本包中还是其他包中) ,这种说法是完全正确的吗?子类在同一包中肯定可以访问,我们讨论在不同包中的情况会略显复杂一些,首先我们在包com.company定义Animal类,而在包com.company1中定义Tiger类,同时定义一个访问修饰符为protected的动物叫的方法如下:
package com.company; public class Animal { protected void Shout(){ System.out.println("Animal"); } }
package com.company1; import com.company.Animal; public class Tiger extends Animal { }
接下来我们再在包com.company中定义一个类,然后在该类中定义方法,将上述位于不同包中的Animal和Tiger类作为参数变量,此时可以访问叫的方法
package com.company; import com.company1.Tiger; public class OtherAnimal { public void OtherMethod(Animal animal, Tiger tiger) { animal.Shout(); tiger.Shout(); } }
然后我们再在Tiger子类定义一个方法,无论是通过当前实例引用还是直接通过super关键字调用,都可以调用基类的叫方法
package com.company; import com.company1.Tiger; public class OtherAnimal { public void OtherMethod(Animal animal, Tiger tiger) { animal.Shout(); tiger.Shout(); } }
当然若我们在基类Animal中定义方法通过其基类变量访问叫的方法毫无疑问也是可以的,即使将叫的方法设置为私有的,因为在其基类内部
public void AnimalMethod(Animal animal) { animal.Shout(); }
若我们在子类Tiger中再定义一个方法,将Tiger作为变量传递进去,此时也是可以访问基类的叫方法
public void tigerMethod1(Tiger tiger) { tiger.Shout(); }
已经列举如上诸多情况,那是不是就说明在子类中一定能访问到叫方法呢?当然不是,如下两种情况则是无效的,会出现编译错误。
当在基类包外直接引用基类变量访问无效
我们在子类中再定义一个方法,直接引用基类的变量,然后访问叫方法,此时将无效。因为protected具有包访问权限,使得直接访问基类受保护成员变为私有或者说直接引用基类变量,无法判断其类型,因为可能在运行时是基类中的其他子类型,这么讲是否会更妥当一点。
public void tigerMethod2(Animal animal) { //发生编译错误 animal.Shout(); }
当在子类包中的非子类直接引用子类变量访问无效
我们在子类所在包中再定义一个类,然后引用子类变量访问叫方法,此时将无效。因为子类从其基类继承受保护的成员,此时会使它们对非子类私有:
package com.company1; public class Tiger1 { public void tiger1Method(Tiger tiger){ //发生编译错误 tiger.Shout(); } }
总结
网上有一部分文章对protected的总结是:本包任何类以及实现该类的子类(不管子类是否在本包中还是其他包中),这种说法不能说错误,只能说意思比较隐晦,因为直接读这段话可能就只考虑了在子类中通过子类实例或直接通过关键字super调用基类的受保护的成员,而可能会欠缺对非子类和直接通过基类引用受保护的成员的考虑。 protected修饰符无论是Java亦或是C#具有两层概念访问权限的组合,一是基于程序集(C#)、包(Java)权限、二是继承权限。所以对于Java的protected访问权限可总结为:本包任何类以及实现该类的子类,无论子类位于本包还是不同包,但对基类外部包直接访问私有,同时对外部包中非子类私有。