Java第四章:类的继承与多态知识点

内容摘要:继承的概念、Java中的Object类、继承的作用、对象实例化内存情况、子类对象实例化内存情况、java中子类的继承性、隐藏、重写、super与构造方法、super与父类成员、final用法、对象的上下转型。

第四章 类的继承与多态

一、java中的继承

1、继承的概念
(1)继承的概念
Java中的继承:子类就是享有父类的属性和方法,并且还存在一定的属性和方法的扩展。
继承就是子类继承父类的特征和行为。
eg;
在这里插入图片描述

在本例中因为IntellgentPhone类中的某些定义语法与Phone中的定义重复,所以让IntellgentPhone类继承Phone类。

(2)继承的语法
*java语言在程序中,如果声明一个类继承另一个类,需要使用extends关键字。
*格式:class 子类 extends 父类{}

eg:
public class A{}
public class B extends A{}

*子类也称派生类、次类、扩展类
*父类也称基类、超类

(3)继承的种类
注:java不支持多继承,c++支持多继承·。
在这里插入图片描述

2、Object类
在这里插入图片描述

java.lang.Object类是所有类的祖先。

注:Object类是唯一一个没有父类的类。

其中:
class A{
}
相当于
class A extends Object{
}
如果缺省extends字句,则该类为java.lang.Object的子类。

附:java中Object类中的方法。
(一)Object():默认构造方法
(二)clone():创建并返回此对象的一个副本
(三)equals():指示某个其他对象是否与此对象相等
(四)finalize():当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法
(五)getClass():返回一个对象的运行时类
(六)hashCode():返回该对象的哈希值
(七)notify():唤醒此对象监视器上等待的单个线程
(八)notifyAll():唤醒此对象监视器上等待的所有线程
(九)toString():返回该对象的字符串表示
(十)wait():导致当前的线程等待,直到其它线程调用此对象的notify()或notifyAll()
(十一)wait(long timeout):导致当前的线程等待调用此对象的notify()或notifyAll()
(十二)wait(long timeout, int nanos):导致当前的线程等待,直到其他线程调用此对象的notify()或notifyAll(),或其他某个线程中断当前线程,或者已超过某个实际时间量
(十三)registerNatives():对本地方法进行注册

3、继承的作用

(1)继承的特点
具有层次结构、子类继承了父类的熟悉那个和方法。
(2)继承的优点
代码的可重用性、父类的属性和方法可用于子类、设计的应用程序变得更加简单、可以轻松的自定义子类。
eg:

在这里插入图片描述

二、java的继承的内存表现

java程序运行时的三个内存区:
堆区:存储new出来的对象,每个对象都包含一个与之对应的class信息。
栈区:栈中只保存基础数据类型的值和对象以及基础数据的引用。
方法区:包含所有的class和static变量。方法区中包含的都是在整个程序中永远的唯一元素,如class,static变量。
在这里插入图片描述

(1)一个对象调用一个方法的内存图
在这里插入图片描述

内存处理步骤:
*首先JVM运行一个class文件时,使用类加载器先将Phone类加载到方法去,然后将main方法压栈(入栈)。
*在栈中运行main方法,当看到局部变量p时,会在栈中开辟一块空间;当看到new Phone()时,会在堆内存中开辟空间,并将堆内存中的对应地址0x123赋值给p;还会拿到方法区的地址值指向方法区。
*在main方法中运行到给对象p属性赋值时,通过地址去堆内存中找到相应属性并赋值,运行到p.sendMessage()这一步时,也是根据地址值去堆内存中找到相应的对象,再用对象去方法区找到sendMessage()方法,然后将sendMessage()方法压入栈中,调用完毕sendMessage()方法会出栈。
*main方法运行结束后会出栈。

(2)两个对象调用同一个方法内存图
在这里插入图片描述

注:二者调用的均为方法区的sendMessage方法。

(3)子类对象构造顺序
在这里插入图片描述

执行顺序:先执行父类构造方法,再执行子类构造方法。在多层继承时,编译器会一直上溯到最初类,再从“上“到”下“依次执行。

(4)子类的对象构造要求
class B { }
public class C extends B {
public C(){
System.out.println(“调用了C类的构造方法”);
}
public static void main(String args[]) {
C c=new C();
}
}
如果在子类中未显示调用父类构造方法,则编译器会自动调用父类的默认构造方法。
每一个class类,如果没有自定义构造方法,那么java编译器会为其设定一个默认的构造方法。

*父类构造方法负责父类中增加变量的初绐化工作,子类构造方法负责子类中新增变量的初绐化工作。

(5)子类对象实例化内存图
在这里插入图片描述

内存处理步骤:
*首先找到主方法,将test类加入到方法区中。再将主方法main压入到栈中。再执行“B b=new B()“。要进行B类的使用,所以将类的内容加入到方法区中,因此将class A, class B加入到方法区中。
*使用new的方式在堆中创建一个子类空间,首先调用父类构造方法进行初始化工作生成父类对象空间,之后再调用子类的构造方法生成子类对象空间。

三、java中子类的继承性

public class A {…}
public class B extends A{}
子类从父类继承方法,使得子类具有父类相同的行为。

注意,子类不能完全继承父类的所有属性和方法。

(1)访问修饰符
*信息隐藏技术是OOP(面向对象程序设计)最重要的功能之一,也是使用访问修饰符的原因。
*信息隐藏的原因包括:
-对任何实现细节所做的更改不会影响使用该类的代码。
-防止用户意外删除数据。
-此类易于使用。

在这里插入图片描述

在这里插入图片描述

(2)子类的继承性
*若子类和父类在一个包内,子类可以继承父类中的访问权限设定为public,protected,default的成员变量和方法。
*若子类和父类不在一个包内,子类可以继承父类中访问权限设定为public,protected的成员变量和方法。

(3)样例分析
eg1:
在这里插入图片描述

错误原因:即使子类与父类在一个包中,但是子类仍旧无法访问父类中的private访问修饰符定义的变量与方法。

eg2:
在这里插入图片描述

错误原因:在父类A中x是私有变量,因此在子类B中无法继承,将B类中的x改为getX()即可。

eg3:
在这里插入图片描述

注意:int y就是缺省(default),若子类与父类在同一个包下,子类可以调用父类中的定义。

eg4:
在这里插入图片描述

错误原因:当子类与父类不在一个包内时,子类只能访问由public与protected定义的变量与方法。

(4)程序执行时内存区的情况

注:虽然private与default定义的变量仍在子类对象空间中存在,但是子类无法访问。

(5)总结

*private类型的属性或方法不能被继承。
*public、protected类型的属性或方法一定可以被继承。
*default类型要看子类是否和父类·在一个包内。

四、隐藏与重写

(一)成员变量的隐藏
1、属性隐藏
*子类对象在寻找方法或成员变量的时候,先搜索子类独有空间,在搜索父类空间。
eg:
在这里插入图片描述

输出:100.
先搜寻的是子类空间中的变量,找到就不再去父类空间中寻找。
2、方法覆盖(重写)
eg:
在这里插入图片描述

输出:我不是子类的方法。

(二)方法重写
方法重写的要求:“两同两小一大”:
两同:方法名相同 参数列表一致
两小:子类返回值类型应该更小或者相等
子类派出的一场小于等于父类抛出的异常
一大:子类的访问权限大于等于父类的访问权限

判断下面程序是否符合方法重写的要求
eg1:
在这里插入图片描述

错误:不满足“一同”,参数列表不一致。

eg2:

在这里插入图片描述

错误:不满足参数列表一致,虽然不会报错,但不是方法重写。

eg3:
在这里插入图片描述

错误:不满足“一大”:子类的访问权限大于等于父类访问权限。红框中的private改成protected,缺省,public均可。改程序代码会报错。

eg4:
在这里插入图片描述

错误:不满足“一小”,子类返回值类型应该更小或者相等。

(三)重写与重载
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同二类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写实在子类存在方法与父类的方法的名字相同,而参数个数与类型一样,返回值也是一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现而方法重写是子类与父类的一种多态性表现。

图示如下:
在这里插入图片描述

(四)隐藏与重写时如何调用
因为子类会隐藏父类的属性和重写父类的方法,那么在继承中,调用步骤如下:
*先去子类找,能找到就使用
*如果找不到,就去父类中找
*如果父类也找不到,就去父类的父类找…
*如果继承树上都找不到,就会报错。

(五)总结
(1)变量只能被隐藏(保活静态和非静态),不能被重写。
(2)可以用子类的静态变量隐藏父类的静态变量,也可以用子类的非静态变量隐藏父类的静态变量。
(3)静态变量(static)只能被隐藏,不能被重写。
(4)非静态方法可以被重写。
(5)不能用子类的静态方法隐藏父类中的非静态方法,否则编译会报错。
(6)不能用子类的非静态方法覆盖父类的静态方法,否则编译会报错。

五、super两个方法

(一)super的用法
1、super与构造函数
*样例分析:
在这里插入图片描述

左侧是正确的代码,右侧的代码会报错。因为子类无法直接访问父类的构造方法。

*执行顺序:先执行父类构造方法,再执行子类构造方法。在多层继承时,编译器会一直上溯到最初类,再从“上”到“下”依次执行。

如果在子类中未显式调用父类的构造方法,则编译器会自动调用父类的默认构造方法,假若父类没有提供默认构造方法,编译时将会出错。

*解决方法
(1)在父类中添加一个无参的构造方法。
在这里插入图片描述

(2)在子类中显式调用父类的有参构造方法。
错误:
在这里插入图片描述

正确:
在这里插入图片描述

*注意:
(1)只有子类的构造方法才能调用父类的构造方法,其他方法调用均会出错。
在这里插入图片描述

(3)super调用,必须是方法体中的第一条语句,否则出错。
在这里插入图片描述

2、使用super访问父类被子类隐藏的成员
*样例分析
在这里插入图片描述

此时可以使用super进行调用父类中的变量的属性和方法。
在这里插入图片描述

(二)super关键字
Super可以用来引用父类的成分,它有两种主要方法:
*引用父类的成员(需要相应的访问权限):
super.变量 或 super.方法([参数列])
*在子类构造方法中调用父类的构造方法:
super([…]);//与this用法类似,应该放在构造方法的第一行位置上。

1、this与super的区别
this:
this是自身的一个对象,代表对象自身,可以理解为:指向对象本身的一个指针。
super:
super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。

2、this与super的内存空间
在这里插入图片描述

3、this与super的使用样例
在这里插入图片描述

(三)总结
*super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)。
*this(参数):调用类中另一种形成的构造函数(应该为构造函数中的第一条语句)。

*super:他引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中的成员数据或函数),基类与派生类中有相同成员定义时如:super.变量名
super.成员函数数据名(实参)
*this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象对象;如果函数的形参与类中的成员数据同名,这是需用this来指明成员变量名)

*调用this()必须在构造方法的第一行,
调用super()必须卸载子类构方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super()。

六、final用法

(一)final描述成员变量
1、final修饰类中的成员变量
表示该属性一旦被初始化编不可改变(常量),这里不可改变的意思是对基本类型来说是其值不改变,而对对象属性来说其引用不可改变,引用空间的值是可以改变的。

*样例分析:
在这里插入图片描述

说明:左侧在定义count变量时没有进行初始化,所以会报错。右侧的代码对左侧进行了修改,右上侧的代码通过对count变量赋初值来进行修改,右下侧代码通过在构造函数中对变量赋初值来进行修改。

注意:
(1)初始化只能进行一次,后面在进行初始化便会报错。
在这里插入图片描述

(2)不能修改final已经定义的变量的值。
在这里插入图片描述

(3)不能修改final定义的引用类型,或者指向的内存空间,但是可以修改指向空间的内部值。
在这里插入图片描述

(4)final变量就是常量,而且通常变量名要大写。
private final int COUNT = 10;

(二)final修饰类中的方法
说明这种方法提供的功能已经满足当前需求,不需要进行扩展,并且也不允许任何从此类继承来的类来重写这种方法,但是仍然可以继承这个方法,也就是说可以直接使用。
在声明类中,一个final方法只能被实现一次。

*样例分析
!!!不能重写,但是可以使用。
在这里插入图片描述

(三)final表示类
无法被任何其他类继承的,意味着次类在一个继承书中是一个叶子类,并且此类的设计已经被人为很完美而不需要进行修改或扩展。通常叫做最终类。
*样例分析
在这里插入图片描述

把类定义成final,实现细节不允许有任何改动、在创建对象模型的时候,缺心这个类不会再被扩展。

(四)总结
(1)修饰变量,为常量,值不可变。
(2)修饰对象,值可变,引用不变。
(3)修饰方法,方法不可以重写。
(4)修饰类,无子类,不可以被继承,更不能重写。

七、对象的多态性

(一)对象上转型
(1)多态概念
Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。
编译时类型由声明该变量时使用的类型决定。
运行时类型由实际赋给该变量的对象决定。
如果编译时类型和运行时类型不一致,就可能出现所谓的多态。

(2)向上转型(upcasting)
因为子类其实是一种特殊的父类,因此java允许把一个子类对象直接赋给一个父类引用变量,无需任何类型转换,或者被称为向上转型,向上转型由系统自动完成。
例如:父类 对象名称=new 子类()

例如:

*样例分析
在这里插入图片描述

编译时定义类A的对象B1,又因为A类中有方法f1所以通过编译。
运行时B1是new出来的B类的对象,运行时调用方法f1,所以输出的是B类中的f1方法。

*样例分析
在这里插入图片描述

因为A类中没有f2方法,所以编译时会报错。

*样例分析
在这里插入图片描述

运行结果:10.因为实例变量并不具有多态性,因此控制输出在A类中定义的值。只有方法有多态性,成员变量没有多态性。

*样例分析
在这里插入图片描述

输出结果:10。因为静态方法与静态变量都不具有多态性。因此只输出A中定义的静态变量。

*样例分析
在这里插入图片描述

B1调用的是A中的f1()方法。
B2调用的是B中的f1()方法。

(二)向下转型
*样例分析(错误)
在这里插入图片描述

如果假设B类是狮子类,A类是动物类,如果如图中定义说明所有的动物都是狮子,不满足实际,所以错误。

*样例分析(错误)
在这里插入图片描述

*样例分析(向下转型)

在这里插入图片描述

(二)总结
(1)多态的前提条件:
子类继承父类,子类重写父类方法,父类引用指向子类对象
(2)多态的成员特点:
用父类引用指向子类对象的时候
*成员变量不变,调用结果为父类的成员变量的值
*成员方法改变,调用结果为子类的成员方法得结果
*静态成员方法不变,调用结果为夫剋的静态成员方法

引用成员之间的转换:
*向上转型:子类转换成父类,由小到大 基本数据类型的自动类型转换
*向下转型:父类转换成子类,由大到小 基本数据类型的强制类型转换

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值