Java向下转型与抽象类

文章详细阐述了Java中的向上转型、final关键字的使用以及多态的概念和实现。向上转型允许通过父类引用调用子类的方法,实现参数统一化;final关键字用于修饰属性、方法和类,确保其不可变或不可被继承;多态则强调在继承体系下,通过父类引用调用子类重写的方法,实现不同的行为。此外,文章还提到了方法重写、重载的原则以及抽象类的作用,强调了抽象方法必须在子类中被覆写。

0、向上转型复习

(1)类和类之间的关系
        ①一种是继承关系,必须满足is a原则(Dog is an Animal)
        ②另一种组合关系,组合关系has a原则,表示一种包含关系被包含的类中的属性与方法也在包含的类中(School has a Student)
(2)protected修饰符
        ①在不同包的具有继承关系的类之间,在子类之中可以访问父类中protected权限的属性与方法,在子类之外,通过子类对象仍然无法访问protected域
        ②在同一个包中的不同类(不一定有继承关系),可以访问彼此protected修饰的属性与方法
        ③四大权限修饰符都针对的是在哪些类\包中的可见性问题,与具体对象无关!!
(3)多态∶
        ①定义:相同的方法/行为,经由不同的对象,表现出了不同的行为,这种特性称为多态性也称为动态绑定(编译阶段无法确定参数具体类型,运行时调用才知道具体是什么)
        ②实现多态有三个必不可少的要求
        · 必须在继承体系下使用多态
        · 使用的方法必须经过子类重写
        · 最终通过父类的引用来调用重写方法(向上转型)
(4)方法重写与方法重载
        ①方法重载:在同一个类中,定义了若干个方法名称相同,参数列表类型或个数不同的一组方法,称为重载方法。方法重载与返回值无关 -> 静态绑定(在编译阶段通过方法参数就能确定到底调用的是哪个方法)
        ②方法重写:在有继承关系的类之间,子类定义了和父类除了权限不同之外,方法名称,参数列表,返回值(向上转型的返回值例外)都相同的方法称为子类重写了父类的方法
        · 子类方法权限>=父类方法权限,private权限不包含在内
        · 关于重写方法的返回值,除了向上转型以外,返回值类型必须都相同,毫无相关的类返回值,基本类型不同的返回值都不算重写方法!
(5)向上转型︰
        ①语法:父类名称 父类引用 = new子类实例(); //天然发生的向上转型
        ②调用【父类引用.方法();】时
        · 这个方法到底调用的是子类中的方法还是父类中的方法,只要new的对象所在的子类重写了该方法,则一定调用子类重写后的方法(以子类为准,优先在子类中寻找该方法)只有子类没有重写该方法,才会调用父类中的方法
        ③向上转型最大优势在于参数统一化,通过父类引用可以接收所有的子类对象

1、final关键字∶称为Java中的终结器

(1)final修饰属性
        ①final修饰的属性称为常量,被final修饰的变量,一旦初始化之后,值不能修改。
        ②被final修饰的引用类型,仍然是值不能改,保存的地址不能改。也就是说,引用只能指向当前对象,不能更改指向,至于对象内部如何修改都是可以的。
(2)final修饰方法
        ①final修饰方法,表示该方法不能被覆写!
        · 无论当前类有多少子类,通过任何对象调用test方法,都只有这一种行为。若父类中的有些方法/行为,不允许子类随意修改,将该方法使用final修饰
(3)final修饰类
        ①final修饰类,表示该类无法被继承,该类没有任何子类!
        · 若当前类中的所有属性与方法都不允许随意修改/覆盖,则使用final修饰,只要没有任何子类就能保证所有人用到的这个类的对象,大家都是一样的表现
        · JDK中String就使用了final进行修饰

2、向上转型

(1)父类名称 父类引用 = new 子类实例();
        ①到底能通过父类引用"∵"(调用哪些方法),要看父类,这些方法必须在父类中定义过
        ②至于调用的这个方法到底表现出怎样的行为,要看new的哪个子类,若子类重写了该方法,则调用子类重写后的方法

· 若需要调用Dog类中独有的bite方法,就需要脱掉Animal外衣,还原为Dog类的引用才可以。这种操作就称为向下转型

 

3、向下转型

(1)语法:

        子类名称 子类实例 = (子类名称) 父类引用
(2)若Animal类有吃饭睡觉方法,狗有咬方法,此时

Animal animal = new Dog();
animal.bite(); //此时无法访问

必须通过向下转型

Dog dog = (Dog) animal; //注意后面是animal不是Animal!!!
dog.bite(); //此时才可实现bite方法

(3)向下转型由于存在类型的强转,有可能会出现类型转换异常。
        ①向下转型必须发生在有继承关系的类之间,毫无关系的类,对象不能发生向下转型(强转)
        ②发生向下转型之前,必须先发生向上转型!!!建立父类引用和具体子类实例的关系~~
(4)由于向下转型存在类型转换异常的风险,引入了instanceof关键字
        ①用法: 引用名称 instanceof 类名称
        ②返回布尔值
        · true:表示当前引用确实指向对应类的实例,可以强转
        · false:表示当前引用与对应类的实例毫无关系,不能强转
        ③一般使用该关键字搭配分支语句来进行向下转型

if(animal instanceof Dog){
    Dog dog = (Dog) animal;
}

(5)什么时候会用到向下转型
        ①通过相同的父类引用接收了子类实例之后

fun(Animal animal) {
    //需要在fun方法中,调用某个具体子类独有的方法/属性,需要用到向下转型
}

 

4、向下转型的具体应用

(1)Object类的equals方法
        · Object类是Java中所有类的父类,所有类默认继承自Object类
        · toString():将当前对象转变为字符串对象,默认的方法就是【输出包名称.类名称@处理后的地址】
        · 若我们需要在当前类中打印输出某些属性值,覆写toString方法即可
(2)关于对象比较相等
        · "=="操作符只能比较两个引用的值是否相同:两个引用指向的对象是否是同一个对象
        · 若需要判断两个不同对象属性值是否相同,需要使用equals方法来判断,必须要学会equals方法的覆写
(3)equals代码
①默认的equals方法代码
 

public boolean equals (Object obj) { //Object类是所有类的父类,因此都可以向上转型通过Object类型来接收
        return (this == obj);
}

        · 和使用 "==" 效果一致,判断传入的对象到底是不是同一个对象
②覆写equals方法

public boolean equals(Object obj){
        //判断当前对象是否和传入引用指向同一个对象
        if(this == obj){
                return true;
        }
        //判断obj这个引用是否指向的就是Student类型的对象
        if(!(obj instanceof Student)){
                return false;
        }else{
        //说明此时obj确实是Student类型的对象,且和当前对象不是同一个对象
        //需要比较当前对象的属性和传入对象的属性是否相等
        //此时需要还原obj引用为当前Student类型的引用
                Student stu = (Student) obj;
                return this.name == stu.name && this.age == stu.age;
        }
}

 

5、面向对象开发的三大特性

        封装(保护性/易用性)、继承(代码复用)、多态(参数统一化)
(1)多态的优势︰参数统一化,降低圈复杂度(避免使用大量的if-else分支)
(2)实现Sharp父类,Triangle、Cycle、Rectangle子类的draw()方法
        ①不使用多态时:

//假设用户现在需要打印多个形状,没有多态
Cycle cycle = new Cycle();
Triangle triangle = new Triangle();
Rectangle rectangle = new Rectangle();

string[] sharps = {"cycle","triangle","cycle","rectangle"}; //用户需要打印这四个图形
for (String s : sharps){ //遍历用户指令,按照指令打印对应的图形
        if (s.equals( "cycle")){
                cycle.draw( );
        }else if (s.equals("triangle")) {
                triangle.draw();
        }else if (s.equals( "rectangle")){
                rectangle.draw();
        }
}

        · 有多少种图形类,就得有多少个分支(if和else if)当现在拓展了新的图形类,就得修改用户的使用代码,增加分支
②使用多态来降低圈复杂度

//无论是什么图形,最终都保存在Sharp对象数组中
Sharp[] sharps = {
    new cycle(),
    new Rectangle(),
    new Triangle(),
    new Flower()    
}; //打印该图形,无论啥图形,最终都要调用sharp中的draw方法进行打印

for (Sharp sharp : sharps) {
    sharp.draw();
}

        · 再拓展新的子类时,对于打印输出,一行代码都不变
 要想使用多态,必须得有继承。子类还必须得覆写相关的方法。没有方法重写,就没有多态,若子类中不覆写这个方法,父类无法强制要求子类覆写方法,于是产生了抽象类

6、抽象类

(1)定义︰所谓的抽象类就是比普通类多了一些抽象方法而已,是普通类的超集(普通类有的,抽象类都有)!
抽象类和抽象方法使用abstract关键字来定义,抽象方法所在的类一定是抽象类
(2)抽象类和抽象方法的定义

public abstract class Animal{
        public abstract void eat();
}

        · 抽象方法所在的类一定是抽象类,抽象类使用abstract来声明,可以不包含任何抽象方法抽象方法使用abstract来声明,只有方法声明,不包含方法体!!! !!
        · 抽象类必须有子类!!抽象方法必须被子类覆写(子类是普通类)就可以对子类中的行为做出要求,这样的话就可以更好的保证多态的正确运行
        · abstract和final这俩关键字不能同时出现
        · abstract和private这俩关键字不能同时出现,private修饰的方法不能重写,abstract修饰的方法必须被重写
(3)子类继承抽象类,必须覆写抽象类中的所有抽象方法(前提∶子类是普通类);若子类也是抽象类,可以选择性的覆写抽象方法(也可以不写)

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值