栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Java笔记】多态

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Java笔记】多态

多态

在介绍多态的概念前,我们先介绍一下向上转型作为引子。

一、向上转型

一个Person类型的变量,可以指向一个Student类型的实例:

Person p = new Student();

这是因为Student继承自Person,因此,它拥有Person的全部功能。Person类型的变量,如果指向Student类型的实例,对它进行操作,是没有问题的!

这种把一个子类类型安全地变为父类甚至祖宗类类型的赋值,被称为向上转型(upcasting)

向上转型实际上是把一个子类型安全地变为更加抽象的父类型:

Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok

注意到继承树是Student > Person > Object,所以,可以把Student类型转型为Person,或者更高层次的Object。

二、多态

多态(Polymorphic)多态指的是同一个行为具有多个不同表现形式。也就是说,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。

如何实现多态?多态的实现具有三种必要条件:

  1. 继承
  2. 重写父类方法
  3. 父类引用指向子类对象

比如下面这段代码:

class Fruit {
    public void eat() {
        System.out.println("eat Fruits");
    }
}

class Apple extends Fruit { // 1.继承
    @Override
    public void eat() { // 2.重写父类方法
        System.out.println("eat Apples");
    }

    public static void main(String[] args) {
        Fruit fruit = new Apple(); // 3.父类引用指向子类对象
        fruit.eat(); // eat Apples
    }
}

执行main方法后,输出结果为"eat Apples",也就是说,实际调用的是Apple子类重写后的eat方法。

注意:对象的多态性,只适用于方法,不适用于属性。也就是说,即使子类对象修改了字段属性,父类对象调用时也看不见这个修改。

功能扩展

多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码

我们举个例子:

class Driver{
    // conn = new MySQlConnection();
    // conn = new OracleConnection();
	public void doData(Connection conn){
		//按照规范的步骤去操作数据
//		conn.method1();
//		conn.method2();
//		conn.method3();
	}
}

我们定义了一个操作数据库的类,将来它就可以根据具体传入的Connection对象的不同,操作操作不同数据库的数据。

  • 假设传入的是继承Connection类的MySQLConnection类的对象,那么它就可以去操作MySQL数据库中的数据。

我们可以看出,此时这个Connection类仅仅只是定义了一个“规范”,使用这个Connection类时实际使用的都是子类对象重写过的方法。所以多态常常会与抽象类/接口一起使用,因为这个父类本身往往并不需要有具体的方法实现。

  • 事实上,Java本身提供的Connection类确实是一个接口,具体使用时,都要导入各个数据库厂商实现过的数据库API。
三、向下转型 (一)为什么使用向下转型

有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用(强行调用会报错)。如何才能调用子类特的属性和方法?我们需要使用向下转型。

(二)向下转型概念

和向上转型相反,如果把一个父类类型强制转型为子类类型,就是向下转型(downcasting)

我们使用强制类型转换符()来实现向下转型。

(三)使用时的注意点

只有父类变量指向子类对象时,向下转型可以成功。例如:

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
  • 把p1转型为Student会成功,因为p1确实指向Student实例。

  • 把p2转型为Student会失败,因为p2的实际类型是Person,不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。

失败的时候,Java虚拟机会报ClassCastException。

(四)如何避免出错 1. instanceof的概念

为了避免向下转型出错,Java提供了instanceof操作符,用于判断一个实例究竟是不是指定类型这个类型的子类

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false

Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true

Student n = null;
System.out.println(n instanceof Student); // false

为何instanceof对于指定类型的子类的对象也会判定为true呢?这其实也很好理解,因为不管你是一个学生还是一位教师,你都是一个人,要是有人说你不是人,你怕是会跳过去打他吧。所以instanceof也是同样道理。

如果一个引用变量为null,那么对任何instanceof的判断都为false

2. instanceof的使用

利用instanceof,在向下转型前可以先判断:

Person p = new Student();
if (p instanceof Student) {
    // 只有判断成功才会向下转型:
    Student s = (Student) p; // 一定会成功
}

从Java 14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型。例如,对于以下代码:

Object obj = "hello";
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

可以改写如下:

Object obj = "hello";
if (obj instanceof String s) {
    // 可以直接使用变量s:
    System.out.println(s.toUpperCase());
}

这种使用instanceof的写法更加简洁。

(五)图示类比

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/606500.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号