今天,我们将要讨论的内容是Java里面类的继承的相关概念。
说到继承,我相信大家都不陌生。生活中,子承父业,子女继承父母的财产,这就是继承。实际上,Java里的继承也是如此。对于一个类来说,它的数据成员和方法就是它的财产,而申明另一个类接收了这个类的财产,这就是Java里的继承。
接下来我将就几个方面,谈一谈继承的相关知识。
一、继承的基本格式与意义
在上文,我们了解了什么叫做继承。那么,如何使用继承?
继承的关键词是extends。继承的一般格式为:public class 类名 extends 类名{};
假定有两个类,A、B,在申明B类的时候,我们要求B类继承A类的数据成员与方法。
具体实现如下:
继承有两大意义。一是提高了代码的重用性,二是提高了程序的扩展性。继承能使得我们的代码更加简易,同时也更加灵活。
二、什么时候使用继承
上文我们讲到,继承能提高代码的重用性,提高程序的扩展性。那么,我们不禁要问,什么时候,我们可以使用到继承?
我的理解是,当两个类存在包含关系的时候,我们为了简化程序,可以使用到继承。
举个例子,车是一个类,公交车也是一个类。两个类存在包含关系。那么,在申明公交车这个类的时候,对于一些车共有的数据成员和方法,我们可以让它继承车这个类从而获取这些属性和方法。而对于一些公交车特有的属性和方法,我们可以在公交车类当中进行添加。比如说,定义一个车类,它有三个属性值,长度、宽度、重量。而定义一个公交类,我们需要添加上一个takepeople的方法。
具体实现如下:
三、继承的内容
大家可能会注意到一个细节,那就是上文当中我编写的代码,无论是数据成员还是方法,我给它的访问修饰符都是public。我们知道,在Java里面,访问修饰符总共有四种,那么,子类是否能够继承下来父类所有的属性和方法呢?事实上,子类是可以接收父类所有的属性和方法的,也就是说,无论访问修饰符是public还是private,子类都能够从父类那里接收到。但是,对于私有域,子类是不能够去访问的。而Java中关于继承的定义是获取可以访问的属性和方法,所以,我们说私有量是不被继承的。
四、方法重写
什么叫方法重写?方法的重写就是在一个子类中,重写它继承的父类的方法的过程。重写的前提是,首先要有至少两个存在继承关系的类。其次,子类在重写方法的过程中,方法的访问修饰符必须大于或者等于父类中方法的访问修饰符。重写不能改变方法的方法名、返回值类型、参数,它只能更改方法体内的内容。注意一点,方法重写后不能与原方法相同,否则不能称为重写。
比如,申明一个学生类,一个大学生类,让大学生类继承学生类,重写学习方法,具体代码如下:
这就是方法重写的具体举例。
五、super关键字
接下来,我们来认识一下super关键字。
谈到super,我们可以联系到之前讨论过的this关键字。关于this,我们可以用this.方法(数据成员)这样的方法去调用当前类的属性和方法,也可以用this(参数类型 参数名)这样的方式去调用当前类的构造方法。this是指向当前类的一个类似指针的引用,super与this类似,它指向的是当前类的父类。我们注意到,当你的父类存在参数不为空的构造方法的时候,往往你的子类就会出现报错的情况。这个时候,我们就需要用到super去调用父类的构造方法。继续以上文所举的学生与大学生为例,我们在学生类当中添加一个带参的构造方法。具体代码如下:
六、自动转型与强制转型
有了继承与方法重写,接下来,我们来认识一下自动转型和强制转型。
在这之前,我们先来回想一下类的实例化的过程。实例化类的格式是:类名 对象名=new 构造方法名(参数类型 参数...)。
自动转型的格式有两种,第一种:父类名 对象名=new 子类构造方法(参数类型 参数名...)。
第二种:public void method(父类名 参数名){
}
子类名 对象名a = new 子类构造方法(参数值,...);
父类名 对象名b = new 子类构造方法(参数值,...);
method(对象名a);
method(对象名b);
我们知道,经过自动转型后,我们将不能调用子类添加的方法和数据成员,那么,我们不禁要问,转型后的对象所调用的方法,究竟是掉的父类的方法还是子类经过重写后的方法呢?我们不妨来做一次测试,代码如下:
让我们再来看一看运行结果:
没错,结果显示,我们调用的是子类重写后的study方法,为什么呢?
我们之前论述过,实例化类的过程。类名 对象名,在栈中开辟一块内存,然后记录下对象名。new 构造方法名(参数类型 参数名...),这是在堆中开辟出一块内存并存储下构造方法所在类的数据成员和方法。=,在两块内存之间建立连接,并且在栈中对象名所在位置添加上数据所在堆的位置。而在自动转型的过程中,因为我们调用的是子类的构造方法,所以,我们获取的是子类的study方法。事实上,子类中新增的属性和方法,同样存在在内存当中,而因为对象是父类对象,所以新增的属性和方法则被隐藏起来无法调用。
认识了自动转型,接下来我们来认识一下强制转型。在翻阅别人的博客过程中,我看到一种举例,非常喜欢,所以,我也将用这个例子向大家解释下强制转型。在我看来,继承一般是存在与具有包含关系的两个及以上的类之间。比如说,动物与狗。狗是动物的子类。什么是自动转型,自动转型就是申明一个动物引用指向狗。简而言之,就是将狗转型成动物。很明显,狗是动物,所以,在转型过程中,不会存在什么问题。但是,强制转型的话,就容易出现一些问题。动物转型成狗,换句话说,是动物就是狗。这很明显是不正确。所以,除非你判定这个动物是狗,你才能使用强制转型,否则,计算机将会报错。举个例子,首先你定义一个狗,然后你通过自动转型将它转型成动物,之后你再通过强制转型将它变回狗,这样的强制转型是允许的。在自动转型中,子类添加的属性和方法被隐藏,在强制转型后,被隐藏的内容得以恢复。除了这种情况,我们还可以采用判断这个动物是不是狗之后再确定是否进行强制转型的方法。这里介绍一个新的关键字,instanceof。我们可以通过它去判断这个动物究竟是不是狗。用法如:animal instanceof dog。
七、接口
最后,我要讲到的是接口的部分相关知识。我们知道,Java不同于C++,Java中只允许继承一个父类。显然,在有的时候,只能继承一个父类是不能够满足我们的需求的。所以,Java引入了另一个概念,接口。继承一个类,我们称为继承。对于接口,我们用实现,实现一个接口。我们知道,类里面有抽象类的说法,在我的理解中,接口是比抽象类更抽象的东西。定义一个接口的格式:
对于接口里面的属性,它必须是一个静态数据,而在接口里定义的方法,一定是一个抽象方法。这就是关于接口的定义。
实现接口的关键字是implements。需要注意的是,当你实现接口后,而你的类又不是一个抽象类的话,那么,你需要重写接口内所有的抽象方法。很好理解,因为你继承了抽象方法,而你又不是一个抽象类,自然就会报错了。
关于类的继承方面的知识,我暂时就讨论这么多,期待大家的指正,欢迎大家一起来探讨。