知识点:
多态 异常 抽象类
一、多态
1.1 概念
多态指同一个实体同时具有多种形式
它是面向对象程序设计(OOP
)的一个重要特征。
主要是指同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
好处是:可以把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出 通用的编程,统一调用标准。
水果有两种形态:水果和苹果,不关心买回来的是苹果还是西瓜,只要是水果就行
class Animal{//1. 定义父类 Animal....}class Cat extends Animal{//2. 定义子类 Cat 继承 Animal....}main(){//3.创建子类对象Cat c = new Cat();//小猫是小猫Animal a = new Cat();//小猫是小动物}
1.2 特点
1) 多态的前提 1:是继承
2) 多态的前提 2:要有方法的重写
3) 父类引用指向子类对象,如:Animal a = new Cat();
4) 多态中,编译看左边,运行看右边
1.3 复习多态入门案例
package cn.mbh.opp;
/**
*@author MBH:
*@version 创建时间:2022年7月10日 下午5:43:12
*/
/**本类用于测试多态的入门案例*/
public class Test6_MoreTypeDemo {
public static void main(String[] args) {
//5.创建父类对象进行测试
Animal a = new Animal();
a.eat();//小动物Animal吃啥都行~~~
//6.创建子类对象进行测试
Cat c = new Cat();
c.eat();//小猫Cat爱吃小鱼干~~~
c.jump();
//!!!7.创建多态对象进行测试
/**口诀1 : 父类引用 指向 子类对象*/
/**口诀2 : 编译(保存)看左边,运行(测试)看右边*/
Animal a2 = new Cat();
//eat()是使用的父类的声明,但是使用的是子类的实现方式
a2.eat();//小猫Cat爱吃小鱼干~~~
/**多态的出现:是为了统一调用的标准,向父类看齐,父类提供的功能才能用,子类特有的功能用不了*/
//a2.jump();
}
}
//1.创建父类
class Animal{
//3.定义父类中的成员方法
public void eat() {
System.out.println("小动物Animal吃啥都行~~~");
}
}
//2.创建子类
class Cat extends Animal{
//4.重写父类中的方法--对父类的代码做修改
//重写的规则:方法签名保持一致 + 子类权限修饰符 >= 父类的权限修饰符
public void eat() {
System.out.println("小猫Cat爱吃小鱼干~~~");
}
public void jump() {//8.定义一个子类特有的方法
System.out.println("小猫Cat跳的老高了~~~");
}
}
二、多态的好处
1) 多态可以让我们不用关心某个对象到底是什么具体类型,就可以使用该对象的某些方法。
2) 提高了程序的扩展性和可维护性
class Animal{//1. 创建父类与成员方法public void eat(){ syso("吃啥都行 ") }}class Dog extends Animal{//2. 创建子类并且重写 eat()public void eat(){ syso("小狗吃骨头 ") }}class Cat extends Animal{//3. 创建子类并且重写 eat()public void eat(){ syso("小猫吃小鱼干 ") }}public class TestEat{//这种写法不好 , 虽然每种小动物都可以 eat(), 调用自己对应类型参数的 eat()//但是程序写死了 , 每个 eat() 只能让一种小动物来吃 , 如果有很多种 , 就得写很多的 eat()//而且除了很多已知的动物以外 , 未知的怎么写呢 ?public void eat(Dog dog){//接收的参数是 Dog 类型 , 所以小狗可以吃了 , 而且只有小狗}public void eat(Cat cat){//接收的参数是 Cat 类型 , 所以小猫可以吃了 , 而且只有小猫}public void eat(Panda panda){}public void eat(Pig pig){}.... 不止有很多动物 , 还有一些未知的动物解决方案 : 多态 : 不关心子类类型 , 把子类当做父类来看 – 可以写出更加通用的代码Public void eat(Animal a){//方法的参数不再是子类类型 , 而是父类类型}main(){eat(new Dog());//调用时的参数类型只要是父类型的子类即可}}
三、多态的成员使用
3.1 特点
1) 成员变量:使用的是父类的
2) 成员方法:由于存在重写现象所以使用的是子类的
3) 静态成员:随着对象而存在,谁调用的就返回谁的
3.2 练习 1:多态成员使用测试
创建 Java 工程: DAY06
创建包: cn.tedu. oop
创建类: Test1_DuoTai.java
package cn.mbh.oop;
/**
*@author MBH:
*@version 创建时间:2022年7月10日 下午5:59:28
*/
/**本类用于测试多态之成员变量的使用*/
public class Test1_DuoTai {
public static void main(String[] args) {
//3.创建子类对象进行测试
Dog d = new Dog();
System.out.println(d.sum);//20
d.eat();//小狗要吃肉骨头
d.play();//小狗爱打滚
//4.创建多态对象进行测试
/**2.父类引用指向子类对象*/
/**3.编译看左边,能用的功能必须都是父类提供的*/
/**总结1:多态中,成员变量使用的是父类的*/
Animal a = new Dog();
System.out.println(a.sum);//10
/**总结2:多态中,成员方法使用的是父类的方法声明,子类的方法体(方法实现)*/
a.eat();//小狗要吃肉骨头
/**总结3:多态中,静态资源使用的是父类的*/
a.play();//玩啥都行
}
}
//1.创建父类与成员变量
class Animal{
int sum = 10;
//5.1创建父类的成员方法
public void eat() {
System.out.println("吃啥都行");
}
//6.1在父类创建一个静态play方法
static public void play() {
System.out.println("玩啥都行");
}
}
//2.创建子类与成员变量
/**1.多态的前提:继承+重写*/
class Dog extends Animal{
int sum = 20;
//5.2在子类中重写父类的eat()
public void eat() {
System.out.println("小狗要吃肉骨头");
}
//6.2在子类中创建一个静态play方法
/**注意!!!静态资源属于类,不存在重写!!!只是这两个类中有同样声明的方法而已,不属于重写*/
static public void play() {
System.out.println("小狗爱打滚");
}
}
四、异常
4.1 概述
用来封装错误信息的对象。
组成结构:类型,提示,行号。
TIPS:Ctrl+Shift+T : 查找你想找到的类
Ctrl+T : 查看类的继承结构
Ctrl+O : 查看类的结构信息(大纲)
4.2 异常的继承结构
Throwable - 顶级父类-- Error:系统错误,无法修复-- Exception:可修复的错误--RunTimeException--ClassCastException-- ClassNotFoundException
4.3 异常处理
程序中遇到了异常,通常有两种处理方式:捕获或者向上抛出。
当调用了一个抛出异常的方法时,调用位置可以不做处理继续向上抛出也可以捕获异常。
4.4 练习 2:异常测试
创建 Java 工程: DAY06
创建包: cn.tedu. exception
创建类: Test2_ExceptionDemo.java
package cn.mbh.exception;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
*@author MBH:
*@version 创建时间:2022年7月10日 下午6:16:42
*/
/**本类用于测试异常入门案例*/
/**
* 总结8:如果方法抛出异常,那么谁调用谁需要解决(继续抛出/捕获解决)
* 所以main()调用method3(),也需要抛出异常
* 注意我一般在main()调用之前捕获解决异常,而不是抛给main(),因为没人解决了
* */
public class Test2_ExceptionDemo {
public static void main(String[] args) throws Exception {
//1.创建method(),用来人为暴露异常
//method();
//2.创建method2(),用来进行异常的捕获
//method2();
//3.创建method3(),用来进行异常的抛出
method3();
}
/**抛出的语法:在可能会抛出异常的方法上加throws 异常类型
* 在抛出时,也可以使用多态,不管会发生什么异常,通通被Exception抛出去
* */
//private static void method3() throws
//ArithmeticException,InputMismatchException,Exception{
private static void method3() throws Exception{
//1.复写刚刚可能会发生异常的代码
System.out.println("请输入两个整数:");
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}
/**捕获的语法:
* try{
* 可能会发生异常的代码
* }catch(异常类型 异常参数名){
* 如果捕获到异常的解决方案
* }
*/
private static void method2() {
//1.按照捕获语法编写try-catch结构
/**总结4:try{}里当的是可能会发生异常的代码*/
try {
//2.复写刚刚可能会发生异常的代码
System.out.println("请输入两个整数:");
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
/**总结5:如果发生异常被捕获,就会匹配对应的解决方案*/
} catch (ArithmeticException e) {//3.1异常捕获1/匹配
System.out.println("除数不能为0!");//3.2异常处理1
/**总结6:由于程序中可能存在多种异常,所以catch可以配合多次使用*/
}catch(InputMismatchException e) {//4.1异常捕获2,注意此异常类型需要导包
System.out.println("输入的类型不正确,请输入整数类型!");//4.2异常处理2
/**总结7:如果还有上述两种异常之外的异常,会被Exception匹配捕获
* 这就是多态最为经典的一种用法,我们不关心子类的类型
* 只要是可解决异常,都是Exception的子类,多态会把这些异常当做父类来看,捕获,通过通用方案进行解决
* */
}catch(Exception e) {
System.out.println("请输入正确的整数~");
}
}
private static void method() {
//1.提示并接收用户输入的两个整数:
System.out.println("请输入两个整数:");
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
//2.输出除法运算的结果
//2.1 计算10/0 -- java.lang.ArithmeticException: / by zero [算数异常,除0了]
//2.2 输入9.9 -- java.util.InputMismatchException[输入不匹配异常]
System.out.println(a/b);
/**总结1:不要害怕BUG,真正的勇士敢于直面自己写的BUG*/
/**总结2:学会看报错信息的错误提示,确定自己错误的方向*/
/**总结3:学会看报错信息的行号提示,哪里报错点哪里,注意源码不会错,看自己写的代码*/
}
}
五、抽象类
5.1 概念
Java 中可以定义只有方法声明,没有方法体的方法,这类方法叫做抽象方法
含有抽象方法的类我们称之为抽象类.
抽象类中的方法实现交给子类来完成.
5.2 抽象方法的格式
修饰符 abstract 返回值类型 方法名 ( 参数列表 );
5.3 特点
1) abstract 可以修饰方法或者类
2) 被 abstract 修饰类为抽象类,被 abstract 修饰的方法为抽象方法
3) 抽象类中可以没有抽象方法
4) 如果类中有抽象方法,那该类必须定义为一个抽象类
5) 子类继承了抽象类以后,要么还是一个抽象类,要么就把所有抽象方法都重写
6) 多用于多态中
7) 抽象类不可以被实例化
5.4 练习 3:抽象类入门案例
创建 Java 工程: DAY06
创建包: cn.tedu. oop
创建类: Test3_Abstract.java
package cn.mbh.oop;
/**
*@author MBH:
*@version 创建时间:2022年7月10日 下午6:40:52
*/
/**本类用于测试抽象类*/
public class Test3_Abstract {
public static void main(String[] args) {
//创建多态对象进行测试
Animal2 a = new Dog2();
a.sleep();//调用抽象父类的抽象方法,实现看子类
a.eat();//调用抽象父类的普通方法
/**4.抽象类可以被实例化/创建对象吗?--不能*/
//Animal2 a = new Animal2();
}
}
/**2.如果一个类中包含抽象方法,那么这个类必须声明为一个抽象类*/
//1.创建抽象父类
abstract class Animal2{
//3.抽象类是否可以有普通方法
public void eat() {
System.out.println("吃啥都行!");
}
public void play() {
System.out.println("玩啥都行");
}
//4.创建抽象方法
/**1.没有方法体的方法叫做抽象方法,被abstract修饰*/
abstract public void sleep();
}
//2.创建子类
/**3.1当子类继承了抽象父类后,要么变成一个抽象子类,要么实现父类的所有抽象方法*/
//1.--变成抽象子类abstract class Dog extends Animal{
class Dog2 extends Animal2{
/**3.2子类继承了抽象父类后,如果没有重写父类的所有抽象方法,子类仍然需要是一个抽象类*/
@Override
public void sleep() {//重写抽象方法,就是去掉abstract,然后添加方法实现
System.out.println("重写抽象父类的抽象方法sleep()");
}
}
六、抽象类的用法
6.1 练习 4:抽象类构造函数测试
创建 Java 工程: DAY06
创建包: cn.tedu. oop
创建类: Test4_Abstract2.java
package cn.mbh.oop;
/**
*@author MBH:
*@version 创建时间:2022年7月10日 下午6:50:02
*/
/**本类用于测试抽象类的构造函数的用法*/
/**总结:
* 1.抽象类中可以有构造方法
* 2.父类的构造方法要优先于子类的执行
* 2.抽象类中的构造方法存在的目的,不是为了创建抽象类本身的对象,而是为了创建子类对象时使用的
*/
public class Test4_Abstract2 {
public static void main(String[] args) {
//4.创建多态对象进行测试
Animal3 a = new Dog3();
//5.测试抽象类是否可以创建对象?---不能!
//Animal3 a3 = new Animal3();
}
}
//1.创建抽象父类
abstract class Animal3{
//3.创建抽象类的构造函数
public Animal3() {
//如果此处传参,会覆盖无参构造,子类super就报错了
System.out.println("Animal3....构造方法");
}
}
//2.创建子类
class Dog3 extends Animal3{
//5.创建子类无参构造,无参构造默认存在
public Dog3() {
super();//隐藏着super();//先访问父类的构造方法,再执行自己的功能
System.out.println("Dog3...构造方法");
}
}
七、扩展
7.1 eclipse 如何查看源码方式
我们常常会遇到想查看某些类源码时无法查看的情况,如下图所示,给大家提供两种解决方案:
方案一:
点击下图中所示的”Attach Source”(关联源码)按钮
![](https://img-blog.csdnimg.cn/90f175dd8d8145ab89c73f7ae5469356.png)
接着从系统中选择”src.zip”源码包的位置,点击 OK 就可以显示源码了!
TIPS:一般来说大家的安装目录都是 C:\Program Files\Java\jdk1.8.0_191 这个目录
![](https://img-blog.csdnimg.cn/5bae8517846447cd8445309fe9ff6947.png)
方案二:
1.打开 Eclipse,随机选择一个项目的 JRE System Library,在其中找到 rt.jar 包
![](https://img-blog.csdnimg.cn/0c50f941c67646c9a76549ac23a01135.png)
2.右键选择属性(Properties),接着选择 Java Source Attachment(java 资源附件)中的
External location(外部存储) 在再其下的 Path(路径)中选择 jdk 中的 src.zip 包即可
![](https://img-blog.csdnimg.cn/9437c28367df49d99a5ee5982409d209.png)
TIPS:查看源码:
按下 ctrl 建, 同时鼠标移动到所在类点击即可,这样就可以看到源码啦
7.2 向上转型和向下转型
在 JAVA 中,继承是一个重要的特征,通过 extends 关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
在应用中就存在着两种转型方式,分别是:向上转型和向下转型。
比如:父类 Parent,子类 Child
向上转型:父类的引用指向子类对象 Parent p=new Child();
说明:
向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类的方法就根据这个引用指向调用子类重写方法。
向下转型(较少):子类的引用指向子类对象,过程中必须要采取到强制转型。
Parent p = new Child();//向上转型,此时,p 是 Parent 类型Child c = (Child)p;//此时,把 Parent 类型的 p 转成小类型 Child//其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
7.3 静态变量和实例变量的区别
在语法定义上的区别:静态变量前要加 static 关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间, 才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
7.4 设计多态的程序
假设现在有一个汽车类,我们可以根据汽车类创建很多汽车对象。
1、 创建汽车类。提供启动、停止、运行功能
2、 创建子类,继承汽车类。覆盖
/
重写 启动和停止功能
3、 创建子类对象,进行子类的功能测试
4、 创建多态对象,进行功能测试
// 设计汽车的程序public class Test8_Car {public static void main(String[] args) {//创建子类对象,进行子类的功能测试BMW bm = new BMW();bm.start();bm.end();bm.run();/*创建多态对象,进行功能测试强化 1: 子类只要重写了父类方法 , 父类实现就会被覆盖强化 2: 多态的两大前提:继承 + 重写强化 3: 多态的过程是从小变大 , 是向上转型的过程强化 4: 多态中 , 只能调用父类功能 ( 如果发生重写就是用子类的 ), 初衷是统一调用标准*/Car c = new BMW();c.start();c.end();c.run();}}// 创建汽车类。提供启动、停止、运行功能class Car{public void start(){System.out.println("Fu..start()");}public void end(){System.out.println("Fu..end()");}public void run(){System.out.println("Fu..run()");}}// 创建子类,继承汽车类。覆盖 / 重写 启动和停止功能class BMW extends Car{public void start(){System.out.println("Zi..start()");}public void end(){System.out.println("Zi..end()");}}