面向对象之多态
一,先引入一个问题
请先写一个程序,食物food 可以有多种 ,鱼 骨头 米饭 ,动物也又多种,狗 猫 鱼 ,Master类中有一个feel喂食方法,接下来用传统方式解决这个问题
问题是代码复用性不高,不利于维护 由此会引出多态 废话不多说 上代码

public class Animal { private String name; //构造器 get set方法 } //======================================== public class Cat extends Animal { public Cat(String name) { super(name); } } //================================ public class Dog extends Animal { public Dog(String name) { super(name); } } //============================ public class Food { private String name;//构造器 get set 方法 } //============================= public class Fish extends Food{ public Fish(String name) { super(name); } } //================================= public class Bone extends Food { public Bone(String name) { super(name); } } //================================ public class Master { private String name; public Master(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; }
public void feel(Cat cat,Fish fish){ System.out.println("主人" + getName() + "给"+cat.getName()+"喂食"+fish.getName()); } public void feel(Dog dog, Bone bone){ System.out.println("主人" + getName() + "给"+dog.getName()+"喂食"+dog.getName()); } } //================================ public class Test { public static void main(String[] args) { Master master = new Master("阿文"); Cat cat = new Cat("小黄猫"); Fish fish = new Fish("鱼"); master.feel(cat,fish); Dog dog = new Dog("小黄狗"); Bone bone = new Bone("骨头"); master.feel(dog,bone); } }
使用多态机制,可以统一的管理主人喂食的问题 动物有很多 ,食物有很多 不利于维护 所以引出多态
animal 编译类型是 Animal,可以指向(接收)Animal 子类的对象
food 编译类型是 Food ,可以指向(接收) Food 子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());
}
二, 多态的基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的
方法的重载和重写就体现出了多态 ,废话不多说上代码
public class PloyMethod { public static void main(String[] args) { A a = new A(); System.out.println(a.sum(2, 3)); System.out.println(a.sum(3, 4, 5));//调用不同的构造器体体现出重载的多态 B b = new B(); b.say(); a.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; } @Override public void say() { System.out.println("a say() 方法被调用了"); } }
三 , 对象的多态 (重点难点)
1>背记的重要知识点
1)一个对象的编译和运行类型可以不一致
2)编译类型在定义对象时就确定了,不能改变
3)运行类型是可以改变的
4)编译看左边 ,运行看右边
public class Animal { public void cry(){ System.out.println("动物在叫"); } } //------------------------------------------------------- public class Cat extends Animal { @Override public void cry() { System.out.println("喵咪~~~~~~"); } } //---------------------------------------------- public class Dog extends Animal{ @Override public void cry() { System.out.println("汪汪~~~~~"); } } //============================ public class Test { public static void main(String[] args) { Animal animal = new Dog();// 编译看左边 运行看右边 animal.cry();//汪汪~~~~~ animal= new Cat(); animal.cry();//喵咪~~~~~~ } }
四 , 多态注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
1)多态的向上转型
1>本质:父类的引用指向子类的对象
2>语法: 父类类型 引用名 = new 子类类型();
3)特点: 编译类型看左边,运行类型看右边
可以调用父类种的所有成员(须遵守访问权限),不能调用子类中特有成员 ,最终运行效果看子类的具体实现
2)多态的向下转型
1>语法 : 子类类型 引用名 = (子类类型)父类引用;
2>只能强转父类的引用 , 不能强转父类的对象
3>要求父类的引用必须指向当前目标的对象
4>当向下转型后,可以调用子类类型中所有的成员
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,你好"); } } //---------------------------------------------------------- public class Cat extends Animal { String name = "小猫"; public void eat(){//方法重写 System.out.println("猫吃鱼"); } public void catchMouse(){//Cat 特有方法 System.out.println("猫抓老鼠"); } } //==================================== public class Test { public static void main(String[] args) { Animal animal = new Cat();//向上转型 父类的引用指向子类的对象 animal.eat(); //向上转型的调用规则 调用父类中的所有成员 遵循访问规则, animal.show();// 不能调用子类特有的成员,因为在编译阶段,能调用哪些成员,是由编译类型来决定的 animal.sleep();//animal.catchMouse();错误 animal.show();//最终运行效果看子类(运行类型)的具体实现, // 即调用方法时,按照从子类(运行类型)开始查找方法 ,然后调用,规则我前面我们讲的方法调用规则一致。 //怎么调用 animal.catchMouse(); 向下转型 Cat cat = (Cat) animal; //子类类型 引用名 = (子类类型)父类引用; cat.catchMouse();//要求父类的引用必须指向的是当前目标类型的对象 我的理解是先向上转型 再向下转型 System.out.println(animal.name); //属性的调用 看编译类型 ==== 动物 System.out.println(cat.name); //小猫 } }
属性的调用 ,看编译的类型 废话不多说 上代码
public class PolyDetail02 { public static void main(String[] args) { Sub sub = new Sub();// 向上转型 属性的值看编译类型 System.out.println(sub.count); //20 Base base = new Sub(); System.out.println(base.count); //10 } } class Base { //父类 int count = 10;//属性 } class Sub extends Base {//子类 int count = 20;//属性 }
小知识点 instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
public class PolyDetail03 { public static void main(String[] args) { BB bb = new BB(); System.out.println(bb instanceof BB);// true System.out.println(bb instanceof AA);// true AA aa = new BB();//aa 编译类型 AA, 运行类型是 BB //BB 是 AA 子类 System.out.println(aa instanceof AA);// true System.out.println(aa instanceof BB);// true Object obj = new Object(); System.out.println(obj instanceof AA);//false String str = "hello"; System.out.println(str instanceof Object);//true } } class AA{} class BB extends AA{}
五 , java的动态绑定机制
1.当调用对象方法的时候,该方法会和对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里调用哪里使用
public class DynamicBinding { public static void main(String[] args) { A a = new B();//向上转型 //a 的编译类型 A, 运行类型 B System.out.println(a.sum());//会调用子类的sum()方法等于40 -> 当子类没有sum方法,会调用父类的sum()方法等于30 System.out.println(a.sum1());//会调用子类的sum1()方法等于30->当子类没有sum方法,会调用父类的sum1()方法等于20 } } class A {//父类 public int i = 10; public int sum() {//父类 sum() return getI() + 10;//20 + 10 } public int sum1() {//父类 sum1() return i + 10;//10 + 10 } public int getI() {//父类 getI return i; } } class B extends A {//子类 public int i = 20; // public int sum() { // return i + 20; // } public int getI() {//子类 getI() return i; } // public int sum1() { // return i + 10; // } }
六 , 多态的应用
1)多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
/应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组 // 中,并调用每个对象 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; } } //----------------------------------------------------------- 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() + "score" + score; } //特有方法 public void study(){ System.out.println("学生" + getName() + "学习java"); } } //----------------------------------------------------------- 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; } @Override public String say() { return "老师 " + super.say() + " salary=" + salary; } //特有方法 public void teach() { System.out.println("老师 " + getName() + " 正在讲java 课程..."); } } //======================================= public class PloyArray { public static void main(String[] args) { Person[] person = new Person[5]; person[0] = new Person("tom", 25); person[1] = new Student("小明" , 16, 100); person[2] = new Student("小强" ,18 , 200); person[3] = new Teacher("高老师", 32,5000); person[4] = new Teacher("赵老师" , 18 , 8000); for (int i = 0; i < person.length; i++) { //循环遍历多态数组 调用say方法 System.out.println(person[i].say());//动态绑定机制 怎么去掉那个子类的特有方法呢 如下 if (person[i] instanceof Student){ Student students = (Student)person[i]; students.study(); }else if (person[i] instanceof Teacher){ Teacher teacher = (Teacher)person[i]; teacher.teach(); }else { System.out.println("你输入的有误"); } } } }
2)多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
定义一个员工类Employee,包含姓名和月工资,以及计算年工资getAnnual的方法,普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求重写getAnnual方法
测试类中添加一个方法showEmpAnnual(Employee e) ,实现获取任何员工对象的年工资,并在main方法中调用该方法e.getAnnual()
测试类种添加一个方法,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 salary*12; } } //---------------------------------------------------- public class Worker extends Employee { //一个员工类 public Worker(String name, double salary) { super(name, salary); } public void Work(){ System.out.println("员工" + getName() + "努力工作"); } @Override public double getAnnual() { return super.getAnnual(); } } //------------------------------------------------------------------ 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() + "管理方法"); } @Override public double getAnnual() { return super.getAnnual() + bonus; } } //================================= public class TestM { public static void main(String[] args) { Worker tom = new Worker("tom", 5000); Manager xiaoming = new Manager("xiaoming", 1000, 5000); TestM testM = new TestM(); testM.showEmpAnnual(tom); testM.testWork(tom); testM.showEmpAnnual(xiaoming); testM.testWork(xiaoming); } public void showEmpAnnual(Employee employee){ System.out.println(employee.getAnnual()); } public void testWork(Employee employee){ if (employee instanceof Worker){//添加一个方法,testWork,如果是普通员工, ((Worker) employee).Work();// 则调用 work 方法,如果是经理,则调用 manage 方法 }else if (employee instanceof Manager){ ((Manager) employee).manage(); }else { System.out.println("不做处理"); } } }
总结:多态的概念理解 向上转型 向下转型 多态数组 多态参数 运行看左边 编译看右边 动态绑定机制

浙公网安备 33010602011771号