【Java之轨迹】第三章:面向对象总结(多态、instanceof关键字)(中)


◉ 多态

① 编译时类型和运行时类型

变量有两种类型

  • 编译时类型: 变量被声明出来时所指定的类型,该类型在编译时起作用,如果不符合某些规范,就算看起来可以运行,也会因为编译不通过而失败
  • 运行时类型: 变量在编译时可能是一种类型,但运行时可能又是另一种类型,这另一种类型就称为运行时类型

② 多态的表现

冰澈理解:多态的表现是由编译时类型和运行时类型的不同引起的

例子:

public class Main extends Test
{
    @Override
    public void test()
    {
        System.out.println("子类的test方法");
    }

    public static void main(String[] args)
    {
        Test t1 = new Main();
        t1.test();
    }
}

public class Test
{
    public void test()
    {
        System.out.println("父类的test方法");
    }
}

先解释一下Test t1 = new Main();

由于 Test 是 Main 的父类,虽说 Main 继承了 Test 可能比 Test 多出一些东西来,好像是比 Test 更大

但实际上这只是功能更强而不是范围更大
Test 可以被很多类继承,所以范围相对比于 Main 应该更大

所以这句代码的原理是小范围赋值给大范围,是符合逻辑的
(相当于发生了自动类型转换)

就如`long a = 10;`合法一样,将小范围的 int 赋给大范围的 long

那么问题来了,t1.test() 调用的是 Test 的还是 Main 的呢?
我的第一直觉是调用 Test 的,因为 t1 的类型正是 Test
但看一下运行结果:
在这里插入图片描述
O.O 调用的却是子类的

理解:
这就和编译时类型和运行时类型有关系了
编译时 t1 的类型为 Test ,编译器会去 Test 寻找 test() 方法

但在运行的时候,由于 Java 是动态绑定
当运行到Test t1 = new Main();这一句时,虚拟机会在堆内存中开辟出新的对象 Main 并将 t1 指向 Main 这个对象
到这里,t1 的类型就已经从 Test 转化为 Main 了,因为它实质上时指向 Main 这个对象

那么当运行到t1.test();这一句时,首先顺着地址找到的,肯定就是子类 Main 的 test() 方法了,所以运行结果就是调用了子类的。

问题:
那么既然调用的是子类的 test() 方法,可不可以将父类的 test() 方法去掉呢?毕竟没有用到嘛。
答案肯定是不行的~
去掉后的运行结果:
在这里插入图片描述
在这里插入图片描述
首先是重写自检查发挥作用啦 ~
父类已经没有这个方法,重写失败了

那我们把 @Override 去掉看看什么样子:
在这里插入图片描述
对了!前面已经说过,在编译的时候,t1 的类型依旧是 Test,应为还没有进行动态绑定。
这时候编译器就会去 Test 中找 test() 方法,结果找不到,也就报错了
这也说明了 ① 中的看起来可行,但实际上会报错

结论:
上面这个现象就称为多态
类型是一样的,调用的方法也一样,但出来的结果却是不一样的,如果每次绑定的子类都不同的话

只要绑定的子类不同,就算类型相同(都是同一个父类)而且调用的方法也相同,也会呈现不同的结果,这就是多态!

特别注意: 如果父类和子类有同名的变量,那么调用的变量依旧是父类的!

假设:
Main 中有变量String name = "main";
Test 中也有变量String name = "test";
那么 t1.name 的结果为 test

原因是: 变量没有重写的概念,只是描述对象本身的属性,而既然 test 的类型为 Test ,那么该属性 name 就应该是 Test 的而不是子类 Main的。方法之所以会调用子类的,是应为我们主观上去重写了该方法,已经明确了要使用子类的!

③ 多态中的强转问题

前面可以将小范围的子类赋值给大范围的父类,那如果反过来呢?
在这里插入图片描述
可以看到结果是编译报错,就和int a = 12L; 一样,这是就需要强制转化了。

问题: 基本数据类型中,大范围转小范围会出现精度损失问题,但是对象之间并不会出现这个问题,那会变成什么样子呢?(不会出现对象被砍掉一半吧?!)

试试咯:Main ma = (Main)(new Test());,结果是…

Exception in thread "main" java.lang.ClassCastException: Test cannot be cast to Main
编译报错!直接不让通过

那如果改成:

Test te = new Main();
Main ma = (Main)te;

那么结果就正确了~

结论:
多态中可以通过强制转化将父类转化为子类,但前提是在发生转化的时候,该父类的运行时类型必须和子类一致,否则就会出现.ClassCastException类型转化异常

实际上如果分开看,强制类型转化在编译时并不会报错,但在运行时发现强转的前后对象不一致,才会出现错误

但在实际操作中我们可能不知道需要强转的对象是否与目标类型相一致,这时候 instanceof 就可以帮忙了!

④ instanceof 判断对象类型

对于上面的例子,可能会出现运行运行着突然程序中断,这样就太不友好了,我们有义务[doge] 介入处理可能发生的情况
那么使用 instanceof 改为:

Test te = new Test();
if(te instanceof Main)
{
    Main ma = (Main)te;
    System.out.println("强转成功");
}
else
{
    System.out.println("强转失败,te并不是 Main 类型或者 Main 的子类");
}

运行结果:
在这里插入图片描述
这样就友好多了,不会突然蹦一个错误错来黑掉

代码解析:
instanceof 的使用方法: 变量 instanceof 类型
返回值:如果该变量就是该指定类型或者是指定类型的子类,返回true。否则返回false。(返回结果正是进行强制转化需要满足的条件~)

注意事项:instanceof 只能用来判断有继承关系的对象和类型,如果两者根本没有继承关系在,那么在编译阶段就会报错!如:
在这里插入图片描述


private static final boolean ICECLEAN = true; (寒冰小澈)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒冰小澈IceClean

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值