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()方法,会优先调用这个方法。

相关推荐
佩奇的技术笔记38 分钟前
Java学习手册:单体架构到微服务演进
java·微服务·架构
zxctsclrjjjcph1 小时前
【高并发内存池】从零到一的项目之centralcache整体结构设计及核心实现
开发语言·数据结构·c++·链表
zm1 小时前
服务器多客户端连接核心要点(1)
java·开发语言
炯哈哈1 小时前
【上位机——MFC】单文档和多文档视图架构
开发语言·c++·mfc·上位机
FuckPatience1 小时前
关于C#项目中 服务层使用接口的问题
java·开发语言·c#
天上掉下来个程小白1 小时前
缓存套餐-01.Spring Cache介绍和常用注解
java·redis·spring·缓存·spring cache·苍穹外卖
揣晓丹2 小时前
JAVA实战开源项目:健身房管理系统 (Vue+SpringBoot) 附源码
java·vue.js·spring boot·后端·开源
编程轨迹_2 小时前
使用 Spring 和 Redis 创建处理敏感数据的服务
java·开发语言·restful
奔驰的小野码2 小时前
SpringAI实现AI应用-自定义顾问(Advisor)
java·人工智能·spring boot·spring
赵和范2 小时前
C++:书架
开发语言·c++·算法