目录
一.引例
用一个例子类引出多态,当我们有一个Master类中有一个feed(喂食)的方法,完成对动物对象喂食的信息,比如Master给狗喂Bone
pubilc void feed(Dog dog , Bone bone){
}
一般都会这样写,但是当我们有多个动物对象和食物对象时,我们写出的代码会十分冗长,因此为提高代码的复用性,便于维护代码,我们引出多态。
二.多态的基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
三.多态的具体体现
A a = new A();
//体现出形参列表具有多种形态
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() 方法被调用...");
}
}
四.对象的多态
先理解几点要点:
1.一个对象的编译类型和运行类型可以不一致
2.编译类型在定义对象时,就确定了,不能改变
3.运行类型时可以变化的
4编译类型看定义时 = 号的左边,运行类型看 = 号的右边
重点理解编译类型和运行类型(可以看我转载的Java将子类对象赋值给父类对象的文章,讲的非常细致)
要理解编译类型和运行类型,首先我认为我们要弄清什么是编译,什么是运行。编译是声明对象的类型,分配属性,检查语法错误等;运行是,将对象加载内存(一般用new,反射也常用), 运行代码执行功能等。我们举个例子,我们有个Animal类,还有个Dog类,且Dog extends Animal,Animal animal = new Dog();这里的animal的编译类型时Animal,而运行类型是Dog,这句代码真正的对象其实是new Dog,而animal只是对对象的引用,如果大家实在不理解为什么Animal是编译类型,Dog是运行类型,给大家举个例子,一头披着羊皮的狼,它的本质肯定是狼,这是毋庸置疑的,但是它却占这羊的身体(内存空间),当它做一下行为(方法)时,比如吃草调用的就是羊的身体(内存空间),所以当我们调用animal的属性的时候就看它的父类,当我们调用方法时就把它看成子类(比如羊吃草),不知道各位读者能理解编译类型和运行类型,反正我是这样理解的(O^ ~ ^O)
下面给大家一串代码,帮助大家理解
package Polydetail;
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("吃");
}
}
package Polydetail;
public class Dog extends Animal{
String name = "狗";
public void eat(){
System.out.println("吃鱼");
}
}
package Polydetail;
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
animal.run();
animal.eat();
System.out.println(animal.name);
}
}
输出结果
五.多态的细节讨论
1.多态的前提:
两个对象(类)存在继承关系
2.向上转型
(相当于在四中举得Animal和Dog的例子)
1)本质:父类的引用指向子类的对象
2)语法:父类类型 引用名 = new 子类类型();
3)特点:编译类型看左边,运行类型看右边
可以调用父类中所有成员(对象,方法...)(需要遵守访问权限)
不能调用子类中特有的成员
最终运行效果看子类的具体体现
3.向下转型
1)语法 :子类类型 引用名 = (子类类型) 父类引用
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有成员
给大家一串代码,帮助大家理解一下
package Detail01;
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("吃");
}
}
package Detail01;
public class Cat extends Animal{
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){//Cat 特有方法
System.out.println("猫抓老鼠");
}
}
package Detail01;
public class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
}
package Detail01;
public class Polydetail {
public static void main(String[] args) {
Animal animal = new Cat();
Object obj = new Cat();//Object 也是 Cat 的父类
//animal.catchMouse();错误
animal.eat();//猫吃鱼.. animal.run();//跑
animal.show();//hello,你好
animal.sleep();//睡
Cat cat = (Cat) animal;
cat.catchMouse();//猫抓老鼠
// Dog dog = (Dog) animal; //可以吗?
// dog.eat();//错误的,因为现在animal指向cat的内存,
//如果我们要执行上面的那条语句,
//就相当于与把cat转成dao,这显然是错误
System.out.println("ok~~");
}
}
输出结果
六.instancof比较操作符
用于判断断对象的类型是否为 XX 类型或 XX 类型的子类型(是运行类型还是编译类型呢?)
代码举例
class AA{}
class BB extends AA{}
BB bb = new BB();
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
//aa 编译类型 AA, 运行类型是 BB
//BB 是 AA 子类
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);//由此看出是看运行类型
Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);
System.out.println(str instanceof Object);//true
七.多态的动态绑定机制
1.当调用对象方法的时候,该方法会和对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里声明哪里使用
用老韩的一张图解释一下
当我们将B类中sum和sum1方法注销后,输出结果改变成30和20,这是因为在调用sum时B类中没有sum方法,且B extends A所以调用A中的sum方法,但是调用之前会先绑定B中的getl方法,对于机制的第一条,所以第一个输出变为30
注销代码如下,大家可以在自己的编译器上运行一下
package Detail02;
public class DynamicBinding_ {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
System.out.println(a.sum());//?40 -> 30
System.out.println(a.sum1());//?30-> 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.多态数组
package Polyarr;
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 + " " + age;
}
}
package Polyarr;
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;
}
public String say(){
return super.say() + " " + score;
}
}
package Polyarr;
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;
}
public String say(){
return super.say() + " " + salary;
}
}
package Polyarr;
public class arr_ {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack",20);
persons[1] = new Student("jack",18,100);
persons[2] = new Student("jack",19,10);
persons[3] = new Teacher("king",21,200);
persons[4] = new Teacher("sa",12,2000);
for (int i = 0; i < 5; i++) {
System.out.println(persons[i].say());
}
}
}
2. 多态参数
问题描述
代码
package Polyparamaeter;
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;
}
}
package Polyparamaeter;
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;
}
}
package Polyparamaeter;
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();
}
}
package Polyparamaeter;
public class Test {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager jack = new Manager("jack", 5000, 200000);
Test test = new Test();
test.showEmpAnnual(tom);
test.showEmpAnnual(jack);
test.testwork(tom);
}
public void showEmpAnnual(Employee e){
System.out.println(e.getAnnual());//动态绑定机制
}
public void testwork(Employee e){
if (e instanceof Worker){
((Worker) e).work();//向下转型
}else if (e instanceof Manager){
((Manager) e).manage();//向下转型
}else {
System.out.println("no");
}
}
}