• java知识3-----核心2-面向对象高级 续1--对象多态性


    对象多态性

    多态性在面向对象中是最重要的,在java中面向对象多态性主要有以下两种主要体现:
    1,方法的重载与覆写;
    2,对象的多态性;
    面向对象三个特性:
    封装,是为了保护类中的属性不被外部直接访问到;
    继承,是为了扩展类的功能;
    多态,方法的重载、覆写;对象的多态;
    对象的多态性,主要分为向上转型和向下转型。
    在这里插入图片描述

    面向对象向上转型、向下转型

    向上转型

    向上转型:子类对象用父类接收;对于向上转型程序会自动完成;

    父类 父类对象 = 子类实例;
    
    • 1

    向下转型:父类对象转为子类对象,对于向下转型必须明确指明要转型的子类类型。

    子类 子类对象 = (子类)父类实例;
    
    • 1
    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class PolDemo01{
    	public static void main(String asrgs[]){
    		B b = new B() ;		// 实例化子类对象
    		b.fun1() ;			// 此方法被子类覆写过
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    fun1()被子类覆写过,所以子类对象调用的时候,肯定是调用了覆写过的方法。
    在这里插入图片描述

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class PolDemo01{
    	public static void main(String asrgs[]){
    		B b = new B() ;		// 实例化子类对象
    		A a = b ;			// 向上转型关系
    		a.fun1() ;			// 此方法被子类覆写过
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    进行向上转型,运行后可以发现,子类对象向上转型成父类对象后,调用的方法依然是子类覆写过的方法。
    在这里插入图片描述

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class PolDemo01{
    	public static void main(String asrgs[]){
    		B b = new B() ;		// 实例化子类对象
    		A a = b ;			// 向上转型关系
    		a.fun1() ;			// 此方法被子类覆写过
    		a.fun2() ;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    调用父类的fun2()方法,fun2()中去调用的fun1()依然是子类覆写过的fun1()。
    在这里插入图片描述
    结论:
    可以发现,通过子类进行父类对象的实例化操作,即对象发生向上转型之后,如果调用的方法被子类覆写过,则调用的肯定都是子类中覆写过的方法,但是当方法没有被覆写过,则调用的就是父类中继承过来的方法,也就是从父类中去找的方法。
    注意点:
    转型后,因为操作的是父类对象,所以是无法找到在子类中定义的新方法的。

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class PolDemo01{
    	public static void main(String asrgs[]){
    		B b = new B() ;		// 实例化子类对象
    		A a = b ;			// 向上转型关系
    		a.fun1() ;			// 此方法被子类覆写过
    		a.fun3() ;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    向上转型以后,找不到子类中定义的新方法。
    在这里插入图片描述

    向下转型

    将父类对象变为子类对象,称为向下转型。向下转型需要采用强制的手段。

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class PolDemo02{
    	public static void main(String asrgs[]){
    		A a = new B() ;			// 向上转型关系
    		B b = (B)a ;		// 发生了向下转型关系
    		b.fun1() ;
    		b.fun2() ;
    		b.fun3() ;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    类B中存在3个方法,所以都可以调用到。
    在这里插入图片描述
    在进行对象向下转型操作之前,有一股注意点:

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class PolDemo03{
    	public static void main(String asrgs[]){
    		A a = new A() ;			// 实例化了一个父类对象
    		B b = (B)a ;		// 发生了向下转型关系
    		b.fun1() ;
    		b.fun2() ;
    		b.fun3() ;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    编译时候没有任何的语法问题,但是在执行的时候报错,一个既空指针异常之后的又一个非常经典的错误,类型转换异常:
    在这里插入图片描述
    此异常是在对象强转的时候会出现,如果两个没有关系的对象,发生转换关系则肯定出现此异常。
    分析:
    仅仅知道A类的情况下,并不知道谁是它的子类;
    但是知道B类的情况下,就可以通过B extends A,知道B是A的子类,这样,就知道了A和B两个类是有关系的;
    发生向下转型之前已经发生了向上转型,然后再进行了向下转型,才不会有任何问题;例如:A a = new B() ; B b = (B)a ;
    当直接进行向下转型,肯定是会报类型转换异常的。比如:A a = new A() ; B b = (B)a ;

    设计一个方法,可以接收A类的任意子类对象。

    方案一:不使用对象多态性,使用重载实现:

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    class C extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("C --> public void fun1(){}") ;
    	}
    	public void fun5(){
    		System.out.println("C --> public void fun5(){}") ;
    	}
    };
    public class PolDemo04{
    	public static void main(String asrgs[]){
    		fun(new B()) ;	// 传递B的实例
    		fun(new C()) ;	// 传递B的实例
    	}
    	public static void fun(B b){
    		b.fun1() ;		// 调用覆写父类中的fun1()方法
    	}
    	public static void fun(C c){
    		c.fun1() ;		// 调用覆写父类中的fun1()方法
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    重载思路方案也是有局限性,没增加一个子类,fun()方法就得重载一次,如果有成百上千的子类的话,就得重载成百上千个方法了。
    为了解决这样的局限性,就得使用对象多态性完成。
    方案二:对象多态性实现:

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    class C extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("C --> public void fun1(){}") ;
    	}
    	public void fun5(){
    		System.out.println("C --> public void fun5(){}") ;
    	}
    };
    public class PolDemo05{
    	public static void main(String asrgs[]){
    		fun(new B()) ;	// 传递B的实例
    		fun(new C()) ;	// 传递B的实例
    	}
    	public static void fun(A a){
    		a.fun1() ;		// 调用覆写父类中的fun1()方法
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    父类对象接收子类实例;这样,不管有多少个子类,都可以轻松完成,不用再重载方法。

    面向对象转型的限制

    向上转型:自动完成。
    向下转型:强制手段进行,发生向下转型之前,必须先发生向上的转型关系。
    对象多态性可以解决方法接收参数的问题。

    instanceof

    instanceof关键字的作用及使用时机

    在java中可以使用instanceof关键字判断,一个对象是哪个类的实例。

    对象 instanceof 类    ---> 返回一个boolean类型
    
    • 1
    class A{					// 定义类A
    	public void fun1(){
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    public class InstanceofDemo01{
    	public static void main(String asrgs[]){
    		A a1 = new B() ;
    		System.out.println("A a1 = new B() " + (a1 instanceof A)) ;
    		System.out.println("A a1 = new B() " + (a1 instanceof B)) ;
    		A a2 = new A() ;
    		System.out.println("A a2 = new A() " + (a2 instanceof A)) ;
    		System.out.println("A a2 = new A() " + (a2 instanceof B)) ;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述
    a1是A的实例,也是B的实例;
    a2是A的实例,但是并不是B的实例,所以a2是无法发生向下转型的,因为a2和类B是没有关系的。

    使用instanceof对对象的转型进行安全验证

    例子:
    A类是父类,B是A的子类,C也是A的子类;判断,如果是B类则调用B类覆写过的fun1()方法后调用B类中新定义的fun3()方法;如果是C类则掉员工C类覆写过的fun1()方法然后调用C类中新定义的fun5()方法。
    (对象发生向上转型以后,就相当于是父类对象了,对象只能调用子类覆写过的方法,对于子类中新定义的方法是无法访问到的)

    class A{					// 定义类A
    	public void fun1(){		// 定义fun1()方法
    		System.out.println("A --> public void fun1(){}") ;
    	}
    	public void fun2(){
    		this.fun1() ;		// 调用fun1()方法
    	}
    };
    class B extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("B --> public void fun1(){}") ;
    	}
    	public void fun3(){
    		System.out.println("B --> public void fun3(){}") ;
    	}
    };
    class C extends A{
    	public void fun1(){		// 此方法被子类覆写了
    		System.out.println("C --> public void fun1(){}") ;
    	}
    	public void fun5(){
    		System.out.println("C --> public void fun5(){}") ;
    	}
    };
    public class InstanceofDemo02{
    	public static void main(String asrgs[]){
    		fun(new B()) ;
    		fun(new C()) ;
    	}
    	public static void fun(A a){
    		a.fun1() ;
    		if(a instanceof B){
    			B b = (B) a ;
    			b.fun3() ;
    		}
    		if(a instanceof C){
    			C c = (C) a ;
    			c.fun5() ;
    		}
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在开发中对于向下转型,最好增加验证,保证转型时不发生ClassCastException。
    如果现在增加A的这类,则就需要修改fun()方法,这样一来,程序就失去了灵活性和可维护性。问题出在哪里?父类的设计不合理,需要把父类重新设计,否则开发中会非常难以维护。

    开发设计原则:
    一个类永远不要去继承一个已经实现好的类,而是去继承抽象类或者实现接口。
    父类一般都设计成抽象类或者接口。

  • 相关阅读:
    高校教室预约使用管理系统(PHP+Mysql)毕业论文+项目源码+数据库sql文件
    SpringBoot+LayUI+MybatisPlus 前后端分离 实现数据表格下拉框功能
    Java基于SpringBoot+Vue的考研资讯平台
    浅谈电商会员管理的价值|数商云B2B系统助力家用电器行业提高交易转化
    C#/.NET/.NET Core推荐学习书籍
    HellaSwag数据集分享
    智能写作工具,一键改写文章不费力
    大数据开发之数据仓库
    Windows使用ssh协议远程连接ubuntu linux子系统
    渲染时间过长?这些参数设置学起来
  • 原文地址:https://blog.csdn.net/jakezhang1990/article/details/126011285