关于JAVA中四种权限修饰词的思考和总结

        本来我以为我已经理解了JAVA里面四种权限词及其用法了,结果前几天写实验的时候,才发现原来自己很多细节根本没有去思考过,更别说搞懂了。于是在查阅资料博客后,以这个为主题写一篇博客,总结一下。

JAVA中有四种修饰词:Public,Protected,default,private。

        

java public修饰 java中的修饰词_父类

一、public

        public关键词修饰的类、属性或者方法,是在何时何地都可以被任意访问到。这个很好理解,就是完全公开的权限,任人访问。

        但是在我们设计一个ADT的时候,往往不能把所有方法或属性简单地设置成为public的。如果全部设成public的话,会破坏一个模块的独立性,增加各个模块间的耦合度,并且还会导致表示泄露的问题,破坏了表示独立性。

因此,原则上来讲,为了表示的内部封装,所有的属性都不能设置为public,而要根据继承时的权限设置为另外三种。而为了模块之间的独立性,一般只需要把实现接口中的方法设置为public,暴露给外部,而其他只有内部方法实现时使用的方法应该设置为其他权限词,比如protected或者private。

二、protected

  • 基类的 protected 成员是包内可见的,并且对子类可见;
  • 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。、、

        用protected修饰的属性或者方法,可以对子类可见。不论是在同一个包下或不同包下的子类,都可以访问到。

        刚开始我只记住这个结论,但是由于JAVA编程经验的匮乏,并没有深入去思考这个问题。现在讲一下我目前的认识。

        用protected修饰的属性或者方法,对于子类是可见的。也就是说,子类知道有这么个东西,可以看得见摸得着,那么进而就可以去使用了。这里的使用是指,可以在子类的方法里去调用或者去重写。具体我们在最后进行对比。

三、default

        default修饰词是只有同一个包内可见。一个文件中,只能有一个类是public向外暴露的,而其他类都只能设定为default,设置为protected和private会报错!

java public修饰 java中的修饰词_System_02

java public修饰 java中的修饰词_子类_03

        用default修饰的类或者方法,可以看作是一个包的内部专属。我的理解是用于实现一个包内的内部辅助类。由于一个模块应该是高度聚合的,所以一个包内的各种类应该更倾向于使用相同的辅助类,这样设计也可以提高代码的复用性。

        而且我们看到,对于类的修饰只有public和default俩种,protected和private只可以修饰属性和方法。当然想一想也挺合理的,private是私有的意思,一个类如果是私有的,那么它属于谁呢?属于包吗?这不就是default的功能嘛。同理protected,类的上一层是包,而包和包之间没有继承的关系,自然也不需要protected权限。

四、private

        private是最狭隘的权限修饰词,用private修饰的属性或方法只有本类中可以看到,哪怕是子类中,也是看不到的。

        怎么理解这个看不到呢?可以这样想,如果一个子类继承了一个父类,他能够看到的是父类中public和protected修饰的属性或者方法,进而可以在自己声明的方法中访问或者来调用。而对于private属性的变量或方法,子类根本不知道它的存在,无法直接在自己扩展的方法中访问或调用。

        注意我这里说的是“直接”!下面我们详细讨论一下private和protected在子类父类继承关系中的差异,下面是本篇中的精华所在!


一 属性的protected和private区别

        先看protected:

        

public class Main {
    public static void main(String[] args) {
    A a=new A();
    System.out.println("父类中方法:");
    a.printProtectedValue();
    B b=new B();
    System.out.println("子类中方法:");
    b.printProtectedValue();
    }
    public void test(){
    }
}

class A {
    protected String a="父亲";
    public A(){
    }
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

class B extends A{
    protected String a="孩子";
    public B(){
        super();
    }
}

 在父类中我们声明了一个protected属性的变量,然后在子类中对该变量重新赋值,用父类中的同一个方法进行访问,查看得到的结果:

java public修饰 java中的修饰词_子类_04

那如果调用的方法是子类中的呢?

向子类中重写同名方法:

class B extends A{
    protected String a;
    public B(){
        super();
        a="孩子";
    }
    @Override
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

输出结果如下:

java public修饰 java中的修饰词_子类_05

为了进一步讨论,在子类中同时也调用父类的方法:

public class Main {
    public static void main(String[] args) {
    A a=new A();
    System.out.println("父类中方法:");
    a.printProtectedValue();
    B b=new B();
    System.out.println("子类中方法:");
    A c=new B();
    b.printProtectedValue();
    System.out.println("谁的方法呢?");
    c.printProtectedValue();
    }
    public void test(){
    }
}

class A {
    protected String a;
    public A(){
        a="父亲";
    }
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

class B extends A{
    protected String a;
    public B(){
        super();
        a="孩子";
    }
    @Override
    public void printProtectedValue(){
        super.printProtectedValue();
        System.out.println(this.a);
    }
}

得到如下结果:

java public修饰 java中的修饰词_java_06

那子类访问父类的属性会怎样呢?

重写子类中方法如下:

@Override
    public void printProtectedValue(){
        super.printProtectedValue();
        System.out.println(this.a);
        System.out.println(super.a);
    }

得到结果:

java public修饰 java中的修饰词_System_07

欸,发现在子类中,似乎有俩个a属性,分别是继承于父类的,还有在子类中定义的! 

 到现在我们可以得到一个大致的结论了。对于protected属性,如果父类和子类中重复定义了,并不会像方法那样进行重写,而是会同时存在的!在子类中,可以同时存在super.a和this.a俩个属性,并且俩个都可以来访问,这样就相当于这俩个属性其实只是名字一样,本质上是俩个不同的属性。

在父类中定义的方法,访问a属性时,就是父类的a属性(这是当然!),而在子类的方法中,父类的属性需要使用super.a方式来显式地访问,不然会访问子类中的属性。子类同时有父类和子类中属性的访问权限。

好,至此我们已经知道了在继承中,属性并不会发生重写,而是简单的附加。这个应该就类似于链接时俩个函数内的同名静态变量,编译器命名为x.a和y.a,变作了俩个不同名的属性。而修饰词只是界限了访问权限的区别。

那下面我们改变a为private会怎样呢?

java public修饰 java中的修饰词_java public修饰_08

 发现子类中没有父类的a访问权限了,这在我们的意料之中。那我们运行一下呢?

 

public class Main {
    public static void main(String[] args) {
    A a=new A();
    System.out.println("父类中方法:");
    a.printProtectedValue();
    B b=new B();
    System.out.println("子类中方法:");
    A c=new B();
    b.printProtectedValue();
    System.out.println("谁的方法呢?");
    c.printProtectedValue();
    }
    public void test(){
    }
}

class A {
    private String a;
    public A(){
        a="父亲";
    }
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

class B extends A{
    protected String a;
    public B(){
        super();
        a="孩子";
    }
    @Override
    public void printProtectedValue(){
        super.printProtectedValue();
        System.out.println(this.a);
    }
}

 

java public修饰 java中的修饰词_子类_09

 父类方法访问父类属性,子类方法访问子类属性,只不过区别是private中子类没有父类的访问权限了,跟我们的预期一致!

二、方法的protected和private区别

        这里我们改一下顺序,先看private方法。

public class Main {
    public static void main(String[] args) {
    A a=new A();
    System.out.println("父类中方法:");
    a.printPerson();
    B b=new B();
    System.out.println("子类中方法:");
    b.printPerson();
    }
}

class A {
    protected String a="A";
    public A(){
    }
    public void printPerson() {
        System.out.println(getInfo());
    }
    private String getInfo() {
        return "父亲:"+this.a;
    }
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

class B extends A{
    protected String a="B";
    public B(){
        super();
    }
    private String getInfo() {
        return "孩子"+this.a;
    }
    @Override
    public void printPerson() {
        super.printPerson();
        System.out.println(getInfo());
    }
}

可以看到,即使是父类和子类中有同名的方法,但是编译器没有要求加上@override标签,说明子类中看不到父类中的方法,俩个方法间没有任何关系。

然后看一下输出结果:

java public修饰 java中的修饰词_java_10

结果似乎跟属性一样,对于private方法,父类中调用父类方法,子类中调用子类方法。这里我没有单独写一个不重写的printPerson方法,因为预期结果就是只会调用父类中方法。

那么如果方法是protected呢?

 

public class Main {
    public static void main(String[] args) {
    A a=new A();
    System.out.println("父类中方法:");
    a.printPerson();
    B b=new B();
    System.out.println("子类中方法:");
    b.printPerson();
    }
}

class A {
    protected String a="A";
    public A(){
    }
    public void printPerson() {
        System.out.println(getInfo());
    }
    protected String getInfo() {
        return "父亲:"+this.a;
    }
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

class B extends A{
    protected String a="B";
    public B(){
        super();
    }
    @Override
    protected String getInfo() {
        return "孩子:"+this.a;
    }
//    @Override
//    public void printPerson() {
//        super.printPerson();
//        System.out.println(getInfo());
//    }
}

先不对printPerson重写,查看一下结果:

java public修饰 java中的修饰词_父类_11

可以看到,虽然printPerson都是父类中的方法,但是调用的方法却改变了:在子类中,会调用子类重写的方法。有点像动态链接,在运行时决定调用具体的方法,父类的方法被重写了!

 那我们取消最后重写的注释再运行一遍呢?

java public修饰 java中的修饰词_java_12

 可以看到,哪怕是显式地调用父类中方法,也会进而调用重写后的方法。对于protected的方法,父类子类中只能有一个!

如果父类中是private,子类中是protected呢?

public class Main {
    public static void main(String[] args) {
    A a=new A();
    System.out.println("父类中方法:");
    a.printPerson();
    B b=new B();
    System.out.println("子类中方法:");
    b.printPerson();
    }
}

class A {
    protected String a="A";
    public A(){
    }
    public void printPerson() {
        System.out.println(getInfo());
    }
    private String getInfo() {
        return "父亲:"+this.a;
    }
    public void printProtectedValue(){
        System.out.println(this.a);
    }
}

class B extends A{
    protected String a="B";
    public B(){
        super();
    }
    protected String getInfo() {
        return "孩子:"+this.a;
    }
    @Override
    public void printPerson() {
        super.printPerson();
        System.out.println(getInfo());
    }
}

结果如下:

java public修饰 java中的修饰词_父类_13

 这时编译器会提醒你不需要加override注释,并且结果可以看到父类中方法并没有覆盖,说明父类和子类中俩个同名的方法是共存的!

那么如果父类中是protected,子类中是private的呢?????

答案是不允许这种情况!

三、总结

        至此,我们可以对继承关系中protected和private俩种修饰词做一下总结。

        对于属性来讲,可以具有多态性,无论是private还是protected,父类子类中都可以存在同名的属性,以为这他俩在编译时其实被区分为了不同的属性,只不过是编辑时名字一样,在符号表中其实是不一样的名字。而俩种修饰词的区别只是在于访问的权限不同,子类方法无法访问父类的private属性!

        对于方法来讲,不具有多态性。如果一个方法是protected或更高的话,那么父类子类中只能有一个相同的方法,在运行时会动态地决定执行父类还是子类中的方法(这里相同指需要override的方法)。而对于private方法,子类并不能看到父类中相同的方法,故不存在重写的情况,在运行时相当于俩个不同的方法!而且子类中也不能对父类中private方法进行重写。

四、进一步讨论

        这些只是作为一个观察者角度来看java中权限修饰词的表现,如果要深入了解其中机制,需要对JAVA虚拟机进行深一步地了解,目前没有这样的时间,留以以后再去研究~