文章目录
- 1.概念
- 2.特点
- 3.入门案例练习
- 4 多态的好处
- 5 多态的使用
- 6 练习:多态成员使用测试
- 7 拓展
- 7.1 综合案例
- 7.2 多态为了统一调用标准
- 7.3 静态变量和实例变量的区别
- 7.4 向上转型和向下转型
1.概念
多态是面向对象程序设计(OOP)的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
比如,你的女盆友让你买点水果回来,不管买回来的是苹果还是西瓜,只要是水果就行,这个就是生活中多态的体现。
再比如,小猫、小狗、小猪我们可以把他们都归纳成小动物,每种小动物都需要吃东西,所以我们可以统一设置他们都必须吃,但是每种小动物的习性不一样,那这个就可以设置成小动物自己特有的功能,多态对象只能调用父类中定义子类中重写的功能,并不能调用子类的特有功能,这样就实现了代码的统一。
2.特点
- 多态的前提1:继承
- 多态的前提2:要有方法的重写
- 父类引用指向子类对象,如:Animal a = new Cat();
- 多态中,编译看左边,运行看右边
3.入门案例练习
package partTwo;
/* 本类用于多态的入门案例*/
public class TestDemo {
public static void main(String[] args) {
//6.创建"纯纯”的对象进行测试(为了好理解,就是直接创建对象)
Animal2 a = new Animal2();
Cat2 c = new Cat2();
Dog d = new Dog();
//调用方法进行测试
a.eat(); //小动物Animal吃啥都行
c.eat(); //小猫爱吃小鱼干,执行子类重写后的功能
d.eat(); //小狗爱吃肉骨头,执行子类重写后的功能
/*2.子类可以调用子类的功能,但是父类不能调用子类的特有功能*/
//a.jump();
c.jump(); //子类对象可以调用自己的功能
d.run(); //子类对象可以调用自己的功能
//8.创建多态进行测试
/* 3.口诀1:父类引用指向子类对象
* 解释:创建出来的子类对象地址值,交给父类类型的引用类型变量来保存*/
Animal2 a2 = new Cat2();
Animal a3 = new Dog();
a2.eat();
a3.eat();
/*4.口诀2:编译看左边,运行看右边
* 解释:必须要在父类里定义这个方法,才能通过编译
* 编译时,把多态对象看做父类,必须要在子类中重写父类定义的方法
* 运行时,方法体使用的是子类的功能*/
//a2.jump() 报错,无法使用,不是父类功能的重写,父类中未定义此功能
//a3.run(); 报错,无法使用,不是父类功能的重写,父类中未定义此功能
}
}
/* 1.多态的前提:继承+重写*/
//1.创建父类
class Animal2{
//3.创建父类的普通方法
public void eat(){
System.out.println("小动物Animal吃啥都行");
}
}
//2.1创建子类小猫
class Cat2 extends Animal2{
//4.1重写父类的功能--小鱼干
@Override
public void eat(){
System.out.println("小猫爱吃小鱼干");
}
//5.1 添加子类的特有功能
public void jump(){
System.out.println("小猫Cat跳的老高了");
}
}
//2.2创建子类小狗
class Dog extends Animal{
//4.2重写父类的功能--肉骨头
@Override
public void eat(){
System.out.println("小狗爱吃肉骨头");
}
//5.2添加子类的特有功能
public void run(){
System.out.println("小狗Dog跑的老快了");
}
}
4 多态的好处
- 多态可以让我们不用关心某个对象到底具体是什么类型,就可以使用该对象的某些方法
- 提高了程序的可扩展性和可维护性
5 多态的使用
前提:多态对象把自己看做是父类类型
- 成员变量: 使用的是父类的
- 成员方法: 由于存在重写现象,所以使用的是子类的
- 静态成员: 随着类的加载而加载,谁调用就返回谁的
6 练习:多态成员使用测试
package partTwo;
/* 本类用于测试多态成员的使用情况*/
public class TestDemo2 {
public static void main(String[] args) {
// 创建纯纯的子类对象
Dog3 d = new Dog3();
System.out.println(d.sum); //20 子类自己的属性
d.eat(); // 子类自己的方法
d.play(); //小狗玩皮球
//8.创建多态对象
/* 口诀1 :父类引用指向子类对象
* 口诀2 :编译(保存)看左边,运行(测试)看右边*/
Animal3 a = new Dog3();
/* 多态中,成员变量使用的是父类的*/
System.out.println(a.sum); //10
/* 多态中,方法的声明的使用的是父类的,方法的实现使用的是子类的*/
a.eat(); //小狗要吃肉包子
/* 多态中,如果父子类都有同名的静态方法,使用的是父类的*/
a.play(); //玩啥都行,静态方法优先级最大,不能被重写
}
}
//1.创建父类
class Animal3{
//3.创建成员变量
int sum = 10;
//4.创建普通方法
public void eat(){
System.out.println("吃啥都行");
}
//创建父类的静态方法
public static void play(){
System.out.println("玩啥都行");
}
}
/* 1.多态的前提:继承+重写*/
//2.创建子类
class Dog3 extends Animal3{
//5.定义子类的成员变量
int sum = 20;
//6. 重写父类的方法
@Override /* 2.此注解表示当前是一个重写的方法*/
public void eat(){
System.out.println("小狗要吃肉包子");
}
//创建子类的静态方法
/* @Override 这不是一个重写的方法,质数恰巧在俩个类中,出现了俩个一模一样的方法
静态方法属于类资源,不存在重写现象所以,在哪个类里定义,就在哪个类里使用*/
public static void play(){
System.out.println("小狗玩皮球");
}
}
7 拓展
7.1 综合案例
package partTwo;
/* 本类用于汽车设计综合案例*/
public class TestCar2 {
public static void main(String[] args) {
//9.创建一个纯纯的对象
Car2 c = new Car2();
System.out.println(c.getColor()); //null 被封装了需要使用get方法
c.start(); //我的车车启动了 使用的父类自己的功能
c.stop(); //我的车车熄火了 使用的父类自己的功能
//c.swim(); 会报错,父类不能使用子类的特有功能
BMW b = new BMW();
System.out.println(b.color); //子类自己的值
System.out.println(b.getColor()); //父类设置的值
b.start(); //重写后的功能
b.stop(); //没有重写,输出的为继承父类的功能
//11.创建多态对象进行测试
Car2 c2 = new TSL();
// System.out.println(c2.color); 父类被封装的color属性,只能在本类使用
System.out.println(c2.getColor()); //可以调用公共get方法使用
c2.stop(); //调用的是重写后的属性
c2.start(); //没有重写,调用的是父类的声明
//c2.swim(); 会报错,多态对象不能使用子类的特有功能,因为它认为自己是父类型
}
}
//1.通过分析,抽象形成一个汽车类
class Car2 {
//2.定义属性--成员变量
//2.1 封装属性--使用private
private String brand; //品牌
private String color; //颜色
private int id; //编号
private double length; //车长
//3.定义方法--功能
public void start(){
System.out.println("我的车车启动了");
}
public void stop(){
System.out.println("唉呀妈呀,熄火了");
}
//2.2 生成属性对应get,set值
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
}
//4.创建一个子类--宝马类
class BMW extends Car2{
//8.1 定义自己的特有属性
String color = "五彩斑斓的黑";
//6.重写父类的方法
@Override
public void start(){
// System.out.println(getColor()); 被封装的,使用的话,要调用getcolor
System.out.println("我的车要起飞了");
}
}
//5创建一个子类--特斯拉
class TSL extends Car2{
//8.2 定义自己的特有属性
String color = "炫彩夺目的红";
@Override
public void stop(){
System.out.println("唉呀妈呀,咋停不下来呢");
}
//7.定义子类的特有功能
public void swim(){
System.out.println("没想到吗,我还是一个潜水艇");
}
}
7.2 多态为了统一调用标准
package partTwo;
public class TestFruit {
public static void main(String[] args) {
Fruit2 f = new Fruit2();
Apple2 a = new Apple2();
Orange o = new Orange();
get(f);
get(a);
get(o);
}
//只需要创建一个方法,就可以执行截然不同的效果
//忽略子类对象的差异统一看作父类类型
public static void get(Fruit2 f){
f.clean();
}
}
class Fruit2{
public void clean(){
System.out.println("水果要洗洗再吃");
}
}
class Apple2 extends Fruit2{
@Override
public void clean(){
System.out.println("苹果需要削皮");
}
}
class Orange extends Fruit2{
@Override
public void clean(){
System.out.println("橙子需要剥皮");
}
}
7.3 静态变量和实例变量的区别
在语法定义上的区别:
- 静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:
- 实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
7.4 向上转型和向下转型
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
那么在这个过程中就存在着多态的应用。存在着两种转型方式,分别是:向上转型和向下转型。
向上转型:可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
比如:父类Parent,子类Child
父类的引用指向子类对象:Parent p=new Child();
说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类中声明过的方法,方法体执行的就是子类重过后的功能。但是此时对象是把自己看做是父类类型的,所以其他资源使用的还是父类型的。
比如:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
向下转型(较少):子类的引用的指向子类对象,过程中必须要采取到强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象
Parent p = new Child();//向上转型,此时,p是Parent类型
Child c = (Child)p;//此时,把Parent类型的p转成小类型Child
其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的
说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
比如:花木兰打仗结束,就不需要再看做是她爸了,就可以”对镜贴花黄”了