面向对象(11-1):继承
面向对象(11):继承
1、继承的概述
(1)多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,
只要继承那个类即可;通过extends关键字可以实现类与类的继承
(2)把多个类中相同的内容提取到另外定义的类中,然后其他类继承这个独立的类
(3)extends:扩大;扩展;延长的意思
2、继承的格式
格式:class 子类名 extends 父类名 {}
单独的这个类称为父类,基类或者超类;
这多个类可以称为子类或者派生类
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员
案例1:父亲/儿子
class Fu{}
class Zi extends Fu{}
案例2:老师与学生
class Student{}
class Teacher{}
改进后:
class Person{} //将老师与学生共有的成员单独提取出来
class Student extends Person{}
class Teacher extends Person{}
3、继承的利弊
利:
(1)提高了代码的复用性(继承之后,相同的代码就不需要再写一遍了)
(2)提高了代码的维护性(需要修改的时候只需要修改父类里面的成员即可)
(3)让类与类之间产生了关系,为后面学习多态做准备
弊:
继承将耦合性增强了
开发的原则:
低耦合,高内聚
耦合:指软件系统结构中各模块间相互联系紧密程度的一种度量,
模块之间联系越紧密,其耦合性就越强,模块的独立性则越差
内聚:是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则
4、案例
//定义一个父类
class Person{
//定义成员变量:姓名和年龄
String name;
int age;
//构造方法(为了方便观看,暂且省略)
//setXxx()和getXxx()
//定义成员方法
public void study(){
System.out.println("学习");
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
//Student是继承自Person的,Student是Person类的派生类/子类
class Student extends Person{
String id;//学生特有的成员变量:学号
public void playGame(){ //学生特有的成员方法:打游戏
System.out.println("打游戏");
}
}
//Teacher是继承自Person的
class Teacher extends Person{
}
//测试类
public class ExtendsDemo {
public static void main(String[] args) {
//创建一个学生对象
Student s = new Student();
s.study();//虽然在学生类中没写,但是学生类继承了父类,父类里面含有学习、吃饭、睡觉
s.eat(); //所以以学生对象调用父类内的成员,仍可以输出
s.sleep();
s.playGame();//学生类特有的方法
//创建一个老师对象
Teacher2 t = new Teacher2();
t.study();
t.eat();
t.sleep();
}
}
执行结果如下:
学习
吃饭
睡觉
打游戏
===========================
学习
吃饭
睡觉
Process finished with exit code 0
5、继承的特点
1、针对于类来说,java它只支持单继承,不支持多继承(多继承不是多层继承)
//错误代码:class Son extends Father,Mother{}
2、java支持多层继承(继承体系)
多层继承体系如下:
class GrandFather{
public void show(){
System.out.println("我是爷爷");
}
}
//爸爸继承爷爷
class Father extends GrandFather{
public void show(){
System.out.println("我是爸爸");
}
}
//儿子继承爸爸
class Son extends Father{
public void show(){
System.out.println("我是儿子");
}
}
//测试类
public class ExtendsDemo {
public static void main(String[] args) {
//创建Son对象
Son m = new Son();
m.show1();//调用自己的方法
m.show2();//调用爸爸的方法
m.show3();//调用爷爷的方法
}
}
执行结果为:
我是儿子
我是爸爸
我是爷爷
Process finished with exit code 0
6、java继承的注意事项
1、子类只能继承父类的所有非私有的成员变量和成员方法,被private修饰的不能继承
2、子类不能继承父类的构造方法,但是要想初始化子类就必须初始化父类
(初始化的时候调用的是构造方法,这里其实是通过一个关键字super去访问的父类构造方法)
3、不要为了部分的功能特地的去使用继承
不要为了部分的功能特地的去使用继承,案例如下:
class A{
show1();
show2();
}
class B{
fun1();
fun2();
fun3();
show1();
}
二者只有show1()方法一样,不适合单独提取出来
7、继承与成员变量的关系
(1)当子类的成员变量与父类的成员变量名字一致的时候,子类方法中访问的变量q会先在该方法内部查找
如果该方法内部没有,那么就去该方法所在的类中去找,如果该类中还没有,那就去父类的成员变量里去找(不能去父类的方法里去找),案例如下:
//创建一个父类
class Fu01{
int a = 10;
public void fun1(){
int a = 100;//方法与方法之间的变量是相互隔离的,fun2访问不了fun1的a
System.out.println(a);
}
}
//创建一个子类
class Zi01{
int b = 20;
public void fun2(){
System.out.println(b);
System.out.println(a);//最后输出的是父类中的成员变量10
}
}
//测试类
public class Demo {
public static void main(String[] args) {
//创建子类对象
Zi01 zi01 = new Zi01();
//调用子类中的fun2方法
zi01.fun2();
}
}
(2)当子类的成员变量与父类的成员变量名字一致的时候,执行程序的时候考虑就近原则;当我们想要父类和子类中相同名字的成员变量都给打印输出来,java为我们提供了一个关键字给我们使用:super
案例1:单层继承访问成员变量
//创建一个父类
class Fu01{
int a = 10;//父类中的成员变量a
public void fun1(){
int a = 100;//父类中的成员方法中的a
System.out.println(a);
}
}
//创建一个子类
class Zi01 extends Fu01{
int a = 40;//子类中的成员变量a
public void fun2(){
int a = 30;//子类中的成员方法中的a
System.out.println(a);//之间子类方法中的a
System.out.println(this.a);//通过this访问子类的成员变量a
//类似于this,我们想要访问父类中的成员变量,使用super
//在子类方法中访问父类的成员变量a
System.out.println(super.a);
}
}
//测试类
public class Demo {
public static void main(String[] args) {
//创建子类对象
Zi01 zi01 = new Zi01();
//调用子类中的fun2方法
zi01.fun2();
}
}
执行结果如下:
30
40
10
Process finished with exit code 0
案例2:多层继承体系访问成员变量
//创建一个爷爷类
class YeYe{
int a = 10;
public void fun1(){
int a = 100;
System.out.println(a);
}
}
//创建一个父亲类
class FuQin extends YeYe{
int a = 40;
public void fun2(){
int a = 30;
System.out.println(a);
}
}
//创建一个儿子类
class Son extends FuQin{
public void fun3(){
System.out.println(a);//该类方法中没有a,那么访问的是继承的那个类中的成员变量a(父亲类中的a)
System.out.println(super.a);//访问的是继承的那个类中的成员变量a(父亲类中的a)
//虽然是多层继承体系,但是不能跨层访问爷爷类的成员变量
//如果想在儿子类的方法中,跨层访问爷爷类的成员变量
//在儿子中创建爷爷对象,然后调用就可以了
YeYe yeYe = new YeYe();
System.out.println(yeYe.a);
}
}
//测试类
public class Demo {
public static void main(String[] args) {
Son son = new Son();//创建儿子类对象
son.fun3();//调用儿子类中的fun3方法
}
}
执行结果如下:
40
40
10
Process finished with exit code 0
案例1、2结论:
super.成员变量 (访问的是父类的成员变量)
this.成员变量 (访问的是本类中成员变量)
同理,得出:
super.成员方法 (访问的是父类的成员方法)
this.成员方法 (不仅可以访问本类中的方法,也可以访问继承自父类中的成员方法)
this访问成员方法的案例:
//在儿子类创建一个方法
public void fun4(){
this.fun1();//儿子类中可以访问爷爷的方法
this.fun2();//儿子类中可以访问父亲的方法
this.fun3();//访问本类中的方法
}
8、继承与构造方法的关系
1、要想初始化子类,就必须初始化父类,而初始化调用的是构造方法
2、子类中所有的构造方法中都会默认含有一个访问父类的无参构造方法的super()
3、当父类没有提供无参构造方法的时候,如何解决?
1、父类提供一个无参构造方法
2、在子类的构造方法中第一句话上使用super访问父类中其他的构造方法,完成父类的初始化
4、注意事项:
1、对构造方法的调用必须是构造器中的第一个语句
2、同一个只能被初始化一次
9、继承与成员方法的关系
1、当子类中的方法与父类中的方法名字不同的时候,根据调用方法的名字不同,调用的方法也不同
案例1:
class Father{
public void fun1(){
System.out.println("这是父类的fun1方法");
}
}
class Son extends Father{
public void fun2(){
System.out.println("这是子类中的fun2方法");
}
}
public class ExtendsDemo1 {
public static void main(String[] args) {
Son son = new Son();
son.fun2();
}
}
执行结果如下:
这是子类中的fun2方法
Process finished with exit code 0
2、当子类中的方法与父类中的方法名字一样的时候
1)先在本类中查找,看看有没有方法,如果有就直接调用
2)如果本类中没有对应名字的方法,就去父类中找
3)如果在父类中也没有找到对应的方法,报错
案例2:
class Father{
public void fun1(){
System.out.println("这是父类的fun1方法");
}
}
class Son extends Father{
public void fun2(){
System.out.println("这是子类中的fun2方法");
}
// public void fun1(){
// System.out.println("这是子类的fun1方法");
// }
}
public class ExtendsDemo1 {
public static void main(String[] args) {
Son son = new Son();
son.fun2();
son.fun1();
}
}
执行结果如下:
这是子类中的fun2方法
这是父类的fun1方法
Process finished with exit code 0
10、方法的重写
1、在继承中,当子类中的方法与父类中的方法名一样的时候,内部实现不一样,这种现象我们称之为方法的重写
又叫做方法的覆盖
2、重写的定义:
子类中的方法名与形参列表以及返回值都和父类一样,只是内部实现不一样
案例:大哥大、小灵通、智能手机
3、面试题:
java中重写与重载的区别:
1、重写是发生在继承的关系中,方法名,参数列表,返回值都和父类一样
2、重载是发生在同一个类中,方法名字一样,参数列表不一样,与返回值无关
4、方法重写的注意事项(存在于继承的关系中)
1、父类中的私有成员方法无法被子类重写
案例:
class Father11{
public void playFootball(){
System.out.println("踢足球");
}
private void fly(){
System.out.println("飞翔");
}
}
class Son11 extends Father11{
//想要重写父类中的一个方法,输入想要重写那个方法的首字母,根据提示选中,回车
//@Override 今后看到这个符号,就代表重写了父类中的方法
@Override
public void playFootball() {
super.playFootball();
}
//当父类中的方法被private修饰的时候,不能重写
//也就是不能使用@Override,如果使用就会报错
}
2、子类重写父类的方法的时候,访问权限不能比父类的访问权限要低;
要么和父类定义方法的权限一致,要么就比它的访问权限高(建议重写的时候,子类与父类中方法定义的格式一致)
例:
父类用修饰符用private,子类不能重写
父类不用修饰符,子类可以不用修饰符,也可以使用修饰符public,但是不能用private
案例:
当把父类中的private去掉,那么子类就可以重写,不会报错
class Father11{
public void playFootball(){
System.out.println("踢足球");
}
void fly(){ //把父类中private去掉
System.out.println("飞翔");
}
}
class Son11 extends Father11{
@Override
public void playFootball() {
super.playFootball();
}
@Override
public void fly() { //子类中可以实现重写
super.fly();
}
}
/*
当把父类中private去掉,子类想要重写父类的方法:
父类没有成员修饰符,子类可以没有成员修饰符,子类也可以有成员修饰符public,
但是子类的修饰符不能用private,不然会报错
*/
3、父类中静态的成员方法,无法被子类重写,静态的成员是属于类本身的东西(方法中加static)
案例:
class Father11{
public void playFootball(){
System.out.println("踢足球");
}
public static void fly(){ //加static修饰
System.out.println("飞翔");
}
}
class Son11 extends Father11{
@Override
public void playFootball() {
super.playFootball();
}
// @Override
// void fly() { 父类中加static修饰,属于静态,子类不能重写
// super.fly(); 这样重写会报错
// }
}

浙公网安备 33010602011771号