摘要:

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

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


版权声明:

本文原创作者:书呆子Rico

作者博客地址:http://blog.csdn.net/justloveyou_/


一. Package

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

  1. package java.lang;
  2. public class MyObject {
  3. public static void main(String[] args) throws CloneNotSupportedException {
  4. Object o = new Object();
  5. System.out.println(o.hashCode());
  6. }
  7. }

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

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

              

          

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


二. Java訪问权限概述

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

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

             

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

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

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

  1. class Test {
  2. // private Constructor!
  3. private Test() {}
  4. // Allow creation via static method:
  5. public static Test getTest() {
  6. return new Test();
  7. }
  8. }

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

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

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

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

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


三. protected 关键字的真正内涵

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

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

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

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

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


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

  1. //演示样例一
  2. package p1;
  3. public class Father1 {
  4. protected void f() {} // 父类Father1中的protected方法
  5. }
  6. package p1;
  7. public class Son1 extends Father1 {}
  8. package p11;
  9. public class Son11 extends Father1{}
  10. package p1;
  11. public class Test1 {
  12. public static void main(String[] args) {
  13. Son1 son1 = new Son1();
  14. son1.f(); // Compile OK,protected方法f()来自于Father1类,与 Test1类 在同一包p1中
  15. son1.clone(); // Compile Error。protected方法clone()来自于Object类,与 Test1类不在同一包中
  16. Son11 son = new Son11();
  17. son11.f(); // Compile OK。尽管Son11类在包p11中。但protected方法f()来自于Father1类,与 Test1类在同一包p1中
  18. son11.clone(); // Compile Error,protected方法clone()来自于Object类,与 Test1类不在同一包中
  19. }
  20. }

  在上面的演示样例中,类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类不可见。编译不通过。


  1. //演示样例二
  2. package p2;
  3. class MyObject2 {
  4. protected Object clone() throws CloneNotSupportedException {
  5. return super.clone();
  6. }
  7. }
  8. package p22;
  9. public class Test2 extends MyObject2 {
  10. public static void main(String args[]) {
  11. MyObject2 obj = new MyObject2();
  12. obj.clone(); // Compile Error,protected方法clone()来自于MyObject2类,与 Test2类 不在同一包p1中
  13. Test2 tobj = new Test2();
  14. tobj.clone();// Complie OK。尽管 protected方法clone()来自于MyObject2类,与 Test2类 不在同一包p1中,但Test2类作为MyObject2类的子类,是能够訪问继承而来的属于它自己的受保护方法的。
  15. }
  16. }

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

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

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


  1. //演示样例三
  2. package p3;
  3. class MyObject3 extends Test3 {
  4. }
  5. package p33;
  6. public class Test3 {
  7. public static void main(String args[]) {
  8. MyObject3 obj = new MyObject3();
  9. obj.clone(); // Compile OK,protected方法clone()来自于Object类,而如今正是在Object的子类Test3类中訪问该方法,所以编通过。注意:
  10. }
  11. }

  在上面的演示样例中。类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();“在例一中编译不通过。


  1. //演示样例四
  2. package p4;
  3. class MyObject4 extends Test4 {
  4. protected Object clone() throws CloneNotSupportedException {
  5. return super.clone();
  6. }
  7. }
  8. package p44;
  9. public class Test4 {
  10. public static void main(String args[]) {
  11. MyObject4 obj = new MyObject4();
  12. obj.clone(); // Compile Error。protected方法clone()来自于MyObject4类,而Test4类与MyObject4类不在同一个包中
  13. }
  14. }

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


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

  1. //演示样例五
  2. package p5;
  3. class MyObject5 {
  4. protected Object clone() throws CloneNotSupportedException {
  5. return super.clone();
  6. }
  7. }
  8. public class Test5 {
  9. public static void main(String[] args) throws CloneNotSupportedException {
  10. MyObject5 obj = new MyObject5();
  11. obj.clone(); // Compile OK,protected方法clone()来自于MyObject5类,而Test5类与MyObject5类又在同一个包中
  12. }
  13. }

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


  1. //演示样例六
  2. package p6;
  3. class MyObject6 extends Test6{}
  4. public class Test6 {
  5. public static void main(String[] args) {
  6. MyObject6 obj = new MyObject6();
  7. obj.clone(); // Compile OK
  8. }
  9. }

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


  1. //演示样例七
  2. package p7;
  3. class MyObject7 extends Test7 {
  4. public static void main(String[] args) {
  5. Test7 test = new Test7();
  6. test.clone(); // Compile Error.
  7. }
  8. }
  9. public class Test {
  10. }

  在本演示样例中。尽管类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的类成员訪问权限修饰词(以及类訪问权限)

Java 訪问权限控制:你真的了解 protected keyword吗?的更多相关文章

  1. ProFTPD配置匿名登录与文件夹訪问权限控制

    对ProFTPDserver配置匿名登录.         查看配置文件proftpd.conf.默认情况下配置文件里的.匿名登录配置User和Group均为ftp. 查看/etc/passwd确认用 ...

  2. Java面试题03-访问权限控制

    Java面试题03-访问权限控制 1. Java中的包主要是为了防止类文件命名冲突以及方便进行代码组织和管理,因此采用域名倒置的方式来进行命名: 2. Java解释器的运行过程:首先找到环境变量CLA ...

  3. 使用Hadoop ACL 控制訪问权限

    使用Hadoop ACL 控制訪问权限 一.HDFS訪问控制 hdfs-site.xml设置启动acl <property>  <name>dfs.permissions.en ...

  4. Objective-C 类属性和方法的訪问权限

    OC中提供了4种訪问权限.@private, @public, @protected这三种和其它的C++, Java是一样的,@package这个訪问权限并非Java里的包訪问权限,OC中没有包的概念 ...

  5. Android 訪问权限清单

    Android权限设置 概述 权限 说明 訪问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES 读取或写入登记check-in数据库属性表的权限 获取 ...

  6. apache主机(网站)配置,port监听,文件夹訪问权限及分布式权限

    前言 一个网站的两个核心信息为: 主机名称(server名/网站名):ServerName server名 网站位置(网站文件夹路径):DocumentRoot "实际物理路径" ...

  7. [加入用户]解决useradd 用户后没有加入用户Home文件夹的情况,Linux改变文件或文件夹的訪问权限命令,linux改动用户password,usermod的ysuum安装包。飞

    usermod的yum安装包: shadow-utils 将nobody用户加入到nogroup 组: usermod -g nogroup nobody cat /etc/passwd|grep n ...

  8. JAVA訪问URL

    JAVA訪问URL: package Test; import java.io.BufferedReader; import java.io.IOException; import java.io.I ...

  9. win7 wifi 无Internet訪问权限或者有限的訪问权限

    自己家的无线路由器,手机和笔记本都使用正常,可是一台新笔记本连上之后总是提示"有限的訪问权限",无法连公网. 网上的非常多办法都无论用,什么设置静态IP或者重新启动路由,基本都是瞎 ...

随机推荐

  1. flash 拾遗

    http://sourceforge.net/adobe/wiki/Projects/ http://www.adobe.com/devnet/air/air-sdk-download.html ht ...

  2. Makefile常用万能模板(包括静态链接库、动态链接库、可执行文件)

    本文把makefile 分成了三份:生成可执行文件的makefile,生成静态链接库的makefile,生成动态链接库的makefile. 这些makefile都很简单,一般都是一看就会用,用法也很容 ...

  3. 数据库表名最大长度(Oracle=30;SqlServer=128;)

    1.Oracle 数据库 (支持30个字符) --30个字符 CREATE TABLE Tab_Test1234567890abcdefghijkl( ts int ); --select * fro ...

  4. iOS开发中多线程断点下载大文件

    主要思想,就是创建一个与目标文件等大小的空白文件,然后分段往这个空白文件中写入数据. 可以通过发送HEAD请求,获得服务器中文件的具体大小,然后再将这样的长度分割成若干等大的数据块,在发送get请求时 ...

  5. 08Vue.js快速入门-Vue综合实战项目

    8.1. 前置知识学习 npm 学习 官方文档 推荐资料 npm入门 npm介绍 需要了解的知识点 package.json 文件相关配置选项 npm 本地安装.全局安装.本地开发安装等区别及相关命令 ...

  6. vue前后分离动态路由和权限管理方案

    需求 需要根据不同的角色来显示不同的菜单 问题 系统是前后分离模式开发的,出现了后端接口和前端路由都需要权限管理. 思路 后端的接口肯定得验证权限 在前端做好组件名和组件的映射 前端的路由通过后端发回 ...

  7. visualstudio学习

    https://docs.microsoft.com/zh-CN/visualstudio/ide/get-started-with-visual-studio

  8. hexo静态博客的安装及应用实践记录

    1. 必须要安装 nodejs,git 2. 安装hexo node install -g hexo 3. npm源的问题使在安装时有卡住的问题而导致无法安装,则需要更改npm的源 npm confi ...

  9. redis主从配置(docker实现)

    一.docker新建两个redis服务端,并分别设置端口为6379和6380 命令如下: docker run -p : -d --name redis-server docker.io/redis: ...

  10. Android——Activity去除标题栏和状态栏

    一.在代码中设置 public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  //去 ...