详解 抽象类
本人在这篇博文中要讲解的知识点,和本人之前的一篇博文有所关联。因为,“抽象类” 是按照 “自下而上” 的顺序来编写所需的类,而在本人之前的博文《详解 继承(上)—— 工具的抽象与分层》中讲到的 继承 则与之相反,按照 “自上而下” 的顺序来编写所需类,那么,话不多说,现在本人就开始讲解这篇博文的主题
抽象类的基本概念:
首先,本人来介绍下什么是抽象类:
定义:
在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());
}
}
我们现在来看看运行结果:
现在,本人来总结下抽象类的注意点:
注意点:
- 只要含抽象方法,就是抽象类,就要加上“abstract”修饰符;
- 抽象类不能存在对象,因为它存在未实现的方法;
- 抽象类中不能存在private类成员及方法,所有抽象方法不能用static修饰;
- 抽象类派生的所有非抽象类,那么,
派生出来的非抽象类必须实现这个抽象类中的所有抽象方法;
若未实现,则子类也为抽象类,必须加上“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));
}
}
下面本人来展示下运行结果:
由以上代码运行结果的展示,我们大致能够看出:
抽象类的成员具有如下特点:
抽象类的成员特点:
- 成员变量:
既可以是变量,也可以是常量。- 构造方法:
必须有,因为 要用于子类访问父类数据的初始化。- 成员方法:
既可以是抽象的,也可以是非抽象的。
抽象类的成员方法具有如下特性:
抽象类的成员方法特性:
- 抽象方法
强制要求子类做的事情- 非抽象方法
子类继承的事情,提高代码复用性
抽象类和继承的逻辑顺序是相反的。
并且,抽象类 与 本人下一篇博文—— 《内部类 与 匿名内部类》 还有很深的关系,能够看出,这个知识点还是蛮重要的,所以,希望同学们能够加以重视。