详解 抽象类

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

抽象类的基本概念:

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

定义
在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));
	}

}

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

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

抽象类的成员特点

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

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

抽象类的成员方法特性:

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

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

posted @ 2020-03-05 08:58  在下右转,有何贵干  阅读(285)  评论(0编辑  收藏  举报