概述
父类中的方法,被子类们重写,子类各自的实现都不尽相同。那么父类的方法 声明 即可,不需要实现 方法体
把 没有方法体 的方法称为 抽象方法
Java规定,包含 抽象方法 的类就是 抽象类。
需要用到 abstract 关键字
抽象方法
使用 abstract 关键字修饰方法,只有 方法的声明 ,没有实现 方法体
格式
修饰符 abstract 返回值类型 方法名 (参数列表);
例子:
public abstract void run();
注意
abstract只能修饰 普通方法,此时方法 不能实现方法体abstract不能修饰static方法abstract不能修饰 构造方法
抽象类
使用 abstract 关键字修饰的类,就是抽象类
抽象类可以有抽象方法
格式
abstract class 类名{
成员变量
构造方法(){
}
方法(){//普通方法
方法体
}
abstract 方法(); //抽象方法
}
例子:
形状就是一个抽象的概念。不同的子类计算形状面积的方法是不一样的。可提供抽象方法来被不同的子类所实现。
abstract public class Shape{
abstract double area()
}
抽象类的意义
为其子类提供一个公共的类型(父类引用指向子类对象)
封装子类中的重复内容(成员变量和方法)
将父类设计成抽象类后,既可借由父子继承关系限制子类的设计随意性,在一定程度上避免了无意义父类的实例化。
注意
抽象类 不能创建对象
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类 可以 没有抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设
计。抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
抽象类的成员
成员变量:既可以是变量,也可以是常量。abstract不能修饰成员变量
构造方法:有。
用于子类访问父类数据的初始化。成员方法:既可以是抽象的,也可以是非抽象的。
- 抽象方法:强制要求子类做的事情。
- 非抽象方法:子类继承,提高代码复用性。
案例
图形面积
不同几何图形的 面积计算公式是不同的,但是它们具有的 特性是相同的,都具有 长 和 宽 这两个属性,也都具有面积计算的方法。
那么可以定义一个抽象类,在该抽象类中含有两个属性(width 和 height)和一个抽象方法 area(),具体步骤如下。
1)首先创建一个表示图形的抽象类 Shape,代码如下所示。
public abstract class Shape {
public int width; // 几何图形的长
public int height; // 几何图形的宽
public Shape(int width, int height) {
this.width = width;
this.height = height;
}
public abstract double area(); // 定义抽象方法,计算面积
}
2)定义一个正方形类,该类继承自形状类 Shape,并重写了 area() 抽象方法。正方形类的代码如下:
public class Square extends Shape {
public Square(int width, int height) {
super(width, height);
}
// 重写父类中的抽象方法,实现计算正方形面积的功能
@Override
public double area() {
return width * height;
}
}
3)定义一个三角形类,该类与正方形类一样,需要继承形状类 Shape,并重写父类中的抽象方法 area()。三角形类的代码实现如下:
public class Triangle extends Shape {
public Triangle(int width, int height) {
super(width, height);
}
// 重写父类中的抽象方法,实现计算三角形面积的功能
@Override
public double area() {
return 0.5 * width * height;
}
}
4)最后创建一个测试类,分别创建正方形类和三角形类的对象,并调用各类中的 area() 方法,打印出不同形状的几何图形的面积。测试类的代码如下:
public class ShapeTest {
public static void main(String[] args) {
Square square = new Square(5, 4); // 创建正方形类对象
System.out.println("正方形的面积为:" + square.area());
Triangle triangle = new Triangle(2, 5); // 创建三角形类对象
System.out.println("三角形的面积为:" + triangle.area());
}
}
在该程序中,创建了 4 个类,分别为图形类 Shape、正方形类 Square、三角形类 Triangle 和测试类 ShapeTest。其中图形类 Shape 是一个抽象类,创建了两个属性,分别为图形的长度和宽度,并通过构造方法 Shape() 给这两个属性赋值。
在 Shape 类的最后定义了一个抽象方法 area(),用来计算图形的面积。在这里,Shape 类只是定义了计算图形面积的方法,而对于如何计算并没有任何限制。也可以这样理解,抽象类 Shape 仅定义了子类的一般形式。
正方形类 Square 继承抽象类 Shape,并实现了抽象方法 area()。三角形类 Triangle 的实现和正方形类相同,这里不再介绍。
在测试类 ShapeTest 的 main() 方法中,首先创建了正方形类和三角形类的实例化对象 square 和 triangle,然后分别调用 area() 方法实现了面积的计算功能。
5)运行该程序,输出的结果如下:
正方形的面积为:20.0
三角形的面积为:5.0
猫狗案例
* 具体事物:猫,狗
* 共性:姓名,年龄,吃饭
* 猫的特性:抓老鼠
* 狗的特性:看家
abstract class Animal {
private String name; //姓名
private int age; //年龄
public Animal(){} //空参
public Animal(String name,int age) {//有参
this.name = name;
this.age = age;
}
public abstract void eat(); //吃饭
}
class Cat extends Animal {
public Cat(){} //空参
public Cat(String name,int age) {//有参
super(name,age);
}
public void eat() {
System.out.println("猫吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public Dog(){} //空参
public Dog(String name,int age) {//有参
super(name,age);
}
public void eat() {
System.out.println("狗吃肉");
}
public void lookHome() {
System.out.println("看家");
}
}
老师案例
- 具体事物:基础班老师,就业班老师
- 共性:姓名,年龄,讲课。
- 具体事物:基础班学生,就业班学生
- 共性:姓名,年龄,学习
abstract class Teacher {
private String name; //姓名
private int age; //年龄
public Teacher(){} //空参
public Teacher(String name,int age) {//有参
this.name = name;
this.age = age;
}
setter / getter
public abstract void teach();
}
class BaseTeacher extends Teacher {
public BaseTeacher(){} //空参
public BaseTeacher(String name,int age) {//有参
super(name,age);
}
public void teach() {
System.out.println("我的姓名是:" + this.getName() + ",我的年龄是:" + this.getAge() + ",讲的内容是java基础");
}
}
员工案例
- 假如我们在开发一个系统时需要对程序员类进行设计,程序员包含3个属性:姓名、工号以及工资。
- 经理,除了含有程序员的属性外,另为还有一个奖金属性。
- 请使用继承的思想设计出程序员类和经理类。要求类中提供必要的方法进行属性访问。
abstract class Employee {
private String name; //姓名
private String id; //工号
private double salary; //工资
public Employee() {} //空参构造
public Employee(String name,String id,double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
setter / getter
public abstract void work();
}
//程序员
class Coder extends Employee {
public Coder() {} //空参构造
public Coder(String name,String id,double salary) {
super(name,id,salary);
}
public void work() {
System.out.println("我的姓名是:" + this.getName() + ",我的工号是:" + this.getId() + ",我的工资是:"
+ this.getSalary() + ",我的工作内容是敲代码");
}
}
//项目经理
class Manager extends Employee {
private int bonus; //奖金
public Manager() {} //空参构造
public Manager(String name,String id,double salary,int bonus) {
super(name,id,salary);
this.bonus = bonus;
}
public void work() {
System.out.println("我的姓名是:" + this.getName() + ",我的工号是:" + this.getId() + ",我的工资是:"
+ this.getSalary() + ",我的奖金是:" + bonus + ",我的工作内容是管理");
}
}
面试题
A:面试题1
- 一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
- 可以
- 这么做目的只有一个,就是不让其他类创建本类对象,交给子类完成
B:面试题2
- abstract不能和哪些关键字共存
- abstract和static
被abstract修饰的方法没有方法体
被static修饰的可以用类名.调用,但是类名.调用抽象方法是没有意义的
- abstract和static
- abstract和final
被abstract修饰的方法强制子类重写
被final修饰的不让子类重写,所以他俩是矛盾
-abstract和private
被abstract修饰的是为了让子类看到并强制重写 - 被private修饰不让子类访问,所以他俩是矛盾的
- abstract不能和哪些关键字共存
abstract class Demo {
//public static abstract void print(); //错误: 非法的修饰符组合: abstract和static
//public final abstract void print(); //错误: 非法的修饰符组合: abstract和final
private abstract void print(); //错误: 非法的修饰符组合: abstract和private
}
课堂练习
多选题
下面代码怎么修改可以编译时不报错() (选择两项)
//父类
public abstract class Bike{
int colornum;
int brand;
int speed;
public abstract void speedup();
}
//子类
public class SpeedBike extends Bike{ //位置1
public void speedup() ; //位置2
}
A 在位置一处将SpeedBike类设为抽象类,同时将位置2处的speedup也设为抽象方法
B 将位置一中的public改为final
c 将位置二中speedup()方法改为抽象方法
d 将位置二中speedup()方法中加入方法的实现
1. 单选题
下列选项中,关于Java的抽象类和抽象方法说法不正确的是()。(选择一项)
A 抽象类和抽象方法都通过abstract关键字来修饰
B 抽象类中必须包含抽象方法
C 抽象方法只有方法声明,没有方法实现
D 子类如果不重写父类所有的抽象方法,则必须设置为抽象类
//抽象类Shape,里面含抽象方法area( )
class Shape{
}
public class Rectangle extends Shape {
//属性:矩形的长lenghth、宽wide
//创建带参构造方法以及无参构造方法
//创建针对长、宽的赋值和取值方法
//重写父类的area()方法
}
//子类圆Circle类
public class Circle extends Shape {
//属性:圆的半径r
//创建带参构造方法以及无参构造方法
//创建针对半径的赋值和取值方法
//重写父类中area()方法
}
//测试类
public class Test {
public static void main(String[] args) {
//创建类的实例,并分别对圆的半径和矩形的长宽进行赋值
//调用area()方法,输出结果
}
}
课后作业
创建一个Vehicle类并将它声明为抽象类。
在Vehicle类中声明一个NoOfWheels方法,
使它返回一个字符串值。
创建两个类Car和Motorbike从Vehicle类继承,并在这两个类中实现NoOfWheels方法。
在Car类中,应当显示“四轮车”信息;
而在Motorbike类中,应当显示“双轮车”信息。创建另一个带main方法的类,