详解 抽象类

馋奶兔 提交于 2020-03-05 09:38:04

本人在这篇博文中要讲解的知识点,和本人之前的一篇博文有所关联。因为,“抽象类” 是按照 “自下而上” 的顺序来编写所需的类,而在本人之前的博文《详解 继承(上)—— 工具的抽象与分层》中讲到的 继承 则与之相反,按照 “自上而下” 的顺序来编写所需类,那么,话不多说,现在本人就开始讲解这篇博文的主题

抽象类的基本概念:

首先,本人来介绍下什么是抽象类

定义
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类

那么,抽象类具有如下特点

特点

  • 抽象类 和 抽象方法 必须用abstract关键字修饰
    抽象类格式: abstract class 类名 {}
    抽象方法格式: public abstract void eat();
  • 抽象类 不一定有抽象方法,有抽象方法的类 一定是抽象类
  • 抽象类中可以有构造方法,抽象类不能进行实例化
    问:那么要构造方法有什么作用呢?
    答曰:用于子类访问父类数据时的初始化
  • 抽象类不能直接实例化
    问:那么,抽象类如何实例化呢?
    答曰:按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
  • 抽象类的子类
    要么是抽象类
    要么重写抽象类中的所有抽象方法

本人来通过两个例子,来展示这个知识点的应用:

一、图形类:

假设现在要求编写几个类可以分别根据图形的属性求出图形的 周长 及 面积。

那么,若我们将几个图形的共性拿出来,做一个基类,之后我们再根据基类派生出各图形的方法。

首先,我们来编写 基本图形类(BaseShape类):
BaseShape.java:

package com.mec.about_abstruct;

public abstract class BaseShape {   //只要含抽象方法,就是抽象类,就要加上“abstract”修饰符
    protected String name;
    protected String type;
    
    protected BaseShape() {
    }
    
    protected BaseShape(String name) {
        this.name = name;
    }
    
    //这两行代码,就是“抽象方法”,不在本类中实现,是专门用来给子类实现的
    public abstract double area();  
    public abstract double rounder();
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return name + "的" + type;
    } 
    
}

现在本人来通过这个类编写一个 确定图形类 ——“Circle类”:

Circle.java:

package com.mec.about_abstruct;

public class Circle extends BaseShape {
    private double radius;
    
    public Circle() {
    }
    
    public Circle(String name) {
        super(name);
        this.type = "圆";
    }
    
    public Circle(String name, double radius) {
        this(name);
        this.radius = radius;
    }

    @Override
    public double area() {
        return  Math.PI * radius * radius;
    }

    @Override
    public double rounder() {
        return 2 * Math.PI * radius;
    }

}

现在同学们可能就对抽象类有了进一步了解:
抽象类:将类似的东西的共同部分(toString() 及 name 和 type)实现,将不同的、但必须要完成的(area()、rounder() 及 各图形特有尺度)表现出来,但不实现。

现在本人来对 Test类 进行一些改动:
Test类:

package com.mec.about_abstruct.test;

import com.mec.about_abstruct.BaseShape;
import com.mec.about_abstruct.Circle;

public class Test {

    public static void main(String[] args) {
        circleOne = new Circle("地球", 6371000.0);
        System.out.println(circleOne.toString() + "周长= " + circleOne.rounder());
        System.out.println(circleOne.toString() + "面积= " + circleOne.area());
    }

}

我们现在来看看运行结果:
在这里插入图片描述现在,本人来总结下抽象类的注意点

注意点:

  1. 只要含抽象方法,就是抽象类,就要加上“abstract”修饰符;
  2. 抽象类不能存在对象,因为它存在未实现的方法;
  3. 抽象类中不能存在private类成员及方法,所有抽象方法不能用static修饰;
  4. 抽象类派生的所有非抽象类,那么,
    派生出来的非抽象类必须实现这个抽象类中的所有抽象方法;
    若未实现,则子类也为抽象类,必须加上“abstract”修饰符;

我们能够发现,无论是 “继承” 还是 “抽象类” ,都是为了 “代码复用”。而本人在本专栏第一篇博文中就讲过,Java最主要的特点就是“面向对象”,也就是“面向工具”,我们现在来实现下一个有趣的问题——计算房价:

我们只需改动一下上面的代码:

BaseShape.java:

package com.mec.about_abstruct.shape;

public abstract class BaseShape {   //只要含抽象方法,就是抽象类,就要加上“abstract”修饰符
    protected String name;
    protected String type;
    //据了解:深圳近3年房价一平最低100
    protected double price = 100;
    
    protected BaseShape() {
    }
    
    protected BaseShape(String name) {
        this.name = name;
    }
    
    public double getPrice() {
        //所有的抽象方法,都可以在本类中被非静态方法调用
        return price * area();
    }
    
    //这两行代码,就是“抽象方法”,不在本类中实现,是专门用来给子类实现的
    public abstract double area();  
    public abstract double rounder();
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return name;
    } 
    
}

因为一般租的房子是投影式长方形的,所以本人现在来编写一个 长方形类:
Rectangle.java:

package com.mec.about_abstruct.shape;

public class Rectangle extends BaseShape {
    private double width;
    private double height;
    
    public Rectangle() {
    }

    public Rectangle(String name) {
        super(name);
        this.type = "长方形";
    }
    
    public Rectangle(String name,double width, double height) {
        this(name);
        this.height = height;
        this.width = width;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
    
    @Override
    public double area() {
        return  width * height;
    }

    @Override
    public double rounder() {
        return 2 * (width + height);
    }
    
}

Test.java:

package com.mec.about_abstruct.test;

import com.mec.about_abstruct.shape.BaseShape;
import com.mec.about_abstruct.shape.Rectangle;

public class Test {

    public static void main(String[] args) {
        BaseShape rectangleOne = new Rectangle("深圳一间小房间", 6, 6);
        System.out.println(rectangleOne.toString() + "周长= " + rectangleOne.rounder());
        System.out.println(rectangleOne.toString() + "面积= " + rectangleOne.area());
        System.out.println(rectangleOne.toString() + "房价/月 约= " + rectangleOne.getPrice( ));
    }

}

在这里插入图片描述

二、货币类:

因为本人在上文中已经将“抽象类”的基本知识点都已讲解完毕,那么,在这个例子中,本人就不会再做过多解释,以展示使用方法为目的,为同学们加深这个知识点的印象。
Currency类(抽象类):

package com.mec.about_abstruct.currency;

public abstract class Currency {
    protected String goodsName;
    protected double weight;
    
    //以下是人民币分别对应其他货币的汇率
    public static double RMB = 1.0;
    public static double DOLLAR = 0.1459;
    public static double EURO = 0.1250;
    public static double POUND = 0.1110;
    public static double YEN = 16.3797;
    public static double WON = 163.8141;
    
    public Currency(String goodsName, double weight) {
        this.goodsName = goodsName;
        this.weight = weight;
    }

    public double getWeight() {
        return weight;
    }

    public abstract double price();
    
    public void setWeight(double weight) {
        this.weight = weight;
    }
    
    public double value(double currencyType) {
        return price() * currencyType;
    }
    
    //这个方法调用了抽象方法,并将其返回值作为货币转换的基础数据
    public String getValueString(double currencyType) {
        return weight + "克" + goodsName + "价格为:"
                + value(currencyType)
                + currencyName(currencyType);
    }
    
    public String  currencyName(double currencyType) {
        if(Math.abs(currencyType - DOLLAR) < 1e-6) {
            return "美元";
        }
        if(Math.abs(currencyType - EURO) < 1e-6) {
            return "欧元";
        }
        if(Math.abs(currencyType - YEN) < 1e-6) {
            return "日元";
        }
        if(Math.abs(currencyType - WON) < 1e-6) {
            return "韩元";
        }
        if(Math.abs(currencyType - POUND) < 1e-6) {
            return "英镑";
        }
        
        return "人民币";
    }
    
}

Silver 类:

package com.mec.about_abstruct.currency;

public class Silver extends Currency{

    public Silver(double weight) {
        super("白银", weight);
    }

    @Override
    public double price() {
        return 3.12 * weight;
    }

}

Gold 类:

package com.mec.about_abstruct.currency;

public class Gold extends Currency {
    
    public Gold(double weight) {
        super("黄金", weight);
    }

    @Override
    public double price() {
        return 264.5 * weight;
    }

}

测试类(Demo 类):

package com.mec.about_abstruct.currency;

public class Demo {

    public static void main(String[] args) {
        Gold gold = new Gold(1.47);
        System.out.println(gold.getValueString(Currency.RMB));
        System.out.println(gold.getValueString(Currency.DOLLAR));
        System.out.println(gold.getValueString(Currency.POUND));
        
        System.out.println("下面演示白银兑换率:");
        Silver silver = new Silver(132.0);
        System.out.println(silver.getValueString(Currency.EURO));
        System.out.println(silver.getValueString(Currency.WON));
        System.out.println(silver.getValueString(Currency.YEN));
    }

}

下面本人来展示下运行结果:
在这里插入图片描述

由以上代码运行结果的展示,我们大致能够看出:
抽象类的成员具有如下特点

抽象类的成员特点

  • 成员变量:
    既可以是变量,也可以是常量。
  • 构造方法:
    必须有,因为 要用于子类访问父类数据的初始化
  • 成员方法:
    既可以是抽象的,也可以是非抽象的。

抽象类的成员方法具有如下特性

抽象类的成员方法特性:

  • 抽象方法
    强制要求子类做的事情
  • 非抽象方法
    子类继承的事情,提高代码复用性

抽象类和继承的逻辑顺序是相反的。
并且,抽象类 与 本人下一篇博文—— 《内部类 与 匿名内部类》 还有很深的关系,能够看出,这个知识点还是蛮重要的,所以,希望同学们能够加以重视。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!