java-多态

来源:互联网 发布:jmeter调用java代码 编辑:程序博客网 时间:2024/06/10 15:33

多态的作用是消除类型之间的耦合性,它允许将多种类型(从同一个基类导出的)视为同一类型来处理。让代码只操纵基类,这样,如果要添加一个新类,也不影响到原来的代码

一、向上转型
把某个对象的引用视为对其基类型的引用的做法称作向上转型

class Food{     public void cook(){         System.out.println("Food is cooking.");     }}class Cake extends Food{    public void cook() {        System.out.println("Cake is cooking.");    }}public class Restaurant {    public static void cook(Food food){        food.cook();    }    public static void main(String[] args) {        Cake cake = new Cake();        cook(cake);    }}

Restaurant.cook()方法接受Food的引用,同时也接受任何导出自Food的类。Cake从Food继承而来,所以Food的方法存在在Cake中,所以可以将Cake的引用传递给Food。如果cook()方法直接接受一个Cake作为引用,之后每次添加新的子类的时候,都需要重新编写一个cook()方法。

我们可以根据自己的需求对系统添加多个新类型,但是却不需要改变cook()方法。只有基类接口通信,操纵基类接口的方法不需要改动就可以应用新类。

二、方法调用绑定
接受基类引用的时候,编译器无法知道这是基类还是引用。
默认的绑定方法为前期绑定,java当中的方法只有final,static,private和构造方法是前期绑定

后期绑定(动态绑定)
在运行时根据对象的类型进行绑定,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。

class Food{     public void cook(){         System.out.println("Food is cooking.");     }}class Cake extends Food{    public void cook() {        System.out.println("Cake is cooking.");    }}class Biscuit extends Food{    public void cook(){        System.out.println("biscuit is cooking.");    }}class Apple extends Food{    public void cook(){        System.out.println("apple is cooking.");    }}class FoodGenerator{//是一个工厂,随机的返回一个引用    private Random random = new Random(47);    //调用next()方法时无法知道具体类型是什么    public Food next(){        switch(random.nextInt(3)){        default:            case 0:return new Cake();            case 1:return new Biscuit();            case 2:return new Apple();            //在return时产生向上转型        }    }}public class Restaurant {    private static FoodGenerator foodGenerator = new FoodGenerator();    public static void main(String[] args) {        Food[] foods = new Food[5];        for (int i = 0; i < foods.length; i++) {            foods[i] = foodGenerator.next();        }        for (Food food : foods) {            food.cook();        }    }}

输出:

apple is cooking.apple is cooking.biscuit is cooking.apple is cooking.biscuit is cooking.

编译器不需要获得任何特殊信息就能进行正确的调用,对cook()方法的调用都是动态绑定进行的

三、缺陷
1、private方法被自动认为是final方法,而且对导出类是屏蔽的,所以不能被重载

public class DefectDemo {    private void f(){        System.out.println("private f().");    }    public static void main(String[] args) {        DefectDemo defectDemo = new Defect1();        defectDemo.f();    }}class Defect1 extends DefectDemo{    public void f(){        System.out.println("public f().");    }}

输出

private f().

Defect1类中的f()方法被认为是一种全新的方法。所以调用的仍然是基类的f().

2、域和静态方法
任何域访问操作都由编译器解析,因此不是多态的。
如果某个方法是静态的,它的行为就不具有多态的,静态方法是与类,而并非与单个的对象相关联的。

四、构造器和多态

调用构造器遵循的顺序
1)在其他任何事物发生之前,将分配给对象的储存空间初始化成二进制的零。
2)调用基类构造器
3)按照声明的顺序调用成员的初始化方法
4)调用导出类的构造器主体

class Meal{    void cook(){        System.out.println("Meal is cooking");    }    public Meal() {        System.out.println("Meal before cook");        cook();        System.out.println("Meal after cook");    }}class Fish extends Meal{    private int num = 2;    public Fish(int num) {        this.num = num;        System.out.println("Fish.Fish(), num = " + num);    }    void cook(){        System.out.println(num + " Fishes is cook");    }}public class PolyMethod {    public static void main(String[] args) {        new Fish(3);    }}

结果:

Meal before cook0 Fishes is cookMeal after cookFish.Fish(), num = 3

在一个构造器的内部调用了正在构造的对象的某个动态绑定方法,用到了被覆盖后的方法,但是它还没有初始化,所以才会输出0 Fishes is cook一个动态绑定的方法调用会向外深入到继承层次结构内部,它可以调用导出类里的方法。

编写构造器时的一条准则

用尽可能简单的方法是对象进入正常状态,如果可以的话,避免调用其他方法

在清理时,如果某个子对象要依赖于其他对象,销毁的顺序应该和初始化的顺序相反,先对导出类清理,然后才是基类。可以引入计数来跟踪仍旧访问着共享对象的对象数量。

0 0
原创粉丝点击