第五章——接口与继承

来源:互联网 发布:软件mt4 编辑:程序博客网 时间:2024/06/04 18:07

这段时间一鼓作气学习了 Java 接口与继承这一块以及与之相关的部分散乱的内容,现在来做个总结


接口

Java 语言的接口

Java 程序语言中,接口与类相似,是一个引用类型,只包含变量、方法签名和嵌套类型,没有方法体。接口不能实例化,只能由类来实现,或被其他接口继承。接口的创建类似于类的创建

    public interface Relatable {        public int isLargerThan(Relatable other);        //more method signatures    }

接口内的方法签名没有花括弧,并且直接以分号结束。要使用接口,就要编写类来实现该接口。实例化类实现接口时,要给接口中声明的方法提供方法体:

class RectanglePlus implements Relatable {    public int isLargerThan(Relatable other) {        //Specific code to achieve...    }}

1.1.将接口用作API

通过 API 的方式,接口中定义的方法签名和返回类型调用类中具体的实现方法。API 对外界公开,但是具体类的实现其实是隐藏的。方法的具体实现可以修改,只要继续实现用户依赖的接口即可。

1.2.接口和多重继承

Java 不支持多重继承,也就是说类只能继承一个类,但是提供了一种替代方法,可以实现多个接口,因此,类可以有多个引用类型:类本身的类型和实现的接口的类型。也就是说,如果变量声明为接口的类型,其值就能引用那些由实现该接口的类实例化得到的对象。

1.3.定义接口

接口声明包括修饰符、interface 关键字、接口名、逗号分隔的父接口、接口体:

public interface SubInterface extends SuperInterface1,SuperInterface2,SuperInterface3 {    //more method signatures}

同时也要注意接口的访问修饰符定义的访问级别:

  • public——包内任意类课使用该接口。
  • 不是 public 只有同一个包内的类可以访问该接口

1.4.实现接口

声明类中实现一个接口,就要在类声明中加入 implements 子句。
Relatable 接口定义如何比较对象的大小:

public interface Relatable{    public int isLargerThan(Relatable other);}

通过 Rectangle 类实现 Relatable 接口重写成 RectanglePlus 类:

public class RectanglePlus implements Relatable {    public int width = 0;    public int height = 0;    public Point origin;    //构造器    public RectanglePlus(){        origin = new Point(0,0);    }    public RectanglePlus(Point p) {        origin = p;    }    public RectanglePlus(Point p,int w,int h){        origin = p;        width = w;        height = h;    }    //动点    public void move(int x,int y){        origin.x = x;        origin.y = y;    }    //面积    public int getArea(){        return width * height;    }    //比较大小    public int isLargerThan(Relatable other) {        RectanglePlus otherRect = (RectanglePlus)other;        if(this.getArea() < otherRect.getArea())            return -1;        else if (this.getArea() > otherRect.getArea())            return 1;        else            return 0;    }}

1.5.将接口用作类型

定义新接口时,会定义一个新的引用数据类型。能够使用任意其他数据类型名的任意位置,都可以使用接口名。如果定义了一个接口类型的引用变量,那么赋值给该变量的任意对象都必须是实现该接口的实例。

举个例子。假设方法可以获取一对对象中较大的对象;其中,任意对象都是实现 Relatable 接口的类的实例。

public Object findLargest(Object object1,Object object2){        Relatable obj1 = (Relatable)object1;        Relatable obj2 = (Relatable)object2;        if(obj1.isLargerThan(obj2) > 0)            return object1;        else            return object2;    }

这里的 findLargest 方法中 object 被强制转换成了 Relatable 类型。类型转换会告诉编译器给对象的实际类型。并且它可以直接调用 Relatable 类中的 isLargerThan 方法

1.6. 重写接口

考虑之前的 Relatable 接口:

public interface Relatable {    public int isLargerThan(Relatable other);    //more method signatures}

假设现在用户有一个需求,需要实现一个新的方法 findSmallest(),那么我们之前提供给他们的接口就不能够满足新的需求,因为旧接口中并没有满足他们要求的方法,这时,我们就需要在接口中添加新的方法 :

public interface Relatable {    public int isLargerThan(Relatable other);    public Object findSmallest(Object object1,Object object2);}

这时,所有实现旧接口的类都无法执行,因为这些类没有重写接口的所有方法。

但是,我们从一开始就设计这个接口的所有用途,几乎是不可能的。因此要创建很多个接口。如果要实现上述中的功能,可以通过继承 Relatable 类创建 RelatablePlus 接口:

public interface RelatablePlus extends Relatable {    public Object findSmallest(Object object1,Object object2);}

这样需要实现 findSmallest() 功能的就使用新接口,不需要的就是用旧接口。


继承

Java语言中的继承

在之前的学习中已经多次遇到了继承这个问题,就像之前提到的那样,继承是 Java 语言面向对象编程的一个重要的组成部分。继承提供功能强大且自然的软件组织和架构机制。

在 Java 语言中,类可以由其他类派生而得,因此要继承他们的字段和方法

定义:

  • 由其它类派生出的类叫子类(也叫派生类拓展类孩子类)。派生出子类的类叫父类(也叫超类基类)。
  • 除了 Object 没有超类外,每个类有且只有一个直接超类(单继承)。没有显示超类,每个类都是 Object 的隐式子类。
  • 类可以派生子类,子类又可以派生子类。最顶层的是 Object 。

2.1. Java 平台中类的层次结构

Object 类在 Java.lang 包中定义,定义和实现所有类的通用行为。
这里写图片描述

Object 是所有类中最一般的类,越靠近底端的类,行为越特殊。

2.2. 继承实例

子类继承父类的所有字段和方法,另外,子类还可以添加自己的字段以及方法。除了构造器外,不需要重写一遍子类,如果父类所包含的方法很复杂,这种做法就很有价值。

public class MountainBike extends Bicycle {    private String suspension;    public MountainBike(int startCadence,int startGear,int startSpeed,String suspensionType){        super(startCadence, startGear, startSpeed);        this.setSuspension(suspensionType);    }    public String getSuspension() {        return this.suspension;    }    public void setSuspension(String suspensionType) {        this.suspension = suspensionType;    }}

注意:子类不会继承父类的私有成员。但是,如果超类声明了一个 public 或者 protected 方法访问这个私有字段,子类就可以通过这些方法访问这些字段。

2.3. 转换对象

对象的数据类型是实例化得到该对象的类。

MountainBike myBike = new MountainBike();

在这个例子中,myBike 是MountainBike 类型。而 MountainBike 由 Bicycle 和 Object 衍生而来,因此 myBike 可以当作 Object 或者 MountainBike 类型来调用。

转换就是在继承和实现支持的对象类型范围内,用一种类型替换成另一种类型。

(1).隐式转换

Object obj = new MountainBike();

这个例子中,obj 对象即是 Object 类型又是 MountainBike 类型。这种方式就叫做隐式转换。

(2).显示转换

MountainBike myBike = obj;

编译器会报错,因为编译器不知道 obj 是 MountainBike 对象。这就需要告知编译器 obj 是一个MountainBike 对象:

MountainBike myBike = (MountainBike)obj;

这就叫做显示转换。显示转换会插入运行时检查,此时,编译器安全假设 obj 是 MountainBike 对象。如果运行时 obj 不是 MountainBike 对象,会抛出异常。

2.4. 覆盖和屏蔽

(1). 实例方法

如果子类的实例方法的方法签名和返回类型都与超类的实例方法相同,就会覆盖超类的方法。使用覆盖方法,子类可以继承那些行为很接近的超类,然后按需修改这些行为。

覆盖方法与被覆盖方法具有相同的方法名、参数数量、参数类型和返回类型。覆盖方法也可以返回被覆盖类型的子类型,这种技术叫做协边返回类型。

(2). 类方法

如果子类的类方法的方法签名和返回类型都与超类的实例方法相同,就会屏蔽超类的方法。

覆盖和屏蔽有很重要的区别:覆盖时,调用的是子类的方法,而屏蔽时,调用的方法取决于调用于哪个类:超类或子类。

例子:

首先我在父类 Bicycle 类中插入下列两个方法,一个实例方法,一个类方法

public static void testClassMethod(){    System.out.println("The class" + " method in Bicycle.");}public void testInstanceMethod(){    System.out.println("The class" + " method in Bicycle.");}

然后我在子类 MountainBike 中也插入同样的方法以及代码段。
调用:

public static void main(String[] args) {    MountainBike mybicycle = new MountainBike(10, 20, 10, "Dual");    Bicycle mybicyclePlus = mybicycle;    Bicycle.testClassMethod();    mybicyclePlus.testInstanceMethod();}

实例化一个 MountainBike 类型的 mybicycle 对象,然后将 mybicycle 赋值给 Bicycle 类型的 mybicyclePlus 对象,其中,不难看出,MountainBike 类覆盖了 Bicycle 类的实例方法,屏蔽 Bicycle 类的类方法。当 main 方法创建一个 mybicycle 实例,并调用类上的 testClassMethod() 方法和实例上的 testInstanceMethod() 方法。程序输出如下:

The class method in Bicycle.The class method in myBicycle.

注意:子类可以重载从父类继承的方法。重载方法既不屏蔽也不覆盖父类的方法,是子类独有的新方法。

2.5. 多态性

为了说明多态性,我再创建一个 Bicycle 派生出来的子类 RoadBike 类,与 MountainBike 类的构造方式一样。MountainBike 类添加一个 suspension 字段,其值为字符串类型:

public class MountainBike extends myBicycle {    private String suspension;    public MountainBike(int startCadence,int startGear,int startSpeed,String suspensionType){        super(startCadence, startGear, startSpeed);        this.setSuspension(suspensionType);    }    public String getSuspension() {        return this.suspension;    }    public void setSuspension(String suspensionType) {        this.suspension = suspensionType;    }    public void printDesccrption() {        super.printDescription();        System.out.println("The MountainBike has a" + getSuspension() + " suspension.");    }}public class RoadBike extends myBicycle {    private int tireWidth;    public RoadBike(int startCadence,int startSpeed,int startGear,int newTireWidth){        super(startCadence, startGear, startSpeed);        this.setTireWidth(newTireWidth);    }    public int getTireWidth() {        return this.tireWidth;    }    public void setTireWidth(int newTireWidth) {        this.tireWidth = newTireWidth;    }    public void printDescription(){        super.printDescription();        System.out.println("The RoadBike" + " has " + getTireWidth() + " MM tires.");    }}

至此已经有三个类:Bicycle、MountainBike、RoadBike。两个子类覆盖了父类的 printDescription 方法,并打印各自独特的信息,调用输出值:

public static void main(String[] args) {    myBicycle bike01,bike02,bike03;    bike01 = new myBicycle(20,10,1);    bike02 = new MountainBike(20, 10, 5, "Dual");    bike03 = new RoadBike(40, 20, 8, 23);    bike01.printDescription();    bike02.printDescription();    bike03.printDescription();}

输出结果为:

Bike is in gear 10 with a cadence of 20 and traveling at a speed of 1. Bike is in gear 10 with a cadence of 20 and traveling at a speed of 5. The MountainBike has a Dual suspension.Bike is in gear 8 with a cadence of 40 and traveling at a speed of 20. The RoadBike has 23 MM tires.

Java 虚拟机为每个变量所引用的对象调用适当的方法,但它不会调用由变量的类型定义的方法。这种调用技术称为 虚拟方法 调用,显示 Java 语言中多态性的重要性。

2.6. 屏蔽字段

字段名与超类的字段名相同,就会屏蔽超类的字段,即使它们的类型不同。在子类的中。不能使用简单名引用超类的字段,必须使用 super 关键字访问。

使用 super 关键字

(1).访问父类的成员
如果一个方法覆盖了父类的方法,就要使用 super 关键字来调用被覆盖的方法。 super 关键字也可以被用于引用被屏蔽的字段。
(2).子类构造器
在上述例子 MountainBike 的构造器中,调用了父类的构造器,然后再添加自己特有的方法:

super(startCadence,startSpeed,startGear);//...
0 0
原创粉丝点击