Java访问权限之 protected详解
摘要:
对于类的成员(包括成员变量和成员方法)而言,其能否被其他类所访问,取决于该成员的修饰词;而对于一个类而言,其能否被其他类所访问,也取决于该类的修饰词。在Java中,类成员访问权限修饰词有四类:private,无(包访问权限),protected 和 public,而其中只有包访问权限和public才能修饰一个类(内部类除外)。由于很多Java书籍对protected可见性的介绍都比较笼统,本文重点说明了protected关键字的可见性内涵。
一、Java访问权限概述
对于一个类,其成员(包括成员变量和成员方法)能否被其他类所访问,取决于该成员的修饰词。在Java中,类成员的访问权限修饰词有四个:private,无(包访问权限),protected 和 public,其权限控制如下表所示:
| | 同一个类中 | 同一个包中 | 不同包的子类 | 不同包的无关类 |
|–|–|–|–|–|–|
| public | ✔ | ✔ | ✔ | ✔ |
| protected | ✔ | ✔ | ✔ | |
| 无(空着不写) | ✔ | ✔ | | |
| private | ✔ | | | |
不同包中子类: 不同包通过继承获得关系
不同包中的无关类: 不同包通过直接创建对象来获得关系
在上面所提到的四种修饰词中,除 protected 外都很好理解和掌握,在此略作简述:
- public :被public修饰的类成员能被所有的类直接访问;
- 包访问权限 :包访问权限就是Java中的默认的权限,具有包访问权限的类成员只能被同一包中的类访问。
- private : 被public修饰的类成员只能在定义它的类中被访问,其他类都访问不到。特别地,我们一般建议将成员变量设为private的,并为外界提供 getter/setter 去对成员变量进行访问,这种做法充分体现了Java的封装思想;如果你不希望其他任何人对该类拥有访问权,你可以把所有的构造器都指定为private的,从而阻止任何人创建该类的对象。
二、protected关键字的可见性
很多介绍Java语言的书籍(比如《Java编程思想》)都对protected做了介绍,但是描述的比较简单,基本都是一句话“被protected修饰的成员对于本包和其子类可见”。这种说法有点太过含糊,常常会对大家造成误解。实际上,protected的可见性在于以下几点:
- 基类(父类)的protected成员(包括成员变量个成员方法)对本包内可见,并且对子类可见;
- 若子类与基类(父类)不在同一包中,那么在子类中,只有子类实例可以访问其从基类继承而来的protected方法,而在子类中不能访问基类实例(对象)(所调用)的protected方法。
- 不论是否在一个包内,父类中可以访问子类实例(对象)继承的父类protected修饰的方法。(子父类访问权限特点:父类访问域大于子类)
- 若子类与基类(父类)不在同一包中,子类只能在自己的类(域)中访问父类继承而来的protected成员,无法访问别的子类实例(即便同父类的亲兄弟)所继承的protected修饰的方法。
- 若子类与基类(父类)不在同一包中,父类中不可以使用子类实例调用(父类中没有)子类中特有的(自己的)protected修饰的成员。(毕竟没有满足同一包内和继承获得protected成员的关系)
接下来我们通过下面几个关于protected方法可见性的例子进行详细解释protected关键字:
示例一:
// 示例一
package p1;
public class Father1 {protected void f() {} // 父类Father1中的protected方法
}package p1;
public class Son1 extends Father1 {}package p2;
public class Son2 extends Father1{}package p1;
public class Test {public static void main(String[] args) {Son1 son1 = new Son1();son1.f(); // Compile OK ----(1)son1.clone(); // Compile Error ----(2)Son2 son = new Son2(); son2.f(); // Compile OK ----(3)son2.clone(); // Compile Error ----(4)Test t = new Test();t.clone; // Compile OK// 测试类test只可以使用test类的继承Object类而来的test.clone()方法}
}
首先看(1)(3),其中f()方法从类Father1中继承而来,其可见性是包p1及其子类Son1和Son2,而由于调用方法f()的类Test也在包p1之内,因此(1)(3)处编译通过。
其次看(2)(4),其中clone()方法所在类为Object默认根类,而Object类所在包为java.lang包。其可见性是java.lang包及其所有子类,对于语句“son1.clone();”和“son11.clone();”,二者的clone()在类Son1、Son2中是可见的(可以使用的),但对Test类是不可见的,因此(1)(3)处编译不通过。
再者,如果测试类在p2包内,由于不满足protected两种可见性(同一包内和子父类关系),也将无法调用protected修饰的f()方法。test测试类在p2包内,即便继承Father1类Son1和Son2也无法调用f()方法(Test t = new Test(); t.f();可以调用),因为在p2包内继承了Father1,虽然不在同一包内,但是现在有了子父类的关系,即Test类与Son1、Son2形成了兄弟关系,由于 protected受访问保护规则是很微妙的,虽然protected域对所有子类都可见,但是子类只能在自己的作用范围内访问自己继承的那个父类protected,而无法到访问别的子类(同父类的亲兄弟)所继承的protected修饰的方法。
子类只能在自己的作用范围(域)中访问父类protected成员,无法访问别的子类实例(即便同父类的亲兄弟)所继承的protected修饰的方法。
但是即便不在一个包里,在父类中也可以访问子类实例(对象)继承的父类protected修饰的方法。
如果子父类不在一个包内,父类不可以调用(父类中没有)子类中特有的(自己的)protected修饰的成员。(毕竟没有满足同一包内和继承获得protected成员的关系)
示例二
package P1;
public class MyObject {protected Object clone() throws CloneNotSupportedException{return super.clone();}
}package P2;
import P1.MyObject;
public class Test extends MyObject {public static void main(String args[]) throws CloneNotSupportedException {MyObject obj = new MyObject();obj.clone(); // Compile Error ----(1)Test tobj = new Test();tobj.clone(); // Complie OK ----(2)}
}
对于(1)而言,clone()方法来自于类MyObject本身,因此其可见性为包p1及MyObject的子类,虽然Test是MyObject的子类,但是由于在子类中,只有本类实例可以访问其从基类继承而来的protected方法,而在子类中不能访问基类实例(对象)(所调用)的protected方法。(作为子类,Test类中建立超类实例,直接去访问超类的protected方法是不可以的),因此编译不通过。
对于(2)而言,由于在Test中访问的是其本身实例的从基类MyObject继承来的的clone(),因此编译通过。
示例三
package P1;
public class Test {public static void main(String args[]) throws CloneNotSupportedException {MyObject obj = new MyObject();obj.clone(); // Compile OK ------(1)}
}package P2;
public class MyObject extends Test {}
对于(1)而言,由于父类中可以访问子类实例(对象)继承的父类protected修饰的方法(子父类访问权限特点:父类访问域大于子类),因此编译通过。
示例四
package P1;
public class Test {public static void main(String args[]) throws CloneNotSupportedException {MyObject obj = new MyObject();obj.clone(); // Compile Error -----(1)}
}package P2;
public class MyObject extends Test {protected Object clone() throws CloneNotSupportedException {return super.clone();}
}
对于(1)而言,clone()方法来自于类MyObject是其特有方法,因此其可见性为包p2及其子类(此处没有子类),而类Test4却在包p1中,因此不满足可见性,编译不通过。
若子类与基类(父类)不在同一包中,父类中不可以使用子类实例访问(父类中没有)子类中特有的(自己的)protected修饰的成员。(毕竟没有满足同一包内和继承获得protected成员的关系)
示例五
package P1;public class MyObject {protected Object clone() throws CloneNotSupportedException{return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {MyObject obj = new MyObject();obj.clone(); // Compile OK ----(1)}
}
对于(1)而言,clone()方法来自于类MyObject,因此其可见性为包p1及其子类(此处没有子类),而类Test也在包p1中,因此满足可见性,编译通过。
示例六
package p1;class MyObject extends Test{}public class Test {public static void main(String[] args) throws CloneNotSupportedException {MyObject obj = new MyObject();obj.clone(); // Compile OK -------(1)}
}
对于(1)而言,clone()方法来自于类Test6,因此其可见性为包p1及其子类MyObject,而类Test也在包p1中,因此满足可见性,编译通过。
示例七
package p1;public class Test {}class MyObject extends Test {public static void main(String[] args) {Test test = new Test();test.clone(); // Compile Error ----- (1)}
}
对于(1)而言,首先,clone()方法所在类为Object默认根类,而Object类所在包为java.lang包。protected修饰的clone()方法其可见性是java.lang包及其所有子类,所以可以判断clone()方法所在包与子类不在同一个包中。
其次,若子类与基类不在同一包中时,子类中不能访问基类实例(对象)(所调用)的protected方法。 MyObject子类访问权限只满足本类示例的访问。
示例八
package A;
public class Animal { protected void crowl(String c){ System.out.println(c); }
}package B;
import A.Animal;
class Cat extends Animal
{ } public class Rat extends Animal{ public void crowl(){ this.crowl("zhi zhi"); //没有问题,继承了Animal中的protected方法——crowl(String) Animal ani=new Animal();ani.crowl("animail jiaojiao"); //wrong, The method crowl(String) from the type Animal is not visible Cat cat=new Cat(); cat.crowl("miao miao"); //wrong, The method crowl(String) from the type Animal is not visible }
}
首先判断其可见性,可以看出虽然不满足同一包内,但是满足子父类继承关系。
由于子类与基类(父类)不在同一包中,所以在子类中,只有本类实例可以访问其从基类继承而来的protected方法。而在子类中不能访问基类实例(对象)(所调用)的protected方法。而在子类中也无法访问其他子类实例(即便相同父类的亲兄弟)所继承的protected修饰的方法。
示例九
package a;
public class A { protected void m(){System.out.println("A m~~~");}
}package b;
import a.A;
public class B extends A {void callM() {m();super.m();B b = new B();b.m();}
}package b;
import a.A;
public class C extends A {void callM() {m();super.m();B b = new B();b.m(); //The method m() from type A is not visible}
}
因为b.m()这种调用属于子类对象调用,因此,当创建子类对象调用父类的protected成员变量时,必须要注意:子类对象和子类是对应的!
由上述案例总结,由于子类与基类(父类)不在同一包中,子类只能在自己的类(域)中访问父类继承而来的protected成员,无法访问别的子类实例(即便相同父类的亲兄弟)所继承的protected修饰的方法。所以编译失败。
三、总结
protected是最难理解的一种Java类成员访问权限修饰词。在编程中,碰到涉及protected的调用时,首先要确定出该protected成员来自何方,其可见性范围是什么,或根据下列访问特点,便可以正确无误的使用了。
- 基类(父类)的protected成员(包括成员变量个成员方法)对本包内可见,并且对子类可见;
- 若子类与基类(父类)不在同一包中,那么在子类中,只有本类实例可以访问其从基类继承而来的protected方法,而在子类中不能访问基类实例(对象)(所调用)的protected方法。
- 不论是否在一个包内,父类中可以访问子类实例(对象)继承的父类protected修饰的方法。(子父类访问权限特点:父类访问域大于子类)
- 若子类与基类(父类)不在同一包中,子类只能在自己的类(域)中访问父类继承而来的protected成员,无法访问别的子类实例(即便相同父类的亲兄弟)所继承的protected修饰的方法。
- 若子类与基类(父类)不在同一包中,父类中不可以使用子类实例调用(父类中没有)子类中特有的(自己的)protected修饰的成员。(毕竟没有满足同一包内和继承获得protected成员的关系)
四、引用
- Java基础知识详解: protected修饰符
- Java 访问权限控制:你真的了解 protected 关键字吗?
- The method * from the type * is not visible
- protected修饰符详解
Java访问权限之 protected详解相关推荐
- Java访问权限修饰符详解
Java中的访问权限修饰符(Java关键字) 今天很皮的发现,自己现在竟然不会玩儿Java基础了,就翻看了一下自己的笔记,写一篇文章复习一下! 测试环境:Java运行环境,myEclipse. 测试前 ...
- java按钮权限控制_详解Spring Security 中的四种权限控制方式
Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Se ...
- java访问本地文件_详解Java读取本地文件并显示在JSP文件中
详解Java读取本地文件并显示在JSP文件中 当我们初学IMG标签时,我们知道通过设置img标签的src属性,能够在页面中显示想要展示的图片.其中src的值,可以是磁盘目录上的绝对,也可以是项目下的相 ...
- java springmvc权限校验_详解Spring MVC使用Filter实现登录及权限验证判断
登录和权限验证判断在后台管理系统中是最常用的功能,这部分代码是比较固定和独立的,为了减少对业务代码入侵性,一般我会考虑使用Filter来实现,下面我就来详细说一下我的实现思路和代码: 前台页面: St ...
- Mybatis零基础教程,Java访问数据库核心操作,详解Spring-boot整合Mybatis持久层!
1.前言 持久层是JavaEE中访问数据库的核心操作,Mybatis是一款优秀的持久层框架,诞生于2010年,2013年迁移至Github.它支持定制化 SQL.存储过程以及高级映射.MyBatis ...
- Java访问权限(public、protected、友好的、private)定义
访问权限定义: 1.访问权限指:对象是否可以通过 " . " 运算符操作自己的变量 或 通过 " . " 运算符调用类中的方法: 2.访问权限修饰符:priva ...
- java request获取域,Java Web - Servlet(13)HttpServletRequest详解(获取请求数据、请求分派、请求域)(二)...
Java Web -- Servlet(13)HttpServletRequest详解(获取请求数据.请求分派.请求域)(2) HttpServletRequest ----------------- ...
- 【Java SE】封装的详解
篮球哥温馨提示:编程的同时不要忘记锻炼哦! 因为热爱,所以编程 目录 1.封装 1.1 什么是封装? 2.2 访问权限 2.3 private 简单使用 2.封装之包的概念 2.1 什么是包? 2.2 ...
- Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)_3
Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)_3 总览 问题 详解 String.intern()的作用 link LeetCode的Two Sum题 ...
最新文章
- 曲线拟合最小二乘法优缺点_最小二乘法的优缺点
- Python3 模块 -- Fabric自动化模版
- java原子操作的实现原理--转载
- 6个免费的C++图形和游戏库
- GPU 实现 RGB -- YUV 转换 (OpenGL)
- Android官方开发文档Training系列课程中文版:线程执行操作之定义线程执行代码
- Linux的 ACL策略的作用
- 使用WebService与Oracle EBS进行集成(下)
- 剑指offer面试题[14]-调整数组顺序使奇数位于偶数前面
- html5页面签字,html5 canvas实现的手机端签字板
- 聚类:Python遗传算法
- 学计算机的学后感,计算机学习心得体会(通用10篇)
- 2018年小美赛C题参赛经历
- php爬虫分析四六级情况
- 史上最全最准的支付行业常用名词解释精选
- 从零开始搭建ROS智能小车@sp头子
- 【05】制作第一个zblog模板第四期,完成首页logo、搜索、文章列表,友情链接等大部分首页自定义模块
- Java——打印九宫格(奇数行方阵)
- android自动播放音乐代码,Android MediaPlayer实现音乐播放器实例代码
- EHub_tx1_tx2_E100 Cartographer 完美部署安装