Java访问权限之 protected详解「建议收藏」

Java访问权限之 protected详解「建议收藏」对于类的成员(包括成员变量和成员方法)而言,其能否被其他类所访问,取决于该成员的修饰词;而对于一个类而言,其能否被其他类所访问,也取决于该类的修饰词。在Java中,类成员访问权限修饰词有四类:private,无(包访问权限),protected和public,而其中只有包访问权限和public才能修饰一个类(内部类除外)。由于很多Java书籍对protected可见性的介绍都比较笼统,本文重点说明了protected关键字的可见性内涵。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

摘要:
    对于类的成员(包括成员变量和成员方法)而言,其能否被其他类所访问,取决于该成员的修饰词;而对于一个类而言,其能否被其他类所访问,也取决于该类的修饰词。在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()方法
    }
}

Jetbrains全家桶1年46,售后保障稳定

    首先看(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成员的关系)

四、引用

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/222879.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 工作流模块Jar包启动报错:liquibase – Waiting for changelog lock….

    工作流模块Jar包启动报错:liquibase – Waiting for changelog lock….

  • 数据库常见面试题及答案(数据库面试常见问题)

    数据库常见面试题及答案(数据库面试常见问题)1、触发器的作用?触发器是一中特殊的存储过程,主要是通过事件来触发而被执行的。它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算。如,某表上的触发器上包含对另一个表的数据操作,而该操作又会导致该表触发器被触发。2、什么是存储过程?用什么来调用?存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在…

  • 内网IP和公网IP的区别

    内网IP和公网IP的区别IP地址对于经常上网的人应该都不陌生,ip地址又可以分成内网ip地址和公网ip地址,今天就来简单介绍下这两者的区别。通常我们所说的内网也就是局域网,是内网的计算机以网络地址转换协议,通过一个公共的网关访问Internet。而内网的计算机也可以向Internet上的其他计算机发送连接请求。但是但Internet上其他的计算机无法向内网的计算机发送连接请求。为了简单理解我们就以网吧的网络举个列子,网吧的网线都是连接在同一个交换机上面的,也就是说它们的IP地址是由交换机或者路由器进行分配的。而且每…

  • 代理服务器基本知识[通俗易懂]

    代理服务器基本知识[通俗易懂]代理服务器系列(一):基础知识2005年9月10日世界网络教研组编一、什么是代理服务器?代理服务器英文全称是ProxyServer,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。在一般情况下,我们使用网络浏览器直接去连接其他Internet站点取得网络信息时,需送出Request信号来得到回答,然后对方再把信息以bit方

  • 估值400亿,小米愿打谁愿挨?

    估值400亿,小米愿打谁愿挨?

    2021年12月14日
  • cshtml标记html5,cshtml常用标签

    cshtml标记html5,cshtml常用标签@RenderSection:在布局页中,将呈现指定部分的内容并指定该部分是否为必需。用法:@RenderSection(“PageSpecificStyleSheetIncludes”,required:false)@Html.Partial:在布局页中,把一个个View给镶入进来并回传的一个Object(MvcHtmlString)用法:@Html.Partial(“_Top”)@H…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号