面向对象三大特性之——多态

面向对象三大特性之——多态

一、引言

此篇文章来自一个初学Java不久的学生,内容的用词、深度、广度甚至部分理解不够到位,再加上Markdown语法的不熟练,所以排版不够美观。但还是希望有疑问的读者能够读完全文,大家遇到问题可以一起交流。谢谢!

二、初步理解多态

多态,顾名思义一种东西的多种形态。那面向对象里面为什么或者说哪里来的多态?要想顺理的理解多态,就需要你对封装和继承又很好的理解。内容可以参考我之前写的
面向对象三大特性之——封装

面向对象三大特性之——继承

当然,也希望你读完这篇文章之后,能够完成下面几个问题。
1.什么是多态,为什么使用多态?
2.理解为什么会有抽象类和接口,二者的区别是什么?
3.理解使用多态类和几口实现多态的不同。

三、多态

一、基本概念

1.什么是多态?

答:同一个对象,在不同时刻表现出来的不同形态,在继承的子父类里面体现。

2.多态的前提?

答:

​ 1)要有继承或实现关系

​ 2)要有方法的重写

​ 3)要有父类引用指向子类对象

二、成员的访问特点

成员变量:编译看父类,运行看父类(编译看左边,运行也看左边)

在这里插入图片描述

成员方法:编译看父类,运行看子类(编译看左边,运行看右边)

People类(父类):

public class People {   
    int age = 100;   
    public void show() {    
        int age = 200;    
        System.out.println("年龄是:"+age);    
    }
}

Student类(子类):

public class Student extends People {   
    int age = 10;    
    @Override   
    public void show() {    
        int age = 20;      
        System.out.println("年龄是:"+age);  
    }   
    public void study() {    
        System.out.println("要好好学习");   
    }
}

测试类:

public class Demo01 {
    public static void main(String[] args) { 
        People p = new Student();      
        p.show();  
    }
}

运行结果:

年龄是:20

总结:成员方法在编译的时候查看父类里面是否含有show()方法,如果有,在运行的时候在子类里面查找show()方法,然后执行子类方法的代码。由于子类继承了父类,所以只要父类能够查找到指定的方法,子类的方法里面就一定含有重写父类的方法。所以运行代码,产出结果。

三、多态的优点和缺点

优点:提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

缺点:不能使用子类的特有成员

四、多态的自动转型

为什么会出现自动转型?

举个例子,我想要打印子类的成员变量age。但是根据多态成员的访问特点来看。成员变量编译看左边,执行也看左边,根本没有出现右边(子类)的成员变量。于是多态的转型就出来了。

在这里插入图片描述

1.向上转型:也成为自动转型,即正常的运行方式

2.向下转型:称为强制转型。

在这里插入图片描述
主要就是这段代码的理解:

int age = ((Student) p).age;
System.out.println(age);

p被声明为People类型,此时需要向下转型为People的子类Student类型。如果你能想到整型数据的强制转化,这里运用了相同的语法格式。(Student)p。那么此时的p则是Student类型的,然后再调用它里面的age对象,再赋值给main方法里面定义的变量age,打印输出。

四、抽象类

一、基本理解

当我们在做子类相同功能进行抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了。在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。口水话理解就是:抽象类就相当于一个没有劳动能力的父亲,但是心中却又有很多抱负(成员变量和常量),于是只能将自己的想法定义为抽象类,自己没有办法实现,之能把这些想法告诉自己的子类,让它们帮助自己去实现这些抱负(常量和方法)

二、抽象类的基本特点

1.抽象类和抽象方法必须使用 abstract 关键字修饰

//抽象类的定义
public abstract class 类名 {}

//抽象方法的定义
public abstract void eat();

2.抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

3.抽象类不能实例化,要想实现抽象类的方法,需要借助其子类实现,也就是多态的概念。

4.抽象类的子类可以是一下两种情况

​ 1)要么重写抽象类中的所有抽象方法

​ 2)要么是抽象类

三、抽象类的成员特点

成员变量:既可以是变量,也可以是常量

构造方法:空参构造,有参构造

成员方法:抽象方法,普通方法

五、接口

一、接口理解

接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。Java中的接口更多的体现在对行为的抽象。这个在我们现实中的理解就好比,在离开继承的is…a关系之后,自然界还有一种关系就好比,你和老鹰会飞,飞机也会飞,你做梦的时候也会飞,但是你们三者很难找到一个继承的关系,所以接口就来了,它就类似于一个功能的开关。当给你连接这个接口的时候,你就可以获得这个功能。

二、接口的特点

接口用关键字interface修饰

public interface 接口名 {} 

类实现接口用implements表示

public class 类名 implements 接口名 {}

接口不能实例化:这一点和抽象类的用法相同。要想实现接口的方法,需要借助其子类实现,也就是多态的概念。

接口的子类:要么重写接口中的所有抽象方法,要么子类也是抽象类

三、接口的成员特点

成员变量:只能是常量
​ 默认修饰符:public static final

构造方法:没有,因为接口主要是扩展功能的,而没有具体存在

成员方法:只能是抽象方法

默认修饰符:public abstract

六、例子

一、抽象类与多态的结合

需求:定义一个抽象的Father类,定义一个Son类用来充当Father的子类,并且实现Father的抽象方法。在测试类里面,利用多态的方式,调用Son里面指定的方法

定义一个Father类:

public abstract class Father {   
    public abstract void hobby();
}

定义一个Son类:

public class Son extends Father {
    @Override   
    public void hobby() {    
        System.out.println("我的爱好是打篮球");  
    }
}

定义一个测试类:

public class Demo03 {
    public static void main(String[] args) { 
        Father f = new Son();  
        f.hobby(); 
    }
}

代码执行结果:

我的爱好是打篮球

二、接口类与多态的结合

需求: 定义一个Fly的接口,里面包含两个不同定义方式的eat和fly方法。再定义三个方法People、Plane和Eagles用于实现接口。定义一个测试类,用前面提到的不同的方式去调用不同的方法和常量。

定义一个Fly的接口类:

public interface Fly { 
    //省略了默认的修饰符的方式定义方法
    void fly();   
    //加上默认的修饰符的方式定义方法,除方法名不一样外,两者的效果一样
    public abstract void eat();
}

定义三个Fly接口的实现类,People、Plane和Eagles:

People类:

public class People02 implements Fly{  
    int num =1;  
    //在子类中对接口中的方法进行重写
    @Override   
    public void eat() {  
        System.out.println("我是人,我要吃饭");  
    } 
    //在子类中对接口中的方法进行重写
    @Override   
    public void fly() {    
        System.out.println("我是人,我做梦会飞"); 
    }
}

Plane类:

public class Plane implements Fly {  
    int num = 2;  
    @Override 
    public void eat() {   
        System.out.println("我是飞机,我不需要吃饭");   
    }   
    @Override  
    public void fly() {   
        System.out.println("我是飞机,我可以飞的很高");  
    }
}

Eagles类:

public class Eagles implements Fly {   
    int num =3;  
    @Override  
    public void eat() {   
        System.out.println("我是老鹰,我要吃蛇");   
    }   
    @Override    
    public void fly() {     
        System.out.println("我是老鹰,我会飞翔");   
    }
}

定义一个测试类:

public class Demo02 {   
    public static void main(String[] args) {    
        //利用多态的方式创建一个对象
        Fly f = new People02();   
        f.eat();  
        f.fly();      
        //向下转型的运用
        System.out.println(((People02) f).num);   
        
        //正常的实例化Plane
        Plane p = new Plane();      
        p.eat();      
        p.fly();    
        System.out.println(());    
        
        Fly e = new Eagles();  
        //使用的是向下转型的方式调用这个方法,但是成员方法的访问特点是:编译看左边,执行看右边,所以使用下面的方式调用也可以
        ((Eagles)e).eat();     
        e.fly();       
        //利用向下转型(强制转型)的方式,打印输出其实现子类的成员变量
        System.out.println(((Eagles) e).num);  
    }
}

代码执行结果:

我是人,我要吃饭
我是人,我做梦会飞
1
我是飞机,我不需要吃饭
我是飞机,我可以飞的很高
2
我是老鹰,我要吃蛇
我是老鹰,我会飞翔
3
发布了8 篇原创文章 · 获赞 5 · 访问量 610

猜你喜欢

转载自blog.csdn.net/qq_44377709/article/details/101942097