9.多态的语法基础

本文详细介绍了多态的向上转型和向下转型概念,以及它们在编译阶段和运行阶段的不同表现。重点讨论了向下转型的风险及如何通过`instanceof`判断来避免ClassCastException,并给出了实例和应用场景。
摘要由CSDN通过智能技术生成

多态的语法基础:

1.什么是向上转型和向下转型!

向上转型和向下转型只针对具有继承关系的类而言,没有继承关系的两个类进行转型编译器会报错

1.向上转型(自动类型转换)

即:父类的引用指向子类的对象

---->

2.向下转型(强制类型转换)

需要强制类型转换符才能实现转换。

2.什么是多态?

顾名思义:多种形态,多种状态!

多态的基本语法是父类的引用指向子类的对象!!!

Animal a = new Dog();

多态分为编译阶段运行阶段

对于上述代码:

编译阶段:编译器对于引用变量a会自动绑定父类的方法

运行阶段:代码运行阶段会动态绑定子类型对象的方法。

比如:

public class Test01{
	public static void main(String[] args){
		Animal a = new Dog();		
		a.move();
		//这里编译器只知道a是Animal类的,编译器会去Animal.class字节码文件
		//中寻找move()方法,找到了就绑定上move()方法,编译通过。
		//(编译阶段为静态绑定)
		//运行阶段,实际上在堆内存中创建的是Dog类型的对象,所以在move的时候,
		//执行的是对象的move()方法,这个过程为运行阶段绑定。
		//(运行阶段绑定为动态绑定)
		//多态就是多种形态:
		//编译阶段是静态,
		//运行阶段是动态
	}
}
//以上代码能编译并运行

多态就是多种形态:

编译阶段静态绑定引用类型(父类);

运行阶段动态绑定实际对象的类型(子类);

3.分析子类独有的方法在多态中怎么使用?

如何分析一个代码能否执行?

一定要从编译阶段和运行阶段去分析。

编译阶段时静态绑定不通过,没有编译,根本就轮不到运行。

分析以下代码:

1.子类

public class Dog extends Animal{
	public void move(){
		System.out.println("dog在汪汪叫");
	}
	public void run(){
		System.out.println("dog,run!!");
	}
}

2.父类

public class Animal{
	public void move{
		System.out.println("动物在移动");
	}
}

3.测试类

public class Test02{
	public static void main(String[] args){
		Animal a = new Dog();
		//下面能执行吗?
		a.run();
	}
}

编译阶段:编译器认为a是Animal类的引用变量,会去 Animal. class 字节码文件中寻找run()方法,找不到,静态绑定失败编译报错!

那么,为了使多态机制下,父类调用子类独有的方法,可以使用向下转型:

public class Test02{
	public static void main(String[] args){
		Animal a = new Dog();
		//下面能执行吗?
		//不能
		//错误: 找不到符号
		//编译器在Animal类中找不到run()方法
		//a.run();
		((Dog)a).run();
		//或者
		Dog d = (Dog)a;
		d.run();
	}
}

4.向下转型有没有什么风险?

举个例子:

1.父类:

public class Animal{
	public void move(){
		System.out.println("动物在移动");
	}
}

2.子类 Dog类:

public class Dog extends Animal{
	public void move(){
		System.out.println("dog在汪汪叫");
	}
	public void run(){
		System.out.println("dog,run!!");
	}
}

3.子类 Cat类:

public class Cat extends Aniaml{
	public void move(){
		System.out.println("Cat喵喵喵!");
	}
}

4.测试类:

public class Test03{
	public static void main(String[] args){
		Animal a = new Cat();
		//下面的代码会不会报错?
		//编译阶段:编译器只知道a是Animal类型的,而Animal类
		//和类Dog有继承关系,所以下面的向下转型编译通过。
		//运行阶段:实际上在堆内存中开辟的对象是Cat,
		//在运行时转换成Dog类型的
		//对象,因为Cat和Dog没有继承关系,运行阶段会抛出异常:
		//java.lang.ClassCastException: Cat cannot be cast to Dog
		Dog d = (Dog)a;
		d.run();
	}
}

以上代码编译通过,但是在运行阶段会抛出类型转换异常

//java.lang.ClassCastException: Cat cannot be cast to Dog

**这说明向下转型存在风险。怎么避免此类风险的发生?**即怎么避免 ClassCastException 的发生?

使用 instanceof 关键字:(运行阶段动态判断

1.instanceof可以在运行阶段动态判断引用指向的对象的类型

语法:引用变量 instanceof 类名

Animal a = new Cat();
if(a instanceof Cat){
    //...
}

该运算符是一个关系表达式,用于判断引用变量指向的堆内存中的JAVA对象是否属于后面的类,是则返回true,不是则返回false;

public class Test04{
	public static void main(String[] args){
		Animal a = new Animal();
		Animal b = new Dog();
		Dog d = new Dog();
		System.out.println(a instanceof Animal);//true
		System.out.println(b instanceof Animal);//true
		System.out.println(b instanceof Dog);//true
		System.out.println(b instanceof Cat);//false
		System.out.println(b instanceof Object);//true
		System.out.println(d instanceof Object);//true
		System.out.println(d instanceof Dog);//true
		
	}
}

5.为什么要instanceof判断?

考虑到向下转型存在一定的风险:应该多使用instanceof运算符:

  1. 不是所有代码都能看出传递的对象在底层上属于什么具体对象。比如:父类的引用指向子类,你只知道父类的引用变量,但是具体指向哪个子类呢?不是所有代码都能一眼看出来的,例如:
public class AnimalTest{
	public void f(Animal a){
		//这是一个实例方法
		/*
		对于这个实例方法,别人再调用时传过来一个Animal类型的引用,
		实际上该引用可以指向Animal类任意子类的对象,可以是
		Dog,也可以是Cat
		//此时并不像之前那样可以通过肉眼观察出传递的引用在底层指向了
		什么具体对象,所以需要instanceof运算符进行判断,
		避免出现类型转换错误
		*/
		if(a instanceof Dog){
			Dog d = (Dog)a;
			d.run();
		}else if(a instanceof Cat){
			Cat c = (Cat)a;
			c.sleep();
		}
	}
}
  1. 测试类:
public class Test05{
	public static void main(String[] args){
		AnimalTest t = new AnimalTest();
		t.f(new Dog());
		t.f(new Cat());
		
	}
}
/*
dog,run!
cat在睡觉!
*/

向下转型一定要使用instanceof 运算符进行判断!!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值