面向对象(继承)
版权声明
- 本文原创作者:清风不渡
- 博客地址:https://blog.csdn.net/WXKKang
引言
上一篇我们学习了Java面向对象的第一个特征——封装,这一篇我们就来学习它的第二个特征——继承,那么,什么是继承呢? 我们可以想一下,既然是“继承”,那么它必须是在两个或多个类之间所发生的关系吧,这样我们就可以说:这个类继承自什么类,假如只有一个类的话,它既无法继承别的类,而且也没有别的类来继承它,这就构不成什么继承关系了哈,理清楚了这一层关系之后我们再来谈谈它继承了什么呢? 既然说什么什么类继承自什么什么类,那它肯定要从继承的那个类中继承点什么呀!我们把发生继承关系的这两个类称为父类和子类,子类可以继承父类的属性和方法,这个就是继承的基本概念,下面我们来系统的学习一下吧 ~ ~
一、类关系
1、现实中的关系
恐龙 | 是 | 动物 |
---|---|---|
– | – | – |
球队 | 包含 | 球员 |
我们要知道,这是完全不同的两种关系,说明如下:
- 恐龙是动物,反而言之如果没有了恐龙这一类动物的话,动物这一说法还是存在的,因为还有其他的如小狗,小猫也是动物呀
- 球队包含球员,即球员组成球队,如果说缺少一个或者多个球员的话,它就不能称之为球队了,因为人数不够呀
理解了这两种关系之后我们来将它延伸到我们的Java中吧:
2、Java中的关系
Java中类的关系有两种:is-a关系、has-a关系。
关系 | 说明 | 核心特征 | 示例 |
---|---|---|---|
is-a | 表示继承关系,即两个类是属于同一种类 | 分类 | 恐龙是动物 |
has-a | 表示组合关系,即多个类共同组成一个类 | 组成 | 球员组成球队 |
3、Java类的老祖宗
Object类,java中所有的类都直接或间接的继承自它
二、继承
继承是面向对象程序的一个基本特征,通过继承可以实现父子关系(被继承的称为父类,实现继承的类称为子类),以及代码的复用,例如:
每个人都具有姓名及年龄,并且具有说话的方法,而在这个基础之上学生则自己具有学习的方法,工人自己具有工作的方法
我们发现,学生和工人都具有姓名及年龄的属性及说话的方法,我们可不可以创建一个人类,它具有姓名和年龄这两个属性及说话的方法,让学生和工人继承它,这样它们就都具有对应的属性和方法了呢?当然是可以的,让我们来一步步实现继承吧:
1、继承的实现
Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类称为父类,有的也称其为基类、超类。父类和子类的关系,是一种一般和特殊的关系,也可以认为父类是大类,子类是小类
语法如下:
修饰符 class 子类的名字 extends 父类的名字{
//类体
}
其实,extends关键字在英文中的意思是扩展而不是继承!这个关键字很好的体现了父类和子类之间的关系:子类是对父类的一种扩展,子类是一种特殊的父类。从这个意义上来看使用继承来描述子类和父类的关系是不够准确的,而用扩展则更恰当一些
继承的特点:
- 描述类与类之间的关系
- 降低类与类之间的重复代码
- 降低对象与对象之间的代码重复使用静态变量
2、extends关键字
Java中不支持类似C++中的多重继承特性,Java只支持单继承,即每个类最多只能继承一个父类,在类的声明中,我们通过一个extends关键字来声明继承关系。
Java类体系中的根类是java.lang.Object,所有类都是Object类的直接子类或者间接子类,如果定义一个类时没有使用extends关键字,那么则默认继承自Object类
3、继承知识点
- Java只支持单继承,即只能有且只有一个父类
- 被继承的类称为父类(基类),继承的类称为子类
- 静态变量和静态方法都可以通过子类名.父类静态成员变量名(或方法名)的形式调用
- 父类的私有成员变量和私有方法(即private修饰)不能被继承
- 构造函数不能被继承
- 子类中可以增加新的属性或方法
- 不要为了使用继承而继承,例如让工人类继承学生类,要符合逻辑和基本意识
三、方法的重写
重写从字面意思上就可以理解,就是当父类中的某一个方法不满足我们的需求时,我们需要重新写一下父类中的方法,但可不是直接去父类中直接修改啊,是在子类中重写父类的方法,下面我们就来学习一下方法的重写吧:
在继承中,子类可以定义和父类相同的名称且参数列表一致的函数,这种函数称之为函数的重写。简而言之,重写方法就是在子类中重新定义父类中已有的方法。
1、重写的要求
- 在子类中重写的方法和在父类中被重写的方法,它们的方法名称和参数列表必须相同
- 在子类中重写的方法的访问权限必须等于或大于父类中对应方法的访问权限,否则编译报错(public > protected > default > private)
- 子类中重写方法的返回值类型必须是父类中方法的返回值类型或该类型的子类,不能返回比父类返回值数据类型范围更大的数据类型
- 子类中重写的方法不能比父类中被重写的方法产生更多的异常类型
- 父类的构造函数是不能被子类所继承的,因此不能在子类中重写。但是在子类中可以使用super关键字访问父类中的构造函数
2、重写方法调用顺序
子类的对象调用方法时按就近原则优先调用子类重写后的方法
3、子类中调用父类中的方法
super.方法名();
4、方法的重写与重载的区别
- 重写用于实现继承关系的父子类中,不能用于同一个类中。重写是一种更改父类(或接口)中方法的行为
- 重载通常用于同一个类中,重载用于一个行为提供多种实现方式
- 重载的特点:函数名相同,参数列表不同,与访问权限修饰符和返回值类型无关
四、构造函数和super
1、示例
我们以人类和学生类来做一个示例,通过示例来理解它们吧:
People类代码:
package cn.com;
/*
原创作者:清风不渡
博客地址:https://blog.csdn.net/WXKKang
*/
public class People {
//公有属性
public String name;
public String age;
//私有属性
private String salary;
//people类构造函数
public People() {
super();
}
//普通方法
public void speak() {
System.out.println("people can speak !!");
}
public void play() {
System.out.println("人们可以玩很多好玩的");
}
//私有化方法
private void message() {
System.out.println("这是人类的信息库!!");
}
}
继承自People类的Student类:
package cn.com;
/*
原创作者:清风不渡
博客地址:https://blog.csdn.net/WXKKang
*/
public class Student extends People{
//学号 学生自有的属性
public String studentId;
//无参构造函数
public Student() {
super();
}
//学习方法
public void study() {
//通过super关键字调用父类speak方法
super.speak();
System.out.println("学生需要学习很多知识");
}
//重写父类play方法
@Override
public void play() {
System.out.println("学生不能玩呀,要加班写作业嘞");
}
}
测试类代码:
package cn.com;
/*
原创作者:清风不渡
博客地址:https://blog.csdn.net/WXKKang
*/
public class Demo {
public static void main(String[] args) {
//创建学生对象
Student student = new Student();
//通过学生对象调用People类中的name属性并赋值,然后打印
student.name = "wly";
System.out.println("姓名:"+student.name);
System.out.println("---------------------");
//通过学生对象调用父类公用方法
student.speak();
System.out.println("---------------------");
//通过学生对象调用本类中的study方法,测试子类通过super调用父类方法
student.study();
System.out.println("---------------------");
//通过学生对象调用重写后的方法,测试重写效果
student.play();
}
}
运行结果:
从上面这个例子中,我们就可以体现出继承的一些特点,下面做以说明:
- 首先,父类中的私有化属性(salary)不能被继承,如果使用子类的实例对象来访问的话会编译报错
- 同样,父类中的私有化方法(message)也不能被继承,如果使用子类的实例化对象来访问的话同样会编译报错
- 而父类中的公有化方法(如speak,play)则可以被子类继承,我们可以通过子类的实例对象来访问父类中的公有方法
- 可以通过super.父类方法名来在子类中调用父类中的方法(如本示例中子类的study方法中就使用该方式调用了父类中的speak方法)
- 我们可以在子类中复写继承自父类中的方法(如本示例中子类复写了父类的play方法)
上面我们通过一个例子来实现了一下继承及继承的相关操作,有没有发现super和this的功能有些相似呢?现在我们就来将它们两做一个小结吧 :
- this和super很像,但this指向当前对象,而super指向当前对象的父类
2、构造函数
当我们创建子类对象的时候,由于必须先初始化父类属性,所以必须先执行父类的构造方法再执行子类的构造方法
子类不能继承父类的构造函数,但子类的构造函数里必须先调用(隐式调用或显式调用)父类的构造函数并初始化父类属性后才调用自己的构造函数,下面我们来说说隐式调用和显示调用
隐式调用: 在子类的构造函数中,如果没有显式的调用父类中的构造函数,那么就会默认调用父类中的无参构造函数,如果父类中没有无参构造函数则编译报错
显式调用: 如果在子类中使用super显式调用父类中带参的构造函数,那么编译器不会调用父类中的无参构造函数的代码,并且,调用父类构造函数的super只能放在子类的构造函数的第一行,且只能调用一次
注意: 在同一个构造函数中,不能同时存在this()和super(),因为它们都要求放在构造函数的第一行代码
五、final关键字
final关键字主要用于修饰类、属性、方法以及方法的形参,下面我们就来逐一进行学习吧
1、修饰类
当final放在类之前(修饰类)的时候,该类不能够被继承
如果类没有必要进行拓展,为了防止代码功能被重写,通常将类加final修饰,这样将限制类不会有继承的子类,如果编写了它的子类,编译时就会报错
2、修饰成员属性
final放在变量之前,就是定义一个常量,表示值不能被修改
在实际项目中,final关键字一般和static关键字结合使用,这样可以使常量优先加载,不必等到创建对象时再初始化,而且访问时不需要事先创建对象,直接使用类名就可以访问
3、final修饰方法
final放在方法之前,该方法不可以被重写
如果父类中的函数不希望被子类继承并重写,可以将该函数使用final修饰
4、final修饰形参
当形参被修饰为final之后,那么该形参在所属方法中不可以修改,即不可以被重新赋值
好啦,今天的学习就到这里吧!记录学习,记录生活,我还是那个java界的小学生,一起努力吧!!
欢迎各位看官评论探讨哟 ~ ~ 小生在此谢过了 ~ ~