毕向东Java基础教程-继承【下】

2019-12-17  本文已影响0人  Lois_Huang

抽象类

概述

特点

相关问题

示例代码

abstract class Employee
{
    private String name;
    private String id;
    private double salary;

    Employee(String name, String id, double salary)
    {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }

    public String getName(){return name;}
    public void setName(String name){this.name = name;}
    public String getId(){return id;}
    public void setId(String id){this.id = id;}
    public double getSalary(){return salary;}
    public void setSalary(double salary){this.salary = salary;}

    public abstract void work();
}

class Programmer extends Employee
{
    Programmer(String name, String id, double salary)
    {
        super(name, id, salary);
    }
    public void work()
    {
        System.out.println("code...");
    }
}

class Manager extends Employee
{
    private double bonus;
    Manager(String name, String id, double salary, double bonus)
    {
        super(name, id, salary);
        this.bonus = bonus;
    }
    public void work()
    {
        System.out.println("manage...");
    }
}

接口

概述

1. 定义
当一个抽象类中的方法都是抽象的时候,可以将该抽象类用另一种形式定义和表示,就是接口interface(表面上是这样,但实质却很不相同)。

abstract class Demo
{
    abstract void show1();
    abstract void show2();
}

2. 格式interface {}

interface Demo
{
    abstract void show1();
    abstract void show2();
}

3. 接口中的成员修饰符是固定的。
成员常量: public static final,成员函数: public abstract

interface Demo
{
    public static final int NUM = 4;
    public abstract void show1();
    public abstract void show2();
}

由于是固定的,所以可以省略,写成下面的形式(但是阅读性差).

interface Demo
{
    int NUM = 4;
    void show1();
    void show2();
}

4. 接口不可以实例化。
只能由实现了接口的子类覆盖接口中所有的抽象方法后,该子类才可以实例化,否则这个子类就是一个抽象类。

class DemoImpl implements Demo
{
    public void show1()//注意必须写public
    {}
    public void show2()
    {}
}
class InterfaceDemo
{
    DemoImpl d = new DemoImpl();
    System.out.println(d.NUM);
    System.out.println(DemoImpl.NUM);
    System.out.println(Demo.NUM);
    // 以上三种都对,但是不能写d.NUM = 3;
}

5. 接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
在java中不直接支持多继承,因为会出现调用的不确定性,所以java将多继承机制进行改良,变成了多实现。
Example1:一个类可以实现多个接口

interface A
{
    public abstract void show();
}
interface B
{
    public abstract void show();
}
class Test implements A,B
{
    public void show()
    {}
}
class Demo
{
    public static void main(String[] args)
    {
        Test t = new Test();
        t.show();//不会有不确定性,因为在接口中的方法没有方法体
    }
}

注意,以下这种情况不允许多实现。因为若只写1,则不能覆盖到B中的函数,而如果1和2都写,则会冲突。

interface A
{
    public abstract void show();
}
interface B
{
    public abstract int show();
}
class Test implements A,B
{
    /*------1------*/
    public void show()
    {}
    /*------2------*/
    public int show()
    {...}
}

Example2:一个类在继承另一个类的同时,还可以实现多个接口
继承自Q,属于Q的体系,但通过实现接口,扩展功能。接口的出现避免了单继承的局限性。

class Q
{
    public void method()
    {}
}
class Test2 extends Q implements A,B
{}

Example3:接口之间可以多继承
类与类之间是继承关系,类与接口之间是实现关系,接口与接口之间是继承关系。
多继承的问题主要在于方法体。

interface CC
{
    void show();
}
interface MM
{
    void method();
}
interface QQ extends CC,MM
{
    void function();
}
class WW implements WW
{
    //需要覆盖三个方法
    public void show(){}
    public void method(){}
    public void funcion(){}
}

特点

例如,笔记本的接口(好好体会)
笔记本的接口(如USB接口)是提前定义的规则【接口】,外围设备(如U盘、鼠标)的生产产商只需按照该规则生产设备【实现类】即可,人们在使用时【使用类】,只需插拔不同的设备(即不同情况使用不同的实现类),而无需去更改笔记本内部的结构,以后若还有其他外围设备生产,也只用实现该接口就可。
笔记本的接口在于三部分:定义规则、实现规则、使用规则。而规则在java中即是interface。
注意:(引用指向的是对象)接口型引用指向的是其子类的对象

接口与抽象类的异同点

相同点:都是不断抽取出来的抽象的概念。

不同点

多态

概述

1. 定义:某一类事物的多种存在形态。
例:动物中猫,狗。
猫这个对象对应的类型是猫类型:猫 x = new 猫();
同时猫也是动物中的一种,也可以把猫称为动物:动物 y = new 猫();(动物是猫和狗具体事物中抽取出来的父类型。)
猫这类事物既具备猫的形态,又具备着动物的形态,这就是对象的多态性(一个对象,两种形态)。简单说,就是一个对象对应着不同类型。
网上的另一种解释——不同类的对象对同一消息作出不同的响应就叫做多态。就像上课铃响了,上体育课的学生跑到操场上站好,上语文课的学生在教室里坐好一样。

2. 多态在代码中的体现:父类或者接口的引用指向或者接受自己的子类对象。

abstract class Animal
{
    abstract void eat();
}
class Dog extends Animal
{
    void eat()
    {
        System.out.println("啃骨头");
    }
}
class Cat extends Animal
{
    void eat()
    {
        System.out.println("吃鱼");
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Cat c = new Cat();
        Dog d = new Dog();

        method(c);
        method(d);
    }
    public static void method(Animal a) //用父类作为参数,这个函数可以接收其各个子类的对象
    {
        a.eat();
    }
}

3. 多态的好处:提高了代码的扩展性(重用性)和后期可维护性,前期定义的代码可以使用后期的内容。
网上的资料——简单讲多态的作用就是解耦。再详细点讲就是,多态是设计模式的基础,不能说所有的设计模式都使用到了多态,但是23种中的很大一部分,都是基于多态的。

class Pig extends Animal //新加的一个Pig类
{
    void eat()
    {
        System.out.println("吃饲料");
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Cat c = new Cat();
        Dog d = new Dog();

        method(c);
        method(d);
        method(new Pig()); //新加代码
    }
    public static void method(Animal a)
    {
        a.eat();
    }
}

4. 多态的弊端:前期定义的内容不能使用(调用)后期子类的特有内容。

abstract class Animal
{
    abstract void eat();
}
class Dog extends Animal
{
    void eat()
    {
        System.out.println("啃骨头");
    }
    void guardHouse()
    {
        System.out.println("看家");
    }
}
class Cat extends Animal
{
    void eat()
    {
        System.out.println("吃鱼");
    }
    void catchMice()
    {
        System.out.println("抓老鼠");
    }
}
class Pig extends Animal
{
    void eat()
    {
        System.out.println("吃饲料");
    }
    void digEarth()
    {
        System.out.println("拱地");
    }
}
class Demo
{
    public static void main(String[] args)
    {
        /* 第一种方式*/
        Animal a = new Cat();
        a.eat();
        a.catchMice(); //编译错误,在类Animal中找不到方法catchMice()

        /* 第二种方式*/
        Cat c = new Cat();
        method(c);
    }
    public static void method(Animal a)
    {
        a.eat();
        a.catchMice(); //编译错误,在类Animal中找不到方法catchMice()
    }
}

5. 多态的前提
1)存在继承或者实现关系
2)子类重写父类方法
3)父类引用指向子类对象

6. 转型

Animal a = new Cat(); //自动类型提升,猫对象提升到了动物类型。
a.eat();

向上转型(自动):猫一旦提升成动物,访问上就有了局限性,不能再访问猫的特有功能了。其作用就是提高扩展性(可以接收不同类型)和限制对特有功能的访问。
如果还想调用具体动物——猫的特有功能,还可将该对象进行向下转型。

Cat c = (Cat)a;
c.eat();
c.catchMice();

向下转型(需要强转):目的是为了使用子类中的特有方法。

Animal a = new Dog();
Cat c = (Cat)a; //运行报错:java.lang.ClassCastException: Dog cannot be cast to Cat
Son s = (Son)new Father(); //注意这种写法也可以

注意,对于转型,自始至终都是子类对象在做着类型的变化,一会变成父类型,一会变成本类型。

7. instanceof关键字
用于判断对象的具体类型,只能用于引用数据类型判断。

public static void method(Animal a)
{
    a.eat();
    if(a instanceof Cat)
    {
        Cat c = (Cat)a;
        c.catchMice();
    }else if(a instanceof Dog)
    {
        Dog d = (Dog)d;
        d.guardHouse();
    }
}

通常在向下转型前使用,增强程序的健壮性(因为如果不是猫的类型,并且程序没有对其判断的话,直接强转,编译时不会报错,但运行会报错)。

8.多态的分类
1)编译时多态,即方法的重载,从JVM的角度来讲,这是一种静态分派(static dispatch)【函数的多态】
2)运行时多态,即方法的重写,从JVM的角度来讲,这是一种动态分派(dynamic dispatch)【对象的多态】

特点

1. 成员变量
编译时和运行时均只看引用变量所属的类。
简单说,编译和运行都参考等号的左边。

class Father
{
    int num = 3;
}
class Son extends Father
{
    int num = 4;
}
class Demo
{
    public static void main(String[] args)
    {
        Father f = new Son();
        System.out.println(f.num); //输出3,覆盖只发生在函数上;若Father中没有num变量,会编译失败
    }
}

2. 成员函数(非静态)
编译时:要查看引用变量所属的类中是否有所调用的成员(有->编译通过;没有->编译失败)。
运行时:要查看对象所属的类中是否有所调用的成员。
简单说:编译看等号左边,运行看等号右边。

class Father
{
    void show()
    {
        System.out.println("father show");
    }
}
class Son extends Father
{
    void show()
    {
        System.out.println("son show");
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Father f = new Son();
        f.show();
        //Father和Son中都有show(),调用Son的方法,输出son show;
        //Father中没有show(),Son中有,编译失败;
        //Father中有show(),Son中没有,调用Father的方法,输出father show
    }
}

内存图解:

非静态方法需要依赖对象。
动态绑定:在执行期间(非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。程序运行过程中,把函数(或过程)调用与响应调用所需要的代码相结合的过程称为动态绑定。
在Java中重写可以被认为是动态绑定的最佳示例,因为父类和子类具有相同的方法—— 也就是说,它不决定要调用的方法。

3. 静态函数
编译和运行都参考等号的左边。

class Father
{
    static void method(){
        System.out.println("father static method");
    }
}
class Son extends Father
{
    static void method(){
        System.out.println("son static method");
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Father f = new Son();
        f.method();//输出father static method
    }
}

静态绑定:staticfinalprivate方法的绑定是静态绑定,都在编译时完成,因为它们无法被覆盖,所以将始终由某个本地类的对象访问,静态绑定提供了更好的性能。

以下三种类型的方法是没有办法表现出多态特性的(因为不能被重写):
1)static方法,因为被static修饰的方法是属于类的,而不是属于实例的。
2)final方法,因为被final修饰的方法无法被子类重写。
3)private方法和protected方法,前者是因为被private修饰的方法对子类不可见,后者是因为尽管被protected修饰的方法可以被子类见到,也可以被子类重写,但是它是无法被外部所引用的,一个不能被外部引用的方法,怎么能谈多态呢。

上一篇下一篇

猜你喜欢

热点阅读