1、多态基础语法
关于多态中涉及到的几个概念:
*向上转型(upcasting)
子类型—>父类型 (又被称为:自动类型转换)
*向下转型(downcasting)
父类型—>子类型(又被称为:强制类型转换)【需要加强制类型转换符】
*无论是向上转型还是向下转型,两种类型之间必须要有继承关系。没有继承关系,程序是无法编译通过的。
//动物类
public class Animal {
public void move(){
System.out.println("动物在移动");
}
}
//猫类
public class Cat extends Animal {
//重写父类中继承过来的方法
public void move(){
System.out.println("猫在走猫步");
}
//不是从父类中继承过来的方法,是子类对象特有的行为
public void catchMouse(){
System.out.println("猫捉老鼠!");
}
}
public class Bird extends Animal {
//重写父类中继承过来的方法
public void move(){
System.out.println("鸟儿在飞翔");
}
public void fly(){
System.out.println("Bird fly!");
}
}
/**
- 1、Animal、Cat、Bird三个类之间的关系
- Cat继承Animal
- Bird继承Animal
- Cat和Bird之间没有任何继承关系
- 2、关于多态中涉及到的几个概念
*向上转型(upcasting)
子类型—>父类型 (又被称为:自动类型转换)
*向下转型(downcasting)
父类型—>子类型(又被称为:强制类型转换)【需要加强制类型转换符】
*无论是向上转型还是向下转型,两种类型之间必须要有继承关系。没有继承关系,程序是无法编译通过的。
*/
public class Test {
public static void main(String[] args) {
Animal a1 = new Animal();
a1.move();
Cat c1 = new Cat();
c1.move();
c1.catchMouse();
Bird b1 = new Bird();
b1.move();
//使用多态语法机制
/**
* 1、Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
* 2、Cat is Animal
* 3、new Cat()创建的对象的类型是Cat,a2这个引用的数据类型是Animal,可见它们进行了类型转换
* 子类型转换成父类型,称为向上转型/upcasting,或者称为自动类型转换。
* 4、java中允许这种语法:父类型引用指向子类型对象。
*/
Animal a2 = new Cat();
/**
* 1、java程序永远都分为编译阶段和运行阶段。
* 2、先分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的。
* 3、编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有move()方法,
* 所以编译通过了。这个过程称为静态绑定,编译阶段绑定。只有静态绑定成功之后才有后续的运行。
* 4、在程序运行阶段,JVM堆内存当中真实创建的对象是Cat对象,那么以下程序在运行阶段一定会调用Cat对象的move()方法,
* 此时发生了程序的动态绑定,运行阶段绑定。
* 5、无论是Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,因为底层真实对象就是Cat对象。
* 6、父类型引用指向子类型对象这种机制导致程序存在编译阶段绑定和运行阶段绑定两种不同的形态/状态。
* 这种机制可以成为一种多态语法机制。
*/
a2.move();//猫在走猫步
/**
* 以下程序不能调用:
* 因为编译阶段编译器检查到a2的类型是Animal类型,从Animal.class字节码文件中查找catchMouse()方法,
* 最终没有找到该方法,导致静态绑定失败了,没有绑定成功,也就是说编译失败了,更别谈运行了。
*/
//a2.catchMouse();
/**
* 需求:假设想让以上的对象执行catchMouse()方法。
* a2是无法直接调用的,因为a2的类型是Animal[Animal中没有catchMouse()方法。]
* 可以将a2强制类型转换为Cat类型。
* a2的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型/downcasting/强制类型转换
* 注:向下转型也需要两种类型之间必须有继承关系,不然编译报错。强制类型转换需要加强制类型转换符。
*
* 什么时候需要使用向下转型?
* 当调用的方法是子类型特有的,在父类型中不存在,必须进行向下转型。
*/
Cat c2 = (Cat)a2;
c2.catchMouse();//猫捉老鼠!
Animal a3 = new Bird();
/**
* 1、以下程序编译通过。
* 2、程序在运行阶段会出现异常,因为JVM堆内存当中真实存在的对象是Bird类型,Bird对象无法转换成Cat对象,因为
* 两种类型之间不存在任何继承关系,此时出现了异常:
* java.lang.ClassCastException
* 类转换异常:这种异常总是在"向下转型"的时候会发生。
*/
//Cat c3 = (Cat)a3;
/**
* 1、以上异常只有在强制类型转换的时候会发生,也就是说"向下转型"存在隐患(编译过了,但是运行错了!)
* 2、向上转型只要编译通过,运行一定不会出问题。Animal a = new Cat();
* 3、向下转型编译通过,运行可能错误:Animal a3 = new Bird(); Cat c3 = (Cat)a3;
* 4、怎么避免向下转型出现的ClassCastException呢?
* 使用instanceof运算符可以避免出现以上的异常。
*
instanceof运算符怎么用?
* 1、语法格式
* (引用 instanceof 数据类型名)
* 2、以上运算符的执行结果类型是布尔类型,结果可能是true/false
* 3、关于运算结果true/false:
* 假设:(a instanceof Animal) //instance翻译为实例
* true表示:a这个引用指向的对象是一个Animal类型。
* false表示:a这个引用指向的对象不是一个Animal类型。
*java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,避免ClassCastException异常的发生。
*/
if(a3 instanceof Cat){ //a3是Cat类型的对象
Cat c3 = (Cat)a3;
//调用子类对象中特有的方法
c3.catchMouse();
}else if(a3 instanceof Bird){ //a3是Bird类型的对象
Bird b2 = (Bird)a3;
b2.fly();
}
}
}
2、多态在实际开发中的作用
降低程序的耦合度,提高程序的扩展力。
能使用多态,尽量使用多态
父类型引用指向子类型对象
面向抽象编程,不要面向具体编程。面向抽象编程的好处是:耦合度低,扩展力强
public class Master { //主人类
public void feed(Pet pet){
pet.eat();
}
}
public class Pet { //宠物类
public void eat(){
}
}
public class CatPet extends Pet { //猫类
public void eat(){
System.out.println("小猫正在吃鱼!");
}
}
public class DogPet extends Pet { //狗类
public void eat(){
System.out.println("小狗正在啃骨头!");
}
}
public class Test2 {
public static void main(String[] args) {
Master zhangsan = new Master();
zhangsan.feed(new CatPet());
Pet erha = new DogPet();
zhangsan.feed(erha);
}
}
3、多态和方法覆盖的比较
1、方法覆盖需要和多态机制联合起来使用才有意义
Animal a = new Cat();
a.move()
要的是什么效果:
编译的时候move()方法是Animal的,运行的时候自动调用到子类重写move()上。
没有多态机制,只有方法覆盖机制,并无意义。没有多态机制的话,方法覆盖可有可无。
没有多态机制的情况下,如果父类的方法无法满足子类业务的需求,子类完全可以定义一个全新的方法。
方法覆盖和多态不能分开
2、静态方法不存在方法覆盖
多态自然就和对象有关系
而静态方法的执行不需要对象,所以一般情况下会说静态方法"不存在"方法覆盖。
方法覆盖只是针对"实例方法","静态方法覆盖"没有意义
public class first{
public static void main(String[] args){
//静态方法虽然可以使用"引用."来调用,但是和对象无关
Animal a = new Cat();
//以下语句在实际运行的时候还是:Animal.doSome()
a.doSome();//Animal的doSome方法执行!
}
}
class Animal{
//父类的静态方法
public static void doSome(){
System.out.println("Animal的doSome方法执行!");
}
}
class Cat extends Animal{
//尝试在子类当中对父类的静态方法进行重写
public static void doSome(){
System.out.println("Cat的doSome方法执行!");
}
}
3、私有方法不能覆盖
public class Second {
//私有方法
private void doSome(){
System.out.println("Second's private method doSome execute!");
}
//入口
public static void main(String[] args){
//多态
Second sc=new T();
sc.doSome();//Second's private method doSome execute!
}
}
//子类
class T extends Second{
//尝试重写父类中的doSome()方法
//访问权限不能更低,可以更高。
public void doSome(){
System.out.println("T's public method doSome execute!");
}
}
由于私有方法不能被覆盖,所以输出结果仍然调用的是父类的doSome方法。