一、引言
此篇文章来自一个初学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
标签:子类,void,多态,面向对象,抽象类,方法,public,三大
From: https://blog.51cto.com/u_15942107/6017185