同一个引用 调用了 同一个方法,但是因为引用的对象不一样,所表现的行为不一样,我们把这种思想称为:多态
目录
1.向上转型
1.1.向上转型定义
1.2.向上转型的优缺点
1.3.向上转型的内存指向
1.4.向上转型的三种方式
1.5.向下转型
2.重写
2.1.概念
2.2.重写的规则
2.3.重写与重载
2.4.动态与静态绑定
2.5.避免在构造方法中调用重写的方法
3.使用多态
3.1.简单的多态实现
3.2.实现自定义多态
学会多态的前提:
(1)继承(2)向上转型(3)重写
继承在之前的课程已经介绍过,下面介绍向上转型和重写,这两种也是发生在继承的基础上。
1.向上转型
1.1.向上转型定义
概念:父类引用 引用 子类对象
语法格式:
父类类型 引用名 = new 子类类型();
例如:Parent是父类类型,Son是子类类型,此时就发生了向上转型
Parent p = new Son();
1.2.向上转型的优缺点
优点:
(1)可以实现代码的灵活使用 (2)实现多态的一种条件
缺点:
(1)虽然引用了子类对象,但是也无法访问子类对象中特有的内容
这是一个父子类:
public class Animal {public String name;public int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat() {System.out.println(name+"正在吃……");}}
class Dog extends Animal {public String color;public Dog(String name, int age,String color) {super(name, age);this.color = color;}public void sleep() {System.out.println(name+"正在睡觉!");}
}
当访问的时候: 父类引用无法访问子类单独的方法
(2)但是可以访问父类子类都存在的方法(重写),这种是发生了动态绑定,在下面的重写部分介绍
1.3.向上转型的内存指向
向上转型就相当于,把子类对象当成父类使用。如果需要调用子类独有的属性,需要通过向下转型回来才能使用。
1.4.向上转型的三种方式
父类、子类
public class Animal {public String name;public int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat() {System.out.println(name+"正在吃……");}
}
class Dog extends Animal {public String color;public Dog(String name, int age,String color) {super(name, age);this.color = color;}public void eat() {System.out.println("正在吃史");}public void sleep() {System.out.println(name+"正在睡觉!");}
}
1.4.1.直接赋值
第一种:
public static void main(String[] args) {Dog dog = new Dog("旺财",2,"黄色");Animal animal = dog;//向上转型animal.eat();}
第二种:
public static void main(String[] args) {Animal animal = new Dog("旺财",2,"黄色");animal.eat();}
1.4.2传参方式
public static void func(Animal animal) {animal.eat();animal.age = 3;}public static void main(String[] args) {Dog dog = new Dog("旺旺",1,"黑色");func(dog);}
1.4.3.通过返回值
public static Animal func2() {return new Dog("旺旺",1,"黑色");}public static void main(String[] args) {Animal animal = func2();animal.name ="666";}
介绍了向上转型,在下面介绍向下转型。
1.5.向下转型
向下转型一般是使用在向上转型之后,因父类引用无法访问子类特有的方法
转型方式:
public static void main(String[] args) {Animal animal = new Dog("1",1,"1");animal.eat();Dog dog = (Dog)animal;dog.sleep();((Dog) animal).sleep();}
注意事项:
总结:
向上转型是安全的,而向下转型是不安全的,慎用。比如:狗一定是动物,但是动物不一定是狗。
做法:经历对实现了向上转型的对象,再使用向下转型
2.重写
重写发生的条件:发生继承
2.1.概念
2.1.1.命名
重写,又称为覆写,覆盖。
2.1.2.啥是重写
对父类的方法进行重新编写
例如:
2.2.重写的规则
重写的模板:
(1)返回值、形参都不能被修改,必须相同。(返回值如果构成父子类关系也可以)
(2)子类重写的方法的修饰访问权限的大小 >= 父类的
不能被重写的:
(1)被final修饰的方法
(2)静态方法
(3)构造方法
(4)被private修饰的方法
以上父类中的方法都是不能被子类重写的!
注解:
(1)重写的注解:@Override
(2)作用:可以标识是被重写的方法;可以帮助校验重写的格式、语法是否正确,否则会报错。
2.3.重写与重载
区别点 | 重写(Override) | 重载(Overloading) |
---|---|---|
参数列表 | 一定不能修改 | 必须修改 |
返回值类 | 一定不能修改(除非构造父子类关系) | 可以修改 |
访问限定符 | 一定不能做到更严格的限制(可以降低限制) | 可以修改 |
2.4.动态与静态绑定
2.4.1.静态绑定
也称为前期绑定,这种代表就是方法重载,在编译时候(代码写完的时候),根据用户传递的参数就确定了 会调用的方法。
2.4.2.动态绑定
也称为后期绑定,典型代表就是重写。在编译期间,是不确定会调用哪一个方法的,只有在程序运行时,才能确定调用的方法。
2.5.避免在构造方法中调用重写的方法
(1)第一种情况
class B {public B() {func();//在构造方法中,调用重写方法(子类父类都有)}public void func() {System.out.println("B.func()");}
}
class D extends B {@Overridepublic void func() {System.out.println("D.func() ");}
}
public class Test2 {public static void main(String[] args) {D d = new D();}
}
在父类的构造方法中,调用了重写的方法。在实例化子类对象的时候,调用的是子类的func()方法。说明在构造方法内,也会发生动态绑定。
(2)第二种情况
class B {public B() {func();//在构造方法中,调用重写方法(子类父类都有)}public void func() {System.out.println("B.func()");}
}
class D extends B {private int num = 1;@Overridepublic void func() {System.out.println("D.func() "+num);}
}
public class Test2 {public static void main(String[] args) {D d = new D();}
}
实例化子类对象时,为什么不会给num赋值
代码指向顺序:实例化子类对象-->子类构造-->父类的构造方法-->调用重写方法,此时num没有被赋值。(当实例化子类的时候,会给子类对象分配一个内存空间,此时属性都会被加载出来,但是没有进行初始化,就默认为0)
说明:
3.使用多态
在上面介绍向上转型的时候,就已经接触到一点多态的影子了。
一个父类、两个子类
public class Animal {public String name;public int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat() {System.out.println(name+"正在吃……");}
}
class Dog extends Animal {public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("正在吃狗粮……");}
}
class Cat extends Animal {public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("正在吃鱼翅……");}
}
3.1.简单的多态实现
3.1.1.先发生重写
3.1.2.发生向上转型
我们使用传参的方式进行向上转型
public static void func(Animal animal) {animal.eat();}public static void main(String[] args) {Dog dog = new Dog("小狗",1);Cat cat = new Cat("小猫",2);func(dog);func(cat);}
3.1.3.多态的发生
(1)多态发生
(2)多态的概念
同一个引用 调用了 同一个方法,但是因为引用的对象不一样,所表现的行为不一样,我们把这种思想称为:多态
3.2.实现自定义多态
下面这种实现的方向,才是以后多态实现的常用手段。一般是通过返回值实现多态
类:
public class Animal {public String name;public int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat() {System.out.println(name+"正在吃……");}
}
class Dog extends Animal {public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃狗粮……");}
}
class Cat extends Animal {public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃鱼翅……");}
}
main函数:
import java.util.Scanner;public class Test {public static void func1(Animal animal) {animal.eat();}public static Animal func(int q) {Scanner in = new Scanner(System.in);if(q == 1) {System.out.print("输入名字:");String s = in.nextLine();System.out.print("输入年龄:");int b = in.nextInt();return new Dog(s,b);}else {System.out.print("输入名字:");String s = in.nextLine();System.out.print("输入年龄:");int b = in.nextInt();return new Cat(s,b);}}public static void main(String[] args) {Scanner in = new Scanner(System.in);int q = 0;do {System.out.println("请输入你的身份:1(dog)/2(cat)");q = in.nextInt();Animal animal = func(q);animal.eat();}while (q == 1 || q == 2);}
}
运行结果:
本节的多态知识就介绍到这里了,快去按照代码练习一下吧。