多态(polymorphic)
多态基本介绍
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现
1、方法的多态(重写和重载体现多态)
// 例子
public class Poly {
public static void main(String[] args) {
// 方法重载体现多态
A a = new A();
// 传入不同的参数,会调用不同sum 方法,就体现多态
System.out.println(a.sum(10, 20));
System.out.println(a.sum(10, 20, 30));
// 方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
// 父类
class B{
public void say(){
System.out.println("B say() 方法被调用...");
}
}
// 子类
class A extends B{
public int sum(int n1, int n2){ // 和下面sum 构成重载
return n1 + n2;
}
public int sum(int n1, int n2, int n3){
return n1 + n2 + n3;
}
public void say(){
System.out.println("A say() 方法被调用...");
}
}
2、对象的多态(核心、重点、难点)
1)一个对象的编译类型和运行类型可以不一致;
// 父类的一个引用指向子类的一个对象
Animal animal = new Dog(); // animal 编译类型是 Animal,运行类型是 Dog
2)编译类型在定义对象时,就确定了,不能改变;
3)运行类型时可以变化的,可以通过 getClass() 来查看运行类型;
animal = new Cat(); // animal 的运行类型变成了Cat,编译类型仍然是 Animal
4)编译类型看定义时 " = " 的左边,运行类型时看 " = " 的右边;
// Animal
public class Animal {
public void cry(){
System.out.println("Animal cry() 动物在叫...");
}
}
// Dog
public class Dog extends Animal{
public void cry() {
System.out.println("Dog cry() 小狗汪汪叫...");
}
}
// Cat
public class Cat extends Animal{
public void cry() {
System.out.println("Cat cry() 小猫喵喵叫...");
}
}
public class PolyObject {
public static void main(String[] args) {
// 体验对象多态特点
// animal 编译类型就是 Animal,运行类型 Dog
Animal animal = new Dog();
// 运行时,执行到该行时,animal 运行类型是 Dog,所以 cry 就是 Dog 的 cry
animal.cry(); // 小狗汪汪叫...
// animal 编译类型就是 Animal,运行类型 Cat
animal = new Cat();
animal.cry(); // 小猫喵喵叫...
}
}
多态的转型、注意事项和细节
多态的前提是: 两个对象(类)存在继承关系;
多态的向上转型:
1)本质:父类的引用指向了子类的对象;
2)语法:父类类型 引用名 = new 子类类型();
3)特点:① 编译类型看左边,运行类型看右边;
② 可以调用父类中的所有成员(需遵守访问权限);
③ 不能调用子类中特有成员;
④ 最终运行效果看子类的具体实现!
// 父类
public class Animal {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void eat(){
System.out.println("吃");
}
public void show(){
System.out.println("hello,你好");
}
}
// Cat 类
public class Cat extends Animal{
public void eat(){ // 方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){ // Cat特有方法
System.out.println("猫抓老鼠");
}
}
public class PolyDetail {
public static void main(String[] args) {
// 向上转型: 父类的引用指向了子类的对象
// 语法: 父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
Object object = new Cat(); // 可以运行, Object 也是 Cat 的父类
// 向上转型调用方法的规则如下:
// 1)可以调用父类中的所有成员 (需遵守访问权限)
// 2)但是不能调用子类的特有成员
// 3)因为在编译阶段, 能调用哪些成员, 是由编译类型决定的
// animal.catchMouse(); 错误
// 4)最终运行效果看子类 (运行类型) 的具体实现
// 即调用方法时, 按照从子类 (运行类型) 开始查找方法, 然后调用
animal.eat(); // 猫吃鱼...
animal.run(); // 跑
animal.show(); // hello,你好
animal.sleep(); // 睡
}
}
运行结果:
多态的向下转型:
1)语法:子类类型 引用名 = (子类类型) 父类引用;
2)规则:① 只能强转父类的引用,不能强转父类的对象;
② 要求父类的引用必须指向的是当前目标类型的对象;
③ 可以调用子类类型中所有的成员;
// Dog 类
public class Dog extends Animal{
}
public class PolyDetail {
public static void main(String[] args) {
Animal animal = new Cat();
// 向下转型:
// 1)子类类型 引用名 = (子类类型) 父类引用;
// cat 的编译类型是 Cat, 运行类型是 Cat
Cat cat = (Cat) animal;
cat.catchMouse(); // 猫抓老鼠
// 2)要求父类的引用必须指向的是当前目标类型的对象
// 报错,类转换异常,此时是将 Dog类 强转为了Animal类的Cat类
Dog dog = (Dog) animal;
// 如果先 new 一个 Dog类
// Animal animal = new Dog();
// 此时 Dog dog = (Dog) animal; 是正确的
}
}
注意事项和细节
1、属性没有重写之说!属性的值看编译类型;
public class PolyDetail02 {
public static void main(String[] args) {
// 属性没有重写之说!属性的值看编译类型
Base base = new Sub(); // 编译类型为 Base
System.out.println(base.count); // 10
Sub sub = new Sub(); // 编译类型为 Sub
System.out.println(sub.count); // 20
}
}
class Base{ // 父类
int count = 10;
}
class Sub extends Base{ // 子类
int count = 20;
}
运行结果:
2、instanceof 比较操作符,用于判断对象的运行类型是否为 XX类型 或 XX类型的子类型;
public class PolyDetail03 {
public static void main(String[] args) {
// 判断对象的类型是否为 XX类型 或 XX类型的子类型
BB bb = new BB();
System.out.println(bb instanceof BB); // ture
System.out.println(bb instanceof AA); // ture
// aa 编译类型 AA,运行类型是 BB
// 判断的是 AA 的运行类型 是否为 BB类型 或 BB类型的子类型
AA aa = new BB();
System.out.println(aa instanceof BB); // ture
System.out.println(aa instanceof AA); // ture
Object object = new Object();
System.out.println(object instanceof AA); // false
String str = "hello";
System.out.println(str instanceof Object); // ture
}
}
class AA{} // 父类
class BB extends AA{} // 子类
运行结果:
多态的应用
实例
1)创建1个Person对象、2个Student对象和2个Teacher对象;
2)统一放在数组中,并调用每个对象say方法;
3)子类特有的方法,比如Teacher有一个 teach,Student有一个 study。
public class Person { // 父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say(){
return name + " \t" + "age = " + age;
}
}
// 学生 子类
public class Student extends Person{
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
// 重写父类的 say() 方法
@Override
public String say() {
return "学生 " + super.say() + "\t" + " score = " + score;
}
// 特有方法
public void study(){
System.out.println("学生" + getName() + "正在上课...");
}
}
// 老师 子类
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
// 重写父类的 say() 方法
@Override
public String say() {
return "老师 " + super.say() + "\t" + " salary = " + salary;
}
// 特有方法
public void teach(){
System.out.println("老师" + getName() + "正在授课...");
}
}
// 运行
public class PolyArr {
public static void main(String[] args) {
// 创建1个Person对象、2个Student对象和2个Teacher对象
// 统一放在数组中,并调用每个对象say方法
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("tom", 16, 95);
persons[2] = new Student("lucy", 14, 87);
persons[3] = new Teacher("smith", 27, 14000);
persons[4] = new Teacher("Rosy", 45, 20000);
// 循环遍历数组,调用 say()
for (int i = 0; i < persons.length; i++) {
// 提示: persons[i] 编译类型是 Person, 运行类型是根据实际情况由 JVM 来判断
System.out.println(persons[i].say()); // 动态绑定机制
// 使用类型判断 + 向下转型
if (persons[i] instanceof Student){ // 判断persons[i] 的运行类型是不是 Student
// 可以缩写: ((Student)persons[i]).study();
Student student = (Student)persons[i]; // 向下转型
student.study();
}else if (persons[i] instanceof Teacher){ // 判断persons[i] 的运行类型是不是 Teacher
// 可以缩写: ((Teacher)persons[i]).teach();
Teacher teacher = (Teacher)persons[i]; // 向下转型
teacher.teach();
}else if (persons[i] instanceof Person){
}else System.out.println("你的类型有误,请自己检查...");
}
}
}
运行结果:
多态参数
1)定义员工类 Employee,包含姓名 name 和 月工资 [private],以及计算年工资 getAnnual;
2)普通员工 worker 和经理 manager 继承了员工类,经理类多了奖金 bonus 属性和管理 manage 方法,普通员工多了 work 方法,普通员工和经理类要求分别重写 getAnnual 方法;
3)测试类中添加一个方法 showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()];
4)测试类中添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法。
// 父类
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
// 得到年工资的方法
public double getAnnual(){
return 12 * salary;
}
}
// worker 子类
public class Worker extends Employee{
public Worker(String name, double salary) {
super(name, salary);
}
public void work(){
System.out.println("普通员工 " + getName() + " is working...");
}
@Override
public double getAnnual() { // 普通员工没有其他收入,直接调用父类方法
return super.getAnnual();
}
}
// manager 子类
public class Manager extends Employee{
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage(){
System.out.println("经理 " + getName() + " is managing...");
}
// 重写获取年薪的方法
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
// 测试类
public class Parameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 3200);
Manager smith = new Manager("smith", 7000, 100000);
Parameter parameter = new Parameter();
Parameter.showEmpAnnual(tom);
Parameter.showEmpAnnual(smith);
Parameter.testWork(tom);
Parameter.testWork(smith);
}
// showEmpAnnual(Employee e)
// 实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()]
public static void showEmpAnnual(Employee e){
System.out.println(e.getAnnual()); // 动态绑定机制
}
//添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法
public static void testWork(Employee e){
if (e instanceof Worker){
((Worker) e).work(); // 向下转型
}else if(e instanceof Manager){
((Manager) e).manage(); // 向下转型
}else System.out.println("不做处理...");
}
}
运行结果: