• 5.5 抽象类


    一、为什么要使用抽象类、抽象方法

      当编写一个类时,常常会定义一些类用于描述该类的行为方式,那么这些方法都有具体的方法体。但在某些情况下,某个父类只知道其子类应该包含什么样的方法,但无法准确知道这些子类的如何实现这些方法。

      例如:定义了一个Shape类,这个类应该提供一个计算周长的方法calPerimeter(),但不同的Shape子类对计算周长的方法是不一样的,即Shape类无法准确知道不同的子类计算周长的方法。但是如果不在Shape类中声明calPerimeter()方法,我们定义了Shape类型的三角形和矩形实例(都是从Shape类的子类派生出来的,且在子类中有定义calPerimeter()方法),这时我们就无法调用calPerimeter()方法,因为Shape类的实例不含calPerimeter()方法。如果必须调用calPerimeter()方法,我们就必须将两个实例强制转换为三角形和矩形类型。

      如何既让Shape类里包含calPerimeter()方法,有无须提供其方法的实现呢?使用抽象类即可满足该要求:抽象方法只有方法签名,没有实现的方法。

    二、抽象方法和抽象类

    抽象类和抽象方法必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。

    抽象方法和抽象类的规则:

    1、都必须必须由abstract修饰,抽象方法不能有方法体。

    2、抽象类不能被实例化。即使抽象类里不包含抽象方法,这个抽象方法也不能创建实例。

    3、抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类主要用于派生子类。

    4、含有抽象方法的类只能被定义成抽象类。

    抽象类可以用“有得有失”来形容,得——抽象类获得了抽象方法;失——抽象类不能创建实例,只能派生子类。

     1 abstract class Shape 
     2 {
     3     {System.out.println("执行Shape的初始化块");}
     4     private String color;
     5     //定义一个计算周长的抽象方法
     6     public abstract double calPerimeter();
     7     //定义一个返回形状的方法
     8     public abstract String getType();
     9     //定义Shape构造器,该构造器并不是用于创建Shape对象的
    10     //而是用于被子类调用
    11     public Shape(){}
    12     public Shape(String color)
    13     {
    14         System.out.println("执行Shape的构造器...");
    15         this.color=color;
    16     }
    17     
    18     //color的setter()和getter()方法
    19     public void setColor(String color)
    20     {
    21         this.color=color;
    22     }
    23     public String getColor()
    24     {
    25         return this.color;
    26     }
    27 }
    View Code

      上面的Shape类里的包含了两个初始化方法:calPerimeter()和getType(),所以这个类只能被定义成抽象类。Shape类既包含初始化块,也包含构造器,这些都不是在创建Shape对象时调用,而是在创建其子类的实例时被调用。

      下面定义一个三角形类,三角形被定义成普通类,因此必须实现Shape类里的所有方法。该类可以创建实例

     1 public class Triangle extends Shape 
     2 {
     3     //定义三角形的三边
     4     private double a;
     5     private double b;
     6     private double c;
     7     public Triangle(String color,double a,double b,double c)
     8     {
     9         super(color);
    10         this.setSides(a,b,c);
    11     }
    12 
    13     public void setSides(double a,double b,double c)
    14     {
    15         if(a>=b+c||b>=a+c||c>a+b)
    16         {
    17             System.out.println("三角形两边之和必须大于第三边");
    18             return;
    19         }
    20         else
    21         {
    22             this.a=a;
    23             this.b=b;
    24             this.c=c;
    25         }
    26     }
    27 
    28     //重写父类的计算周长的抽象方法
    29     @Override
    30     public double calPerimeter()
    31     {
    32         return a+b+c;
    33     }
    34     //重写父类中的返回形状的抽象方法
    35     public String getType()
    36     {
    37         return "三角形";
    38     }
    39 }
    View Code

       下面再定义一个Circle普通类,Circle类也是Shape类的一个子类

     1 class Circle extends Shape 
     2 {
     3     private double radius;
     4     public Circle(String color,double radius)
     5     {
     6         super(color);
     7         this.radius=radius;
     8     }
     9 
    10     public void setRadius(double radius)
    11     {
    12         this.radius=radius;
    13     }
    14     //重写Shape类计算周长的抽象方法
    15     @Override
    16     public double calPerimeter()
    17     {
    18         return 2*Math.PI*radius;
    19     }
    20     @Override
    21     //重写父类的返回形状的方法
    22     public String getType()
    23     {
    24         return getColor()+"圆形";
    25     }
    26 }
    View Code

    测试上面的类:

     1 class ShapeTest 
     2 {
     3     public static void main(String[] args) 
     4     {
     5         //我们不能创建Shape的实例
     6         //Shape s=new Shape("黄色");//ShapeTest.java:6: 错误: Shape是抽象的; 无法实例化
     7         Shape s1=new Triangle("黑色",3,4,5);
     8         Shape s2=new Circle("蓝色",3);
     9         System.out.println(s1.getType());//三角形
    10         System.out.println(s1.calPerimeter());//12.0
    11 
    12         System.out.println(s2.getType());//矩形
    13         System.out.println(s2.calPerimeter());//18.84955592153876
    14     }
    15 }
    16 ---------- 运行Java捕获输出窗 ----------
    17 执行Shape的初始化块
    18 执行Shape的构造器...
    19 执行Shape的初始化块
    20 执行Shape的构造器...
    21 三角形
    22 12.0
    23 蓝色圆形
    24 18.84955592153876
    25 
    26 输出完成 (耗时 0 秒) - 正常终止
    View Code

    上面的main()方法定了两个Shape类型的引用变量,分别指向Triangle对象和Circle对象。由于在Shape类中定义了calPerimeter()方法和getType()方法,所以程序可以直接调用s1变量和s2变量的calPerimeter()和getType()方法,无需强制转换为其子类类型。

      利用抽象类和抽象方法的优势,可以很好地发挥多态的优势。当使用abstract修饰类时,表面这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须有子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用。 

     注意:

    (1)abstract只能修饰类、方法,不能修饰成员变量、构造器、初始化块。

    (2)static修饰一个方法时,表明这个方法属于类本身,即通过该类就可以调用该类本身。但如果该方法被定义成抽象方法,则将导致通过类来调用方法时出现错误(调用一个没有方法体的方法肯定会引起错误)。因此static和abstract不能同时修饰一个方法,即没有所谓的类抽象方法。

    (3)static和abstract并不是绝对互斥的,static虽然不能同时修饰某个方法,但他们可以同时修饰内部类。

    (4)abstract关键字修饰的方法必须被子类重写才有意义,否则该方法将永运不会有方法体。因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。

     三、抽象类的作用

      抽象类不能创建实例,只能当成父类来继承。从语法角度来讲,抽象类是从多个具体的类中抽象出来的父类,它具有更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。

      抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。

      如果编写一个抽象父类,父类提供多个子类的通用方法,并把一个或多个方法留给其子类来实现,这就是一种模板模式。

  • 相关阅读:
    C# list去重合并查找
    C# 循环依赖,分片,聚类
    C# 大于等于转换数学区间
    python 声明类必须要用def __init__(self):
    NPM install ERR
    简单的刷题总结
    Python学习笔记:Pandas数据转换编码的10种方式
    Python学习笔记:natsort实现自然排序
    java debug 在服务器上打断点 何苦
    linux 目录结构 何苦
  • 原文地址:https://www.cnblogs.com/weststar/p/12411123.html
Copyright © 2020-2023  润新知