Java——多态

目录

[3.3 多态](#3.3 多态)


3.3 多态

  • 多态,多种形态,指向那个对象就去指向那个对象的方法。
  • 实现多态的条件:必须有继承关系、必须要有重写,父类的引用指向子类的对象。
  • instanceof 判断xx是否为Child2类型的对象,或者Child2子孙后代对象。
  • 强制类型转换
  • 基本数据类型强制类型转换不会报错,但是结果可能与预期不符。

  • 引用类型数组强制类型转换,前提是有继承关系。

java 复制代码
public class Parent {
	public void eat() {
		System.out.println("我是Parent");
	}
}
java 复制代码
public class Child1 extends Parent{
	public void eat() {
		System.out.println("我是Child1");
	}
}
java 复制代码
public class Child2 extends Parent {
	public void eat() {
		System.out.println("我是Child2");
	}
}
java 复制代码
public class Child3 extends Parent{
	public void eat() {
		System.out.println("我是Child3");
	}
}
java 复制代码
public class GrandSon1 extends Child1 {
	public void eat() {
		System.out.println("我是GrandSon1");
	}
}
java 复制代码
public class Test {
	public static void main(String[] args) {
//		多态,多种形态,指向那个对象就去指向那个对象的方法。
//		实现多态的条件:必须有继承关系、必须要有重写,父类的引用指向子类的对象
		/*Parent xx = new Parent();
		xx.eat();
		
		xx = new Child1();
		xx.eat();
		
		xx = new GrandSon1();
		xx.eat();*/
		
		aa(new Parent());
		aa(new Child1());
		aa(new Child2());
		aa(new Child3());
		aa(new GrandSon1());
		
		//强制类型转换
		//基本数据类型强制类型转换不会报错,但是结果可能与预期不符
		//引用类型数组强制类型转换,前提是有继承关系
		Parent xx = new GrandSon1();//GrandSon1是小碗,Parent是大碗,大碗可以装小碗
		Child1 yy1 = (Child1)xx;//小碗不能装大碗,除非强制类型转换
		
		
		//Child2 yy2 = (Child2)xx;//编译没有错,但是运行会报错
		
		
		//instanceof 判断xx是否为Child2类型的对象,或者Child2子孙后代对象
		if(xx instanceof Child2) {
			Child2 yy3 = (Child2)xx;
			System.out.println("可以转换");
		}
		else {
			System.out.println("不可以转换");
		}
		
		int a = 32;
		short b = (short)a;
		
		int[] arr = {100,245,128,110};
		for(int i=0;i<arr.length;i++) {
			char j = (char)arr[i];
			System.out.println(j);
		}
		
	}
	
	public static void aa(Parent xx) {
		xx.eat();
	}
}
  • Java中,创建对象时,在创建子对象之前会创建它的所有祖先对象,从直接父类开始,一直向上到Object类。
  • 这是因为Java 中的继承机制,子类会继承父类的属性和方法,为了正确地初始化对象,需要先初始化父类部分。当创建一个子类对象时,Java 虚拟机会首先调用父类的构造函数(原因见3.1继承概述 代码里关于super()的内容,3.1暂时还没有发,过几天发),如果父类还有父类,会继续向上调用,直到Object类的构造函数被调用,然后再逐步向下执行子类的构造函数,完成对象的创建和初始化。
  • 父类的引用指向子类对象的内存图:

对于Animal a1 = new Cat();(下图的代码)

这个语句,会创建两个对象,先创建Animal对象,然后再创建Cat对象。而a1会默认指向创建的Animal对象。如果在Cat中没有重写run方法,此时执行a1.run(),将会调用创建的Animal里的run方法。简略内存图如下:

当重写run方法后,a1指向不会改变,但是创建的Animal对象里的run()方法会指向创建的Cat对象里的run()方法。如果这时执行a1.run(),将会调用创建的Cat里的run方法。

  • 例子:
java 复制代码
public class Test1 {
	
	public static void main(String[] args) {
		A a1 = new A();
		A a2 = new B();//a2实际上是一个A的对象,只可以调用B中重写的A中的方法和A中没有被重写的方法
		//父类的引用指向子类的对象时(多态),只可以调用子类中重写父类的方法和父类中没有在子类重写的方法
		
		B b = new B();
		C c = new C();
		D d = new D();
		
		System.out.println("1:"+a1.show(b));
		System.out.println("2:"+a1.show(c));
		System.out.println("3:"+a1.show(d));//a1的方法里面两个都能用,但是最后只会用精度最高的那一个
		
		//a2只可以调用A的第一个方法B的第二个方法,子类重写父类的方法后,就不可以在调用父类的该方法,只可以调用重写后的方法
		System.out.println("4:"+a2.show(b));
		System.out.println("5:"+a2.show(c));
		System.out.println("6:"+a2.show(d));
		
		//b可以调用B中的所有方法和A中的第一个方法
		System.out.println("7:"+b.show(b));
		System.out.println("8:"+b.show(c));
		System.out.println("9:"+b.show(d));
	}
}

解答:

每个类的关系如下图,最上传的A是祖先。

1:a1无祖先(准确来说其祖先是Object类,但是这里涉及不到,暂时不考虑),所以在创建对象时,只会创建一个(实际上还创建了一个Object的对象,但是,这里不涉及,暂时不考虑,下面同理)。对于第一个打印语句,Show方法传的实参是一个B类型的对象,但是A类的方法里没有形参是B类型对象的方法,此时会用到多态,父类的引用指向子类的对象,对于A类里的两个Show()方法,形参分别是一个D类型的变量,一个A类型的变量。实参b是是B类型,B类型是A的子类,此时会调用形参类型为A的Show()方法。所以最后输出结果是A and A。

2:原理同1。实参c是C类型的对象。C是A的后代。

3:实参d是D类型的对象。D虽然是A的后代。但是因为A类里面有一个形参为D的Show()方法,会优先调用这个方法。即,如果两个方法都可以被调用,会优先调用精度高的。

4:a2只有一个祖先,所以在创建对象时,只会创建2个,一个A型的,一个B型的。因为B类重写了形参为A类型的Show()方法,当调用Show(A)时,调用的是B类的方法。又因为a2是A类型,它指向了它的子类对象,所以,B类型的Show(Object obj)无法被调用。对于第四个打印语句,Show方法传的实参是一个B类型的对象,但是A类的方法里没有形参是B类型对象的方法,此时会用到多态,父类的引用指向子类的对象,对于A类里的两个Show()方法,形参分别是一个D类型的变量,一个A类型的变量。实参b是是B类型,B类型是A的子类,此时会调用形参类型为A的Show()方法。所以最后输出结果是B and A。

5:原理同4,实参c是C类型的对象。C是A的后代。

6:实参d是D类型的对象。D虽然是A的后代。但是因为A类里面有一个形参为D的Show()方法,会优先调用这个方法。即,如果两个方法都可以被调用,会优先调用精度高的。

7:b只有一个祖先,所以在创建对象时,只会创建2个,一个A型的,一个B型的。因为B类重写了形参为A类型的Show()方法,当调用Show(A)时,调用的是B类的方法。又因为b是B类型,此时他一共可以调用三个方法,如图。对于第七个打印语句,Show方法传的实参是一个B类型的对象,可调用的方法里没有形参是B类型对象的方法,此时会用到多态,父类的引用指向子类的对象,对于可以调用的三个个Show()方法,形参分别是一个D类型的变量,一个A类型的变量,一个是Object型的变量。实参b是是B类型,B类型是A的子类,也是Object的后代,此时会调用形参类型为A的Show()方法,优先调用精度高的。所以最后输出结果是B and A。

8:原理同4,实参c是C类型的对象。C是A的后代。

9:实参d是D类型的对象。D虽然是A的后代。但是因为可以调用的方法里面有一个形参为D的Show()方法,会优先调用这个方法。

相关推荐
怡人蝶梦3 分钟前
Java后端技术栈问题排查实战:Spring Boot启动慢、Redis缓存击穿与Kafka消费堆积
java·jvm·redis·kafka·springboot·prometheus
瓯雅爱分享7 分钟前
MES管理系统:Java+Vue,含源码与文档,实现生产过程实时监控、调度与优化,提升制造企业效能
java·mysql·vue·软件工程·源代码管理
bubiyoushang8889 分钟前
matlab雷达定位仿真
开发语言·matlab
鬼多不菜1 小时前
一篇学习CSS的笔记
java·前端·css
深色風信子1 小时前
Eclipse 插件开发 5.3 编辑器 监听输入
java·eclipse·编辑器·编辑器 监听输入·插件 监听输入
yezipi耶不耶1 小时前
Rust入门之并发编程基础(一)
开发语言·后端·rust
Blossom.1181 小时前
人工智能在智能健康监测中的创新应用与未来趋势
java·人工智能·深度学习·机器学习·语音识别
shangjg31 小时前
Kafka 如何保证不重复消费
java·分布式·后端·kafka
无处不在的海贼2 小时前
小明的Java面试奇遇之互联网保险系统架构与性能优化
java·面试·架构