Java多态的理解

Java多态的理解
要点提示:多态意味着父类型的变量可以引用子类型的对象。
面向对象程序设计的三大支柱是封装、继承和多态。
继承关系使一个子类能继承父类的特征,并且附加一些新特征。子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是反过来不成立。例如:每个圆都是一个几何对象,但并非每个几何对象都是圆。因此,总可以将子类的实例传给需要父类型的参数。
例子
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
方法displayObject(第10行)具有GeometricObject类型的参数。可以通过传递任何一个GeometricObject的实例(例如:在第5和6行的newCirc1e(1,“red”,fa1se)和newRectang1e(1,1,“b1ack”,true))来调用disp1ayObject。使用父类对象的地方都可以使用子类的对象。这就是通常所说的多态(polymorphism,它源于希腊文字,意思是“多种形式”)。简单来说,多态意味着父类型的变量可以引用子类型的对象。

1.强制类型转换 cast
例子

double heightAvg1=176.2; 
int heightAvg2=(int)heightAvg1;

2. 向上转型upcasting
1.向上转型不要强制转型
2.父类引用指向的或者调用的方法是子类的方法,这个叫动态绑定
3.向上转型后父类引用不能调用子类自己的方法
• 一个引用变量可以存放对象的地址。
GradedActivity exam;
• 我们可以使用exam变量存放GradedActivity 对象的地址。
exam = new GradedActivity();
• GradedActivity 类是 FinalExam 类的基类。
• 我们也可以用exam变量存放一个 FinalExam 对象的地址。
GradedActivity exam = new FinalExam(50, 7);
FinalExam finalExam = new FinalExam(50, 7);
GradedActivity exam = finalExam; 向上转型

例子

package multistate;

public class Human {

    public void sleep() {
        System.out.println("Human sleep..");
    }

    public static void main(String[] args) {
        Male m = new Male();
        m.sleep();

        Human h = new Male();// (1)向上转型
        h.sleep();             // (2)动态绑定
      // h.speak();  (3)此方法不能编译,报错说Human类没有此方法
    }
}

class Male extends Human {
    @Override
    public void sleep() {
        System.out.println("Male sleep..");
    }

    public void speak() {
        System.out.println("I am Male");
    }
}

class Female extends Human {
    @Override
    public void sleep() {
        System.out.println("Female sleep..");
    }

    public void speak() {
        System.out.println("I am Female");
    }
}

3. 向下转型downcasting
public class Human {
    public void sleep() {
        System.out.println("Human sleep..");
    }
    public static void main(String[] args) {
    // (1)向上转型
    Male m1 = new Male();
        Human h1 = m1;      
        h1.sleep();
        //h1.speak();  // 此时需要向下转型,否则不能调用speak方法。
        
//        // (2)向下转型
//        Male m2 = new Male();
//        Human h2 = m2;  
//        m2 = (Male) h2;         
//        m2.speak(); 
        
//        // (3)向下转型:失败
//        Human h3 = new Human();
//        Male m3 = (Male)h3;
//        m3.speak();               //此时会出现运行时错误,所以可以用instanceOF判断 
//        
//        // (4)向下转型:类型防护
//        Human h4 = new Human();
//        if (h4 instanceof Male){  // 因为h4不是Male的实例,所以不执行if内部代码
//             Male m4 = (Male)h4;
//             m4.speak();
//        }
    }
}
class Male extends Human {
    @Override
    public void sleep() {
        System.out.println("Male sleep..");
    }

    public void speak() {
        System.out.println("I am Male");
    }
}

4. 多态性:基类型的引用对象指向派生类
• 我们也可以用exam对象存放一个 FinalExam 对象的地址。
GradedActivity exam = new FinalExam(50, 7);
• 这条语句创建了一个 FinalExam 对象,并且把对象的地址存放在 exam 对象中。
• 这是一个多态性例子。
• 多态性是指采用多种形式的能力。
• 在Java中, 引用变量可以是多态的, 即它可以存放不同类对象的地址,只要这些类是它声明的类的派生类即可。

  1. 多态性:调用派生类重写的方法
    • GradedActivity 类有三个方法:
    – setScore, getScore, 和 getGrade。
    – 一个GradedActivity类型的引用变量只可以调用这三个方法或这三个方法的重写方法。
    GradedActivity exam = new FinalExam(50, 7);
    System.out.println(exam.getScore()); // This works.
    System.out.println(exam.getGrade()); // This works.
    System.out.println(exam.getPointsEach()); // ERROR!

  2. 多态性和动态绑定
    • 如果派生类对象已经重写了基类中的一个方法:
    – 如果引用变量调用了这个方法,派生类中的版本将会被执行。
    GradedActivity exam = new PassFailExam(50,5,60);
    System.out.println(exam.getGrade());
    • 当一个引用变量含有多态性时,Java执行的是动态绑定 或者迟绑定。
    • Java虚拟机在运行时根据当前引用变量所指的对象的类型决定到底调用哪个版本。

7.多态性
• 如果基类的方法被重写了,那么是对象的类型而不是引用变量的类型决定了哪个方法被调用。
• 实例:
– Polymorphic.java
• 你不能把基类对象赋值给派生类的引用变量。

多态:基类型对象访问派生类重写的方法
• 循环调用基类对象,访问不同派生类方法
• 实参是派生类;行参是基类
例子

GradedActivity[] tests = new GradedActivity[3];

// 第一次考试采用五级计分制,考了75
tests[0] = new GradedActivity();
tests[0].setScore(75);

// 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
// 通过的最低分数线是60分
tests[1] = new PassFailExam(20, 5, 60);

// 第三次是期末考试也采用五级计分制. 总共50题,每题分值相同,考试答错7题
tests[2] = new FinalExam(50, 7);

// 显示每次考试的分数和等级
for(int i=0; i<tests.length; i++){
System.out.println("Score: " + tests[i].getScore()+ "\t" + 
"Grade: " + tests[i].getGrade());
}
public static void main(String[] args) {
GradedActivity[] tests = new GradedActivity[3];

// 第一次考试采用五级计分制,考了75
tests[0] = new GradedActivity();
tests[0].setScore(75);

// 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
// 通过的最低分数线是60分
tests[1] = new PassFailExam(20, 5, 60);

// 第三次是期末考试也采用五级计分制. 总共50题,每题分值相同,考试答错7题
tests[2] = new FinalExam(50, 7);

// 显示每次考试的分数和等级
for(int i=0; i<tests.length; i++){
   showValue(tests[i]);
}
}
public static void showValue(GradedActivity exam){
System.out.println("Score: " + exam.getScore()+ "\t" + 
"Grade: " + exam.getGrade());
}

8.要实现多态需要满足一下几个条件:
• 有继承关系
• 有方法重写(抽象方法)
• 有父类引用指向子类对象

9.多态的优点
• 消除类型之间的耦合关系
• 可替换性
• 可扩充性
• 接口性
• 灵活性
• 简化性

10.实现方式
• 方式一:重写
• 方式二:抽象类和抽象方法
抽象方法只有声明,没有方法体,声明语法如下:
abstract void fun();
包含抽象方法的类即为抽象类,定义类时需要加上关键字 abstract。抽象方法的具体实现在抽象类的子类中完成,所以显然不能用 final 修饰抽象方法。我们不能直接创建抽象类的对象,所以抽象类必须被继承,才能被使用。通常是定义引用变量指向子类对象。

• 方式三:接口
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法,是对类的一组需求描述。接口的思想在于它可以增加很多类都需要实现的功能,但并不关心功能的实现,使用相同接口的类不一定有继承关系。

声明语法

修饰符 interface 接口名称 [extends 其他的接口名] {
  // 常量定义
  // 抽象方法
}

• 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
• 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
• public 修饰的接口可以被任何一个类使用。如果一个接口不加 public 修饰,则称其为友好接口,可以被同一个包中的类使用。
• 接口中的方法都自动地被设置为 public ,接口中的域被自动设置为 public static final。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值