Java 訪问权限控制:你真的了解 protected keyword吗?

摘要:

  在一个类的内部,其成员(包含成员变量和成员方法)是否能被其它类所訪问,取决于该成员的修饰词;而一个类是否能被其它类所訪问,取决于该类的修饰词。Java的类成员訪问权限修饰词有四类:private,无(默认情况下。包訪问权限),protected 和 public,而当中仅仅有包訪问权限和public才干修饰一个类(内部类除外)。特别地,非常多的介绍Java的书籍对protected介绍的比較笼统,经常会对大家造成误解。

因此,本文重点揭示了 protected 关键字的内涵和使用方法,并介绍了一些其它的修饰符。


版权声明:

本文原创作者:书呆子Rico
作者博客地址:http://blog.csdn.net/justloveyou_/


一. Package

  关于包的使用,我们仅仅需注意一点:在一个项目中,不能够有同样的两个包名。也就是说,我们的包名不能和项目中其它的包名反复。这里不但包含自己定义包名也包含项目所引用的类库的包名。看以下样例:

package java.lang;

public class MyObject {
    public static void main(String[] args) throws CloneNotSupportedException {
        Object o = new Object();
        System.out.println(o.hashCode());
    }
}

  我们给自己的程序的包名是 java.lang。其实。我们知道 java.lang 是JDK使用的包名。

程序能够正常编译,但当我们执行程序时会有包冲突警告并抛出 “java.lang.SecurityException: Prohibited package name: java.lang” 异常。例如以下图所看到的。

              包冲突.png-7kB

          Package.png-46.2kB

  此外。我们须要注意:假设我们在程序中使用了Package语句,那么它必须是文件里除凝视外第一句程序代码,否则不能通过编译。


二. Java訪问权限概述

   在一个类的内部。其成员(包含成员变量和成员方法)是否能被其它类所訪问,取决于该成员的修饰词。Java的类成员訪问权限修饰词有四类:private,无(默认情况下,包訪问权限),protected 和 public。

其权限控制例如以下表所看到的:

              Java訪问权限控制.png-10.7kB

  特别要注意的是,对于Java中的 类(不是其内部成员,两者要区分开)其訪问权限修饰词仅有 public 和 “无”(即包訪问权)两种。而没有 private 和 protected(有一个特例,仅仅有内部类能够是private或protected的,关于内部类进一步了解请见我的博客《Java 内部类综述》)。

因此,对于非内部类。我们仅仅能赋予其包訪问权限或是 public 。假设你不希望其它不论什么人对该类拥有訪问权,你能够把全部的构造器都指定为 private。从而阻止不论什么人创建该类的对象。

这个时候,该类的对象就仅仅能在其 static 成员内部进行创建。这样的情形有点像单例模式,比如像以下的样例那样:

 class Test {
       // private Constructor!
       private Test() {}
       // Allow creation via static method:
       public static Test getTest() {
           return new Test();
       }
    }

  在上面所提到的四种修饰词中。除 protected 外,都非常好理解和掌握。我们在此进行简述:

  • public :被public修饰的类成员能被全部的类直接訪问;

  • private:被public修饰的类成员仅仅能在定义它的类中被訪问,其它类都訪问不到。特别地,我们一般建议将成员变量设为private的,并为外界提供 getter/setter 去对成员变量进行訪问,这样的做法充分体现了Java面向对象的四大特性(封装,多态。继承,抽象)中的封装思想;

  • 包訪问权限:包訪问权限就是Java中的默认的权限,具有包訪问权限的类成员仅仅能被同一包中的类訪问。

      因为 protected 关键字的真正内涵不太easy理解。我们将在下一节专门介绍 protected 关键字。


三. protected 关键字的真正内涵

  非常多的有关介绍Java语言的书籍 (包含《Java编程思想》)。都对protected介绍的比較的简单。基本都是一句话。就是:被protected修饰的成员对于本包和其子类可见。

这样的说法有点太过含糊,经常会对大家造成误解。对于protected的成员,要分子类和超类是否在同一个包中两种情况看待,现以 protected方法的调用为例进行说明,protected的成员变量相似。

  实质上,protected方法的调用是否合法(编译是否通过)关键是要看被调用的protected方法从根源上看所在的类相应的包与调用代码所在的类相应的包是否同样。若同样,则合法;否则,不合法。

当然。不管怎样,子类是能够訪问继承而来的属于它自己的受保护方法的。

  我们能够看以下样例进行了解。


1). 第一种情形:子类与基类不在同一个包中

//演示样例一
package p1;
public class Father1 {
    protected void f() {}   // 父类Father1中的protected方法
}

package p1;
public class Son1 extends Father1 {}

package p11;
public class Son11 extends Father1{}

package p1;
public class Test1 {
    public static void main(String[] args) {
        Son1 son1 = new Son1();
        son1.f(); // Compile OK,protected方法f()来自于Father1类,与 Test1类 在同一包p1中
        son1.clone(); // Compile Error。protected方法clone()来自于Object类,与 Test1类不在同一包中

        Son11 son = new Son11();
        son11.f(); // Compile OK。尽管Son11类在包p11中。但protected方法f()来自于Father1类,与 Test1类在同一包p1中
        son11.clone(); // Compile Error,protected方法clone()来自于Object类,与 Test1类不在同一包中
    }
}

  在上面的演示样例中,类Father1、Son1 和 Test1 在同一个包p1下,类Son11在包p11下。

可是我们知道,不管Son1类还是Son11类,它们的protected方法f()在根源上都来自于p1包中的类Father1。而因为Test1也在p1包中。因此f()方法对Test1类可见,编译通过。但因为Son1类和Son11类中的clone()方法在根源上均来自于java.lang包下的类Object,与 Test1类不在同一包中,因此clone()方法对Test1类不可见。编译不通过。


//演示样例二

package p2;
class MyObject2 {
protected Object clone() throws CloneNotSupportedException {
       return super.clone();
    }
}

package p22;
public class Test2 extends MyObject2 {
    public static void main(String args[]) {
       MyObject2 obj = new MyObject2();
       obj.clone(); // Compile Error,protected方法clone()来自于MyObject2类,与 Test2类 不在同一包p1中

       Test2 tobj = new Test2();
       tobj.clone();// Complie OK。尽管 protected方法clone()来自于MyObject2类,与 Test2类 不在同一包p1中,但Test2类作为MyObject2类的子类,是能够訪问继承而来的属于它自己的受保护方法的。

} }

  在上面的演示样例中,类MyObject2 和 类Test2 分别在包 p2 和 p22 下。

因此,在类Test2中通过MyObject2的引用调用MyObject2的protected方法clone()时,因为类MyObject2 和 类Test2 不在同一包中而编译不通过。

可是我们知道,尽管 类Test2 的protected方法clone()在根源上也来源于 类MyObject2。可是Test2类作为MyObject2类的子类。是能够訪问继承而来的属于它自己的受保护方法的。


//演示样例三

package p3;
class MyObject3 extends Test3 {
}

package p33;
public class Test3 {
  public static void main(String args[]) {
    MyObject3 obj = new MyObject3();
    obj.clone(); // Compile OK,protected方法clone()来自于Object类,而如今正是在Object的子类Test3类中訪问该方法,所以编通过。注意:
  }
}

  在上面的演示样例中。类MyObject3 和 类Test3 分别在包 p3 和 p33 下。可是因为 MyObject3类的protected方法clone()在根源上来自于类Object中。而如今正是在Object类的子类Test3类中訪问该方法。因此编译通过。原理与演示样例一相似。

须要注意的是,MyObject3 直接继承于Test3 ,而Test3又直接继承于Object,并在Test3 中訪问来自于Object类protected方法clone()。与例一不同的是,尽管Son1的protected方法clone()也来自于Object类,类Test1也是类Object的子类,但这三者不是直接继承关系,因此“son1.clone();“在例一中编译不通过。


//演示样例四

package p4;
class MyObject4 extends Test4 {
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

package p44;
public class Test4 {
  public static void main(String args[]) {
    MyObject4 obj = new MyObject4();
    obj.clone(); // Compile Error。protected方法clone()来自于MyObject4类,而Test4类与MyObject4类不在同一个包中
  }
}

  该演示样例与演示样例三非常相似,唯一不同的是 类MyObject4 重写了从 类Test4 中继承过来的protected方法clone()。这样,MyObject4 的 protected方法clone()在根源上来自于类本身而非Test4类。而类MyObject4 和 类Test4 又不在同一包下,因此编译不通过。


2). 另外一种情形:子类与基类在同一个包中

//演示样例五

package p5;

class MyObject5 {
    protected Object clone() throws CloneNotSupportedException {
       return super.clone();
    }
}
public class Test5 {
    public static void main(String[] args) throws CloneNotSupportedException {
       MyObject5 obj = new MyObject5();
       obj.clone(); // Compile OK,protected方法clone()来自于MyObject5类,而Test5类与MyObject5类又在同一个包中
    }
}

  该演示样例与演示样例四非常相似。唯一不同的是 类MyObject5 与 类Test5在同一个包p5中。正因为二者在同一包中,因此编译通过。


//演示样例六

package p6;

class MyObject6 extends Test6{}
public class Test6 {
  public static void main(String[] args) {
    MyObject6 obj = new MyObject6();
    obj.clone();        // Compile OK
  }
}

  在本演示样例中,因为类MyObject中的protected方法clone()从根源上来自于Test6类,而如今正是在 Test6 中调用protected方法clone(),因此编译通过。


//演示样例七

package p7;

class MyObject7 extends Test7 {
    public static void main(String[] args) {
        Test7 test = new Test7();
        test.clone(); // Compile Error.
  }
}

public class Test {
}

  在本演示样例中。尽管类MyObject7与Test7类在同一个包p7中。可是因为 类Test7 的protected方法clone()从根源上来自于 java.lang.Object类。而其又与MyObject7不在同一个包中。因此编译不通过。


四. 其它的修饰符

static:修饰变量和内部类(不能修饰常规类),当中所修饰变量称为类变量或静态变量。静态变量是和类存在一起的,每一个实例共享这个静态变量。在类载入时初始化。

final:被声明为final的变量必须在声明时给定初值(当然。空白 final 情形除外)。并且被修饰的变量不能改动值。

当修饰类时,该类不能派生出子类;修饰方法时,该方法不能被子类覆盖。若读者想对 final 有一个更深刻的了解,请移步我的博文 《Java 继承、多态与类的复用》

abstract:修饰类和方法。

当修饰类时,该类不能创建对象。修饰方法时。为抽象方法。

类仅仅要有一个abstract方法,类就必须定义为abstract,但abstract类不一定非要有abstract方法不可。


五. 总结

  在一个类的内部。其成员(包含成员变量和成员方法)是否能被其它类所訪问,取决于该成员的修饰词;而一个类是否能被其它类所訪问,取决于该类的修饰词。

Java的类成员訪问权限修饰词有四类:private。无(默认情况下,包訪问权限)。protected 和 public,而当中仅仅有包訪问权限和public才干修饰一个类(内部类除外)。特别地。本文重点揭示了 protected 关键字的内涵和使用方法,并介绍了一些其它的修饰符。


六. 说明

  在综述《Java 訪问权限控制:你真的了解 protected 关键字吗?》的过程中,我们涉及到了非常多知识点。当中有一些我们已经在其它博文中专门提到过,因此没有作很多其它具体的阐述,这里给出相应的链接:

 若读者想深入了解 Java 内部类,请移步我的博文《Java 内部类综述》
 若读者想深入了解 final关键字。请移步我的博文《Java 继承、多态与类的复用》


 若读者想深入了解 Java 克隆,请移步我的博文《 Java String 综述(下篇)》,本文用一个小节专门阐述了在Java中克隆的原理和使用方式,并揭示了String对象在克隆过程中的特殊性。


引用

JAVA中的protected(具体解释),以及和clone()方法有关的一些问题
java的訪问权限
Java基础具体解释 (一)Java的类成员訪问权限修饰词(以及类訪问权限)

原文地址:https://www.cnblogs.com/tlnshuju/p/7383494.html