`

012 抽象类与方法和接口

 
阅读更多

第八讲

 

抽象类&抽象方法&接口

 

抽象类(abstract class):使用了abstract关键字所修饰的类叫做抽象类。抽象类无法实例化,也就是说,不能new出一个抽象类的对象(实例化)。

注:相对于抽象类,之前学习的是具体类(Concrete class)。

 

抽象方法(abstract method):使用abstract关键字所修饰的方法叫做抽象方法。抽象方法需要定义在抽象类中。相对于抽象方法(有声明,无实现),之前所定义的方法叫做具体方法(有声明,有实现)。

 

抽象类&抽象方法

1)如果一个类包含了抽象方法,那么这个类一定是抽象类。

2)如果某个类是抽象类,那么该类可以包含具体方法(有声明、有实现)。

3)如果一个类中包含了抽象方法,那么这个类一定要声明成abstract class,也就是说,该类一定是抽象类;反之,如果某个类是抽象类,那么该类既可以包含抽象方法,也可以包含具体方法,甚至可以全部是具体方法。

4)无论何种情况,只要一个类是抽象类,那么这个类就无法实例化。

 

抽象类的继承 在子类继承父类(父类是个抽象类)的情况下,那么该子类必须要实现父类中所定义的所有抽象方法;否则,该子类需要声明成一个abstract class。

 

 

抽象方法的意义:

 

有些类的方法在定义时是无法直接实现的,因此要推迟到某个子类中实现。

[Class011/ShapeArea.java]

 

 

接口(interface):

 

1.接口的地位等同于class,接口中的所有方法都是抽象方法。在声明接口中的方法时,可以使用abstract关键字,也可以不使用。通常情况下,都会省略掉abstract关键字。

 

2.可以将接口看作是特殊的抽象类(抽象类中可以有具体方法,也可以有抽象方法,而接口中只能有抽象方法,不能有具体方法)。

[Class011/InterfaceTest.java]

3.类可以实现接口。实现使用关键字implements表示,代表了某个类实现了某个接口。

一个类(不是抽象类)实现了某个接口,那么该类必须要实现接口中声明的所有方法。如果该类是个抽象类,那么就无需实现接口中的方法了。

[Class011/ImplementsTest]

4.Java是单继承的,也就是说某个类只能有唯一一个父类;一个类可以实现多个接口,多个接口之间使用逗号分隔。所以非抽象类在实现接口时要实现接口里所有的方法。

[Class011/ImplementsTest]

 

5. 多态新解:所谓多态,就是父类型的引用可以指向子类型的对象,或者接口类型的引用可以指向实现该接口的类的实例。关于接口与实现接口的类之间的强制类型转换方式与父类和子类之间的强制类型转换方式完全一样。

[Class011/NewPolymorphism.java]

 

 

接口中所声明的方法都是抽象方法。接口中的方法都是public的。

1.      接口中也可以定义成员变量。接口中的成员变量都是public final static 的。

[Class011/InterfaceTest.java]

 

分析下面程序:

interface Service

{

    String serviceType;

}

此程序编译错误

因为接口中的成员变量都是publicfinal static 的,声明是必须赋初值。当然,一般接口里面是不定义属性的,更多是定义方法。

 

3.一个类不能既是final ,又是abstract 的。因为 abstract 主要目的是定义一种约定,让子类去实现这种约定,而final 表示此类不能被继承,这样abstract 希望该类可以被继承而final明确说明给类不能被继承,两者矛盾。因此一个类不能既是final的,又是abstract 的。

[Class011/AbstractExample.java]

 

 

 

在我们编写一个对象的时候,我们可能会对一些类的方法进行定义,但是并不具体实现。而是将这些方法的实现放到它的子类中去。这样可以增强类设计的灵活性。

 

比如,我们定义了一个表示各种图形的类Shape,这个类有一些属性,还有一个用于计算这个图形的周长的方法calPerimeter(),但是,对于不同的图形,对周长的计算方法也不同,我们不能将所有的图形的周长的计算方法都写到这个方法中。

 

通过关键字abstract,我们可以在父类Shape中不实现这个方法,而将它的实现放到子类中去。比如,一个圆形的类继承了Shape这个父类,然后,在这个子类中实现calPerimeter()这个方法的细节:圆形的周长=2ПR,其中, П表示圆周率,而R表示圆的半径。如果是一个矩形继承了这个类,则可以在这个子类中实现这个方法calPerimeter(),它的计算方式是2*(Width+Heigt)。


从上面的讨论中可以看出来,我们有时候只是定义某个类的一个“骨架”,并不具体实现,而将进一步的具体实现放到子类中去完成。这个时候,我们可以将这个类定义为abstract的,而将没有实现的方法声明为abstract的。因为abstract类中具有没有被实现的方法,所以,抽象类不能被实例化。

 

这样做,比起在类里面留一个未实现的空方法来说,要安全的得多,会防止其他人误用,导致难以发现问题的bug。使用抽象类,会让使用该类的人明确知道,需要由自己来根据实际情况来实现这个类中的部分方法。

 

抽象类里面并非一定需要抽象方法,当你定义了一个类,但是又不想让它被直接实例化的时候,可以使用抽象类的方法来实现。反之,如果类中有抽象方法,则一定要将类定义为抽象的类。


在以下任一条件成立时,类必须定义成抽象类:
类中有至少一个抽象方法;
类继承了父类中的抽象方法,但是至少有一个抽象方法没有实现;
类实现了某个接口,但没有全部实现接口中的方法。

 

下面我们来看一个抽象类的例子。

 

首先我们定义了一个用来表示各种图形的类Shapes,这个类有两个抽象方法,一个用于返回图形的形状,一个用于返回图形的周长:

public abstract class Shapes {
 private String color;

 /**
  * 得出周长
  */
 public abstract double perimeter();

 /**
  * 得到形状
  */
 public abstract String getType();

 // 用于设置/获取“颜色”属性的方法
 public String getColor() {
  return color;
 }

 public void setColor(String theColor) {
  color = theColor;
 }
}


注意,这是一个抽象类,不能直接实例化它。


 

public class ShapeTriangle extends Shapes {
 protected double a, b, c;

 public ShapeTriangle() {
  setSides(0.0, 0.0, 0.0);
 }

 public ShapeTriangle(double i, double j, double h) {
  setSides(i, j, h);
 }

 public void setSides(double x, double y, double z) {
  this.a = x;
  this.b = y;
  this.c = z;
 }

 /**
  * 实现父类中的抽象方法
  */
 public double perimeter() {
  return a + b + c;
 }

 public String getType() {
  return "三角形";
 }

 public static void main(String args[]) {
  ShapeTriangle st = new ShapeTriangle(3.0, 4.0, 5.0);
  System.out.println("形状:" + st.getType());
  System.out.println("周长:" + st.perimeter());
 }
}


这是一个表示三角形的类,这个类继承了父类Shapes,然后,在父类的基础上新增了一些属性以及相应的方法。

 

根据三角形的实际情况,实现了父类中的两个抽象方法:getType()返回一个“三角形”的字符串,而且根据三角形周长的算法实现了父类中的计算周长的方法:perimeter。

 

而对于圆形,可能需要这样来实现它的两个抽象方法:

public class ShapeCircle extends Shapes {
 private double r;

 public ShapeCircle() {
  setRadius(0.0);
 }

 public ShapeCircle(double ra) {
  setRadius(ra);
 }

 public void setRadius(double rad) {
  this.r = rad;
 }

 /**
  * 实现父类的抽象方法
  */
 public double perimeter() {
  return 2.0 * Math.PI * r;
 }

 public String getType() {
  return "圆";
 }

 public static void main(String args[]) {
  ShapeCircle sc = new ShapeCircle(5);
  System.out.println("形状:" + sc.getType());
  System.out.println("周长:" + sc.perimeter());
 }
}


在这个类ShapeCircle中,也继承了类Shapes,但它对抽象方法perimeter()的实现方式是和ShapeTriangle不一样的。
注意:
  区分没有实现的方法和空方法体的方法的区别,下面这个方法是没有实现的方法:public int methodA();
  而下面这个方法是空方法体的方法:public int methodB(){}
  没有实现的方法可以用abstract来修饰,而空方法体方法却不能使用abstract来修饰,它已经实现了方法,只是在这个方法中什么动作也没有做而已

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics