9_多态

多态的概念

Rect类重写show方法的使用

Shape.java

Shape.java

package cn.itcast.day01.demo13;

public class Shape {
    private int x;
    private int y;

    public Shape() {}

    public Shape(int x, int y) {
        setX(x);
        setY(y);
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void show() {
        System.out.println("横坐标:" + x + ",纵坐标:" + y);
    }
}

Rect.java

package cn.itcast.day01.demo13;

public class Rect extends Shape{
    int len;
    int wid;

    public Rect() {}

    public Rect(int x, int y, int len, int wid) {
        super(x, y);
        setLen(len);
        setWid(wid);
    }

    public int getLen() {
        return len;
    }

    public void setLen(int len) {
        this.len = len;
    }

    public int getWid() {
        return wid;
    }

    public void setWid(int wid) {
        this.wid = wid;
    }

    @Override
    public void show() {
        super.show();
        System.out.println("长度是:" + len + ",宽度是:" + wid);
    }
}

ShapeRectTest.java

package cn.itcast.day01.demo13;

public class ShapeRectTest {
    public static void main(String[] args) {
        Shape s1 = new Shape(10, 20);
        s1.show();
        System.out.println("--------------------------------");

        Rect r1 = new Rect(30, 40, 50, 60);
        r1.show();
        System.out.println("--------------------------------");

        Shape sr = new Rect(70, 80, 90, 100);
        //当Rect类中没有重写show方法时,下面的代码调用shape类中的show方法
        //当Rect类中重写show方法后,下面的代码在编译阶段调用Shape类的方法,在运行阶段调用Rect类中的show方法
        sr.show();
        System.out.println("--------------------------------");
    }
}

编译并执行ShapeRectTest.java

横坐标:10,纵坐标:20
--------------------------------
横坐标:30,纵坐标:40
长度是:50,宽度是:60
--------------------------------
横坐标:70,纵坐标:80
长度是:90,宽度是:100
--------------------------------

子类继承父类,当子类重写父类的方法后,代码在编译阶段调用父类的方法,运行阶段调用子类的方法。

多态的特点

引用数据类型之间转换的方式

ShapeRectTest.java

package cn.itcast.day01.demo13;

public class ShapeRectTest {
    public static void main(String[] args) {
        Shape sr = new Rect(70, 80, 90, 100);
        int ib = ((Rect) sr).getLen(); //强制转换:父类转子类。大到小的转换。
        System.out.println("--------------------------------");
    }
}

引用数据类型转换的注意事项

引用数据类型之间的转换必须发生在父子类之间,否则编译报错。

目前已经学过的异常有:
	ArithmeticException算术运算异常
	ArrayIndexOutOfBoundsException数据越界异常
	NullPointerException空指针异常
	ClassCastException类型转换异常

Circle.java

package cn.itcast.day01.demo13;

public class Circle extends Shape{
}

ShapeRectTest.java

package cn.itcast.day01.demo13;

public class ShapeRectTest {
    public static void main(String[] args) {
        Shape sr = new Rect(70, 80, 90, 100);
        int ib = ((Rect) sr).getLen(); //强制转换:父类转子类。大到小的转换。
        
        if(sr instanceof Circle) {
            System.out.println("可以放心转换了");
            Circle c1 = (Circle)sr;
        } else {
            System.out.println("强转有风险,操作需谨慎!");
        }
        System.out.println("--------------------------------");
    }
}

多态的实际意义

首先看下面的例子:

ShapeTest.java

package cn.itcast.day01.demo13;

public class ShapeTest {
    public static void draw(Rect r) {
        r.show();
    }

    public static void draw(Circle c) {
        c.show();
    }

    public static void main(String[] args) {
        Rect r1 = new Rect(1, 2, 3, 4);
        ShapeTest.draw(r1);

        System.out.println("-------------------");

        Circle c1 = new Circle(5, 6, 7);
        ShapeTest.draw(c1);
    }
}
//执行结果:
横坐标:1,纵坐标:2
长度是:3,宽度是:4
-------------------
横坐标:5,纵坐标:6
半径是:7

Rect和Circle都是Shape的子类,在ShapeTest类中,当需要画出Rect(矩形)和Circle(圆形)时,我们需要分别写两个draw方法,形参分别是RectCircle对象,在调用各自的show方法。这样显得极其繁琐,有相当多的冗余代码。

于是我们这样改写代码:ShapeTest.java

package cn.itcast.day01.demo13;

public class ShapeTest {
    public static void draw(Shape s) {
        //编译阶段调用父类的版本,运行阶段调用子类重写以后的版本
        s.show();
    }

    public static void main(String[] args) {
        Shape r1 = new Rect(1, 2, 3, 4);
        ShapeTest.draw(r1);

        System.out.println("-------------------");

        Shape c1 = new Circle(5, 6, 7);
        ShapeTest.draw(c1);
    }
}

改写后的代码就使用到了多态的一个应用场景:父类的引用指向子类的对象,形成了多态,此时编译阶段调用父类的版本,运行阶段调用子类重写以后的版本。

所以说,多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果。

抽象方法和抽象类的概念

实例:

AbstractTest.java

package cn.itcast.day01.demo13;

//抽象类:用abstract修饰类
public abstract class AbstractTest {
    private int cnt;

    public AbstractTest() {
    }

    public AbstractTest(int cnt) {
        setCnt(cnt);
    }

    public int getCnt() {
        return cnt;
    }

    public void setCnt(int cnt) {
        this.cnt = cnt;
    }
    
    //抽象方法:用abstract修饰方法
    public abstract void show();
}

抽象类的实际意义

写一个类继承AbstractTest类:

package cn.itcast.day01.demo13;

public class SubAbstractTest extends AbstractTest {
    @Override
    public void show() {
        System.out.println("子类SubAbstractTest继承抽象类AbstractTest之后,子类必须重写父类的抽象方法show");
    }

    public static void main(String[] args) {
        SubAbstractTest sat = new SubAbstractTest();
        sat.show();

        System.out.println("--------------------");

        AbstractTest at = new SubAbstractTest();
        at.show();
    }
}
//执行结果:
子类SubAbstractTest继承抽象类AbstractTest之后,子类必须重写父类的抽象方法show
--------------------
子类SubAbstractTest继承抽象类AbstractTest之后,子类必须重写父类的抽象方法show

所以抽象类的实际意义是:

抽象类的应用

Account.java

package cn.itcast.day01.demo13;

public abstract class Account {
    private double money;

    public Account() {
    }

    public Account(double money) {
        setMoney(money);
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        if (money >= 0) {
            this.money = money;
        } else {
            System.out.println("账户金额必须大于等于0!");
        }
    }

    public abstract double getLiXi();
}

FixedAccount.java

package cn.itcast.day01.demo13;

public class FixedAccount extends Account {
    @Override
    public double getLiXi() {
        return getMoney() * 0.03 * 1;
    }

    public FixedAccount(double money) {
        super(money);
    }

    public FixedAccount() {
    }

    public static void main(String[] args) {
        Account acc = new FixedAccount(1000);
        double res = acc.getLiXi();
        System.out.println("计算的利息是:" + res);
    }
}
//执行结果:
计算的利息是:30.0

一些注意事项

  • private和abstract不能共同修饰一个方法
  • final和abstract不能共同修饰一个方法
  • static和abstract不能共同修饰一个方法

接口的基本概念

InterfaceTest.java

package cn.itcast.day01.demo13;

//自定义一个接口,关键字:interface
public interface InterfaceTest {
    public static final int cnt1 = 1;
    /*public static final*/ int cnt2 = 1;   //只能有常量

    private void show() {}  //从java9开始允许接口中出现私有方法

    public abstract void show2(); //接口中只能有抽象方法(新特性除外),public abstract关键字可以省略,但是建议写上
}

接口的实际意义

弥补子类无法继承多个父类的缺点:子类可以实现多个接口。

Metal.java

package cn.itcast.day01.demo14;
//金属类
public interface Metal {
    //自定义抽象方法描述发光行为
    public abstract void shine();
}

Money.java

package cn.itcast.day01.demo14;
//货币类
public interface Money {
    //自定义抽象方法描述购物行为
    public abstract void Buy();
}

Gold.java

package cn.itcast.day01.demo14;

//黄金类
public class Gold implements Metal, Money {
    @Override
    public void shine() {
        System.out.println("发出了金黄色的光芒...");
    }

    @Override
    public void Buy() {
        System.out.println("买了好多好吃的。。。");
    }

    public static void main(String[] args) {
        Metal m1 = new Gold();
        m1.shine();

        Money m2 = new Gold();
        m2.Buy();
    }
}
//执行结果:
发出了金黄色的光芒...
买了好多好吃的。。。

类和接口之间的关系

先来写一个实例:

接口只能继承接口,不能继承普通类。

Runner.java:

package cn.itcast.day01.demo15;

public interface Runner {
    public abstract void Run();
}

Hunter.java:

package cn.itcast.day01.demo15;

public interface Hunter extends Runner{
    public abstract void Hunt();
}

Man.java:

package cn.itcast.day01.demo15;

public class Man implements Hunter {
    @Override
    public void Hunt() {
        System.out.println("正在追捕一只小白兔...");
    }

    @Override
    public void Run() {
        System.out.println("被人追赶,正在玩命逃跑...");
    }

    public static void main(String[] args) {
        //声明接口类型的引用执行实现类的对象,形成了多态
        Runner runner = new Man();
        runner.Run();

        Hunter hunter = new Man();
        hunter.Hunt();
    }
}

执行结果:

被人追赶,正在玩命逃跑...
正在追捕一只小白兔...

接口和抽象类的主要区别

Hunter.java

package cn.itcast.day01.demo15;

public interface Hunter extends Runner {
    public abstract void Hunt();

    //增加非抽象方法
    public default void show1() {
        System.out.println("这里仅仅是接口类的默认功能,实现类可以自由选择是否重写!");
    }

    //增加静态方法,隶属于类层级,也就是接口层级
    public static void test() {
        System.out.println("这里是静态方法,可以直接通过接口名. 的方式调用,省略对象的创建");
    }
}
posted @ 2020-10-09 22:33  咕噜噜~  阅读(225)  评论(0编辑  收藏  举报