Java 三大特性之——继承

继承(inheritance)是面向对象的重要概念。继承是除组合(composition)之外,提高代码重复可用性(reusibility)的另一种重要方式。我们在组合(composition)中看到,组合是重复调用对象的功能接口。我们将看到,继承可以重复利用已有的类的定义。


类的继承

        我们之前定义类的时候,都是从头开始,详细的定义该类的每一个成员。比如下面的Human类:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class Human  
  2. {     
  3.    /** 
  4.      * accessor 
  5.      */  
  6.     public int getHeight()  
  7.     {  
  8.        return this.height;  
  9.     }  
  10.   
  11.     /** 
  12.      * mutator 
  13.      */  
  14.     public void growHeight(int h)  
  15.     {  
  16.         this.height = this.height + h;  
  17.     }  
  18.   
  19.     /** 
  20.      * breath 
  21.      */  
  22.     public void breath()  
  23.     {  
  24.         System.out.println("hu...hu...");  
  25.     }  
  26.   
  27.     private int height;   
  28. }  
从上面的类定义,我们可以了解该类的所有细节: 该类的数据成员,该类的方法,该类的接口。


现在要定义一个新的类,比如Woman类,并假设Woman与Human类相当类似:


         Human & Woman

我们可以像以前一样,从头开始,完整的定义Woman类:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class Woman{  
  2.     /** 
  3.      * accessor 
  4.      */  
  5.     public int getHeight()  
  6.     {  
  7.        return this.height;  
  8.     }  
  9.     /** 
  10.      * mutator 
  11.      */  
  12.     public void growHeight(int h)  
  13.     {  
  14.         this.height = this.height + h;  
  15.     }  
  16.     /** 
  17.      * breath 
  18.      */  
  19.     public void breath()  
  20.     {  
  21.         System.out.println("hu...hu...");  
  22.     }  
  23.     /** 
  24.      * new method 
  25.      */  
  26.     public Human giveBirth()  
  27.     {  
  28.         System.out.println("Give birth");  
  29.         return (new Human(20));  
  30.     }  
  31.     private int height;   
  32. }  

一个程序员在写上面程序的时候,会有很大的烦恼。许多定义都曾在Human类中写过,但我们还要重新敲一遍。Woman类只新增了一个giveBirth()方法 (该方法创建并返回一个新的Human对象)。


利用继承,我们可以避免上面的重复。让Woman类继承自Human类,Woman类就自动拥有了Human类中所有public成员的功能。

我们用extends关键字表示继承:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class Woman extends Human  
  2. {  
  3.     /** 
  4.      * new method 
  5.      */  
  6.     public Human giveBirth()  
  7.     {  
  8.         System.out.println("Give birth");  
  9.         return (new Human(20));  
  10.     }  
  11. }  
这样,我们就省去了大量的输入。通过继承,我们创建了一个新类,叫做衍生类(derived class)。被继承的类(Human)称为基类(base class)。衍生类以基类作为自己定义的基础,并补充基类中没有定义的giveBirth()方法。继承关系可以表示为:

 

可以用以下Test类测试:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. public class Test  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         Woman aWoman = new Woman();  
  6.         aWoman.growHeight(120);  
  7.         System.out.println(aWoman.getHeight());                                               
  8.     }  
  9. }  


1、衍生层

通过继承,我们创建了Woman类。整个过程可以分为三个层次:基类定义,衍生类定义,外部使用

基类定义的层次就是正常的定义一个类,比如上面的Human类定义。
在外部使用者看来(比如Test类中创建Woman类对象),衍生类有一个统一的外部接口:


对于外部使用者来说,上述接口就已经足够了。仅从接口看,衍生类也没有什么特别之处。

然而,当程序员在衍生类定义的层次时,就必须要小心:

首先,接口是混合的: getHeight()方法和growHeight()方法来自基类,giveBirth()方法则是在衍生类内部定义的。
还有更加复杂的地方。我们之前在类的内部可以自由访问类的成员(利用this指代对象)。然而,当我们在Woman类的定义范围内,我们无法访问基类Human的private成员。我们记得private的含义: private的成员仅供该类内部使用。Woman类是一个不同于Human类的新类,所以位于Human类的外部。在衍生类中,不能访问基类的private成员。

但有趣的是,我们的growHeight()和getHeight()方法依然可以运行。这说明基类的private成员存在,我们只是不能直接访问。

为了清晰概念,我们需要了解衍生类对象的生成机制。当我们创建一个衍生类的对象时,Java实际上先创建了一个基类对象(subobject),并在基类对象的外部(注意,这里是基类对象的外部,衍生类对象的内部),增加衍生类定义的其他成员,构成一个衍生类对象。外部使用者能看到的,就是基类和衍生类的public成员。如下图:

        基类对象与衍生类对象

图中黄色为基类对象。基层的成员之间可以互相访问 (利用Human类定义中的this指代基类对象)。

蓝色部分为衍生对象新增的内容,我将这部分称为衍生层。蓝色和黄色部分共同构成衍生对象。衍生层的成员可以相互访问(Woman定义中的this)。更进一步,我们还可以访问基层中public的成员。为此,我们用super关键字来指代基类对象,使用super.member的方式来表示基层的(public)成员

当我们位于衍生层时(也就是在定义Woman类时),不能访问红色的基层private成员。当我们位于外部时,既不能访问紫色的衍生层private成员,也不能访问红色的基层private成员。(衍生层的private成员有访问禁忌,所以标为斜线。基层的private成员访问禁忌最多,所以标为交叉斜线)

superthis类似,也是隐式参数。我们在类定义的不同层次时,this会有不同的含义。要小心的使用this和super关键字。
(Java并不强制使用this和super。Java在许多情况下可以自动识别成员的归属。但我觉得这是个好习惯。)


2、protected

  我们之前介绍了两个访问权限相关的关键字,private和public,它们控制了成员的外部可见性。现在,我们介绍一个新的访问权限关键字: protected。
标为protected的成员在该类及其衍生类中可见。这个概念很容易理解,就是说,基类的protected成员可以被衍生层访问,但不能被外部访问,如下图:


3、方法覆盖

  衍生类对象的外部接口最终由基类对象的public成员衍生层的public成员共同构成。如果基类public成员和衍生层的public成员同名,Java接口中呈现的究竟是哪一个呢?

  我们在构造方法与方法重载中已经提到,Java是同时通过方法名和参数列表来判断所要调用的方法的。方法是由方法名和参数列表共同决定的。上述问题中,如果只是方法名相同,而参数列表不同,那么两个方法会同时呈现到接口,不会给我们造成困扰。外部调用时,Java会根据提供的参数,来决定使用哪个方法(方法重载)

  如果方法名和参数列表都相同呢? 在衍生层时,我们还可以通过superthis来确定是哪一个方法。而在外部时,我们呈现的只是统一接口,所以无法同时提供两个方法。这种情况下,Java会呈现衍生层的方法,而不是基层的方法。
这种机制叫做方法覆盖(method overriding)。方法覆盖可以被很好的利用,用于修改基类成员的方法。比如,在衍生层,也就是定义Woman时,可以修改基类提供的breath()方法:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class Woman extends Human  
  2. {/** 
  3.      * new method 
  4.      */  
  5.     public Human giveBirth()  
  6.     {  
  7.         System.out.println("Give birth");  
  8.         return (new Human(20));  
  9.     }  
  10.   
  11.     /** 
  12.      * override Human.breath() 
  13.      */  
  14.     public void breath()  
  15.     {  
  16.         super.breath();  
  17.         System.out.println("su...");  
  18.     }  
  19. }  

注意,此时我们位于衍生层,依然可以通过super来调用基类对象的breath()方法。当我们外部调用Woman类时,由于方法覆盖,就无法再调用基类对象的该方法了。

方法覆盖保持了基类对象的接口,而采用了衍生层的实现。使用时要注意一下三点:

1、三同原则:同方法名、同返回类型、同参数表
2、子类中的覆盖方法不能使用比父类中被覆盖的方法更严格的访问权限。
3、如需使用父类中原有的方法,可使用super关键字,该关键字引用了当前类父类的方法。
覆盖实际上是父子类之间的关系


4、构造方法

   在了解了基类对象和衍生层的概念之后,衍生类的构造方法也比较容易理解。

   我们要在衍生类的定义中定义与类同名的构造方法。在该构造方法中:
1)由于在创建衍生对象的时候,基类对象先被创建和初始化,所以,基类的构造方法应该先被调用。我们可以使用super(argument list)的语句,来调用基类的构造方法。
2)基类对象创建之后,开始构建衍生层 (初始化衍生层成员)。这和一般的构建方法相同,参考构造方法与方法重载

比如下面的程序中,Human类有一个构造方法:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class Human  
  2. {     
  3.   
  4.     /** 
  5.      * constructor 
  6.      */  
  7.     public Human(int h)  
  8.     {  
  9.         this.height = h;  
  10.     }  
  11.   
  12.     /** 
  13.      * accessor 
  14.      */  
  15.     public int getHeight()  
  16.     {  
  17.        return this.height;  
  18.     }  
  19.   
  20.     /** 
  21.      * mutator 
  22.      */  
  23.     public void growHeight(int h)  
  24.     {  
  25.         this.height = this.height + h;  
  26.     }  
  27.   
  28.     /** 
  29.      * breath 
  30.      */  
  31.     public void breath()  
  32.     {  
  33.         System.out.println("hu...hu...");  
  34.     }  
  35.   
  36.     private int height;   
  37. }  
衍生类Woman类的定义及其构造方法:
[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class Woman extends Human  
  2. {  
  3.     /** 
  4.      * constructor 
  5.      */  
  6.     public Woman(int h)  
  7.     {  
  8.         super(h); // base class constructor  
  9.         System.out.println("Hello, Pandora!");  
  10.     }  
  11.   
  12.     /** 
  13.      * new method 
  14.      */  
  15.     public Human giveBirth()  
  16.     {  
  17.         System.out.println("Give birth");  
  18.         return (new Human(20));  
  19.     }  
  20.   
  21.     /** 
  22.      * override Human.breath() 
  23.      */  
  24.     public void breath()  
  25.     {  
  26.         super.breath();  
  27.         System.out.println("su...");  
  28.     }  
  29. }  


java的继承覆盖总结

1.构造函数:

当子类继承一个父类时,构造子类时需要调用父类的构造函数,存在三种情况

1)父类无构造函数或者一个无参数构造函数,子类若无构造函数或者有无参数构造函数,子类构造函数中不需要显式调用父类的构造函数,系统会自动在调用子类构造函数前调用父类的构造函数

2)父类只有有参数构造函数,子类在构造方法中必须要显示调用父类的构造函数,否则编译出错

3)父类既有无参数构造函数,也有有参构造函数,子类可以不在构造方法中调用父类的构造函数,这时使用的是父类的无参数构造函数
         

2.方法覆盖:
1)子类覆盖父类的方法,必须有同样的参数返回类型,否则编译不能通过
2)子类覆盖父类的方法,在jdk1.5后,参数返回类可以是父类方法返回类的子类
3)子类覆盖父类方法,可以修改方法作用域修饰符,但只能把方法的作用域放大,而不能把public修改为private
4)子类方法能够访问父类的protected作用域成员,不能够访问默认的作用域成员
5)子类的静态方法不能隐藏同名的父类实例方法
6)java与C++一样,继承的方法具有多态性
         //以上6个结论已经过代码验证

3.成员覆盖:
1)当子类覆盖父类的成员变量时,父类方法使用的是父类的成员变量,子类方法使用的是子类的成员变量
  这个听起来很容易理解的一回事,但是实际使用过程中很多人容易搞混:尤其是在多态的时候,调用一个被继承的方法,该方法访问是一个被覆盖的成员m,那么方法中到底是访问了父类的成员还是子类的成员m?结论是,若实际调用的是父类的方法,就使用了父类的该成员m,若实际调用的是子类的方法,就使用子类的成员m,记住一句,每个类使用成员都相当于在前面加了 一个this指针。

总结例子:

[java] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class SuperClass {    
  2.     private int number;    
  3.     public SuperClass() {    
  4.         this.number = 0;    
  5.     }    
  6.     
  7.     public SuperClass(int number) {    
  8.         this.number = number;    
  9.     }    
  10.     
  11.     public int getNumber() {    
  12.         number++;    
  13.         return number;    
  14.     }    
  15. }    
  16.     
  17. class SubClass1 extends SuperClass {    
  18.     public SubClass1(int number) {    
  19.         super(number);    
  20.     }    
  21. }    
  22.     
  23. class SubClass2 extends SuperClass {    
  24.     private int number;    
  25.     
  26.     public SubClass2(int number) {    
  27.         super(number);    
  28.     }    
  29. }    
  30.     
  31. public class SubClass extends SuperClass {    
  32.     private int number;    
  33.     
  34.     public SubClass(int number) {    
  35.         super(number);    
  36.     }    
  37.     
  38.     public int getNumber() {    
  39.         number++;    
  40.         return number;    
  41.     }    
  42.     
  43.     public static void main(String[] args) {    
  44.         SuperClass s = new SubClass(20);    
  45.         SuperClass s1 = new SubClass1(20);    
  46.         SuperClass s2 = new SubClass2(20);    
  47.         System.out.println(s.getNumber());    
  48.         System.out.println(s1.getNumber());    
  49.         System.out.println(s2.getNumber());    
  50.         //结论一:多态时,当子类覆盖了父类的方法,使用子类覆盖的方法    
  51.         //结论二:当子类覆盖父类的成员变量时,父类方法使用的是父类的成员变量,子类方法使用的是子类的成员变量    
  52.     }     
  53. }   
执行结果:

1
21
21

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401988.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Linux的 Open×××网络之网络架构应用实例

基于Linux的 Open网络之网络架构应用实例Open 概述Open 是一个开源的加密隧道构建工具,基于 OpenSSL 的 SSL/TLS 协议,可以在 Internet中实现点对点的 SSL 安全连接。使用 Open 的好处是安全、易用和稳定,且认证方式灵活,具备实现…

Java 进阶——自动装箱和自动拆箱

1、什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。 一般我们要创建一个类的对象实例的时候,我们会这样: Class a new Class(parameter); 当我们创建一个Integer对象时,却可以这样&…

基于KVM的虚拟化研究及应用

引言 虚拟化技术是IBM在20世纪70年代首先应用在IBM/370大型机上,这项技术极大地提高了大型机资源利用率。随着软硬件技术的迅速发展,这项属于大型机及专利的技术开始在普通X86计算机上应用并成为当前计算机发展和研究的一个热点方向。目前&am…

Java 进阶—— super 和 this 的用法

一、this Java关键字this只能用于方法方法体内。当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是this。因此,this只能在类中的非静态方法中使用,静态方…

unity中脚本编辑器UnIDE

引言 unity默认脚本编辑器是MonoDevelop,随着unity4.3面世,MonoDevelop (4.0.1)版本也随之而来,更新为界面更改和bug自动修复功能等,具体还未使用。 点击unity的Edit下的属性(preference),可以更改默认脚本编辑器&…

apk,task,进程区别

2019独角兽企业重金招聘Python工程师标准>>> apk,task,进程区别 apk一般占一个dalvik,一个进程,一个task。通过设置也可以多个进程,占多个task。 task是一个activity的栈,其中"可能"含有来自…

Java 线程 —— 基础篇

一、操作系统中线程和进程的概念 现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe…

【Java并发性和多线程】线程安全及不可变性

2019独角兽企业重金招聘Python工程师标准>>> 本文为转载学习 原文链接:http://tutorials.jenkov.com/java-concurrency/thread-safety-and-immutability.html 译文链接:http://ifeve.com/thread-safety-and-immutability/ 当多个线程同时访问…

Java 高级—— IO 基础

一、File 类 先看一下File 类的定义 [java] view plaincopy public class File extends Object implements Serizliable Comparable<File> 从定义看&#xff0c;File类是Object的直接子类&#xff0c;同时它继承了Comparable接口可以进行数组的排序。 File类的操作包括…

安装 SharePoint 2013 Foundation

一、Foundation版本的区别Foundation版本的区别见附件&#xff08;英文&#xff09;。官网下载地址 http://www.microsoft.com/zh-cn/download/details.aspx?id35488二、安装必备软件三、独立安装模式1. 启动安装向导2. 接受软件许可条款3. 选择服务器类型和数据位置4. 结束安…

Java 异常处理机制

异常处理是程序设计中一个非常重要的方面&#xff0c;也是程序设计的一大难点&#xff0c;从C开始&#xff0c;你也许已经知道如何用if...else...来控制异常了&#xff0c;也许是自发的&#xff0c;然而这种控制异常痛苦&#xff0c;同一个异常或者错误如果多个地方出现&#x…

架构师未来性的基础:简单性

作者&#xff1a;高焕堂&#xff0c;misoo.twqq.com 首页&#xff1a;Backee e架构师未来性的基础&#xff1a;简单性 Apple公司创始人乔布斯(Steve Jobs)曾说到&#xff1a;“简单比复杂更难&#xff0c;你必须努力让你的想法变得清晰…

Android 基础—— 对Context的理解与使用技巧

一、Context 基础概念 1、什么是Context 1) Context是一个抽象类&#xff0c;其通用实现在ContextImpl类中。 2) Context&#xff1a;是一个访问application环境全局信息的接口&#xff0c;通过它可以访问application的资源和相关的类&#xff0c;其主要功能如下&a…

Android 四大组件 —— 广播(广播机制解析)

在网络通信中&#xff0c;一个IP网络范围中最大的IP 地址是被保留作为广播地址来使用的。比如某个网络的IP 范围是192.168.0.XXX&#xff0c;子网掩码是255.255.255.0&#xff0c;那么这个网络的广播地址就是192.168.0.255。广播数据包会被发送到同一网络上的所有端口&#xff…

Android 基础 —— 活动的生存周期

一、返回栈 Android 中的活动是可以层叠的。我们每启动一个新的活动&#xff0c;就会覆盖在原活动之上&#xff0c;然后点击Back 键会销毁最上面的活动&#xff0c;下面的一个活动就会重新显示出来。 其实Android 是使用任务&#xff08;Task&#xff09;来管理活动的&#xff…

产品经理做市场调研和数据分析的方法

产品经理&#xff0c;你对用户的需求了解多少呢&#xff1f;你知道用户想要什么样的产品吗&#xff1f;你想知道用户将会如何看待你的产品吗&#xff1f;你想知道你设计的产品在用户中的口碑如何吗&#xff1f; 是 的。每一个产品经理都希望在产品开始立项设计前&#xff0c;得…

Android 基础 —— 活动的启动模式

活动的启动模式来说应该是个全新的概念&#xff0c;在实际项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。启动模式一共有四种&#xff0c;分别是standard、singleTop、singleTask 和singleInstance &#xff0c; 可以在AndroidManifest.xml 中通过给<activity…

Android 四大组件 —— 服务

一、服务是什么 服务&#xff08;Service&#xff09;是Android 中实现程序后台运行的解决方案&#xff0c;它非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面&#xff0c;即使当程序被切换到后台&#xff0c;或者用户打开了…

Highlighting System

Highlighting System 法线贴图漫反射着色器 Unity论坛&#xff1a;http://forum.unity3d.com/threads/143043-Highlighting-System-Released 需要条件 Requires Unity 3.5.6 or higher.This package requires Unity Pro, as it uses post-processing!Now supports Unity iOS Pr…

Android 基础 —— 模拟实现拨打电话功能

前面已经学习活动的显示跳转及隐式跳转&#xff0c;也学习 TextView 及 Button 两个控件的使用&#xff0c;下面我们来学习 EditText的使用&#xff1a; EditText 是程序用于和用户进行交互的另一个重要控件&#xff0c;它允许用户在控件里输入和编辑内容&#xff0c;并可以在程…