JAVA 抽象类和抽象方法

概述

父类中的方法,被子类们重写,子类各自的实现都不尽相同。那么父类的方法 声明 即可,不需要实现 方法体

把 没有方法体 的方法称为 抽象方法

Java规定,包含 抽象方法 的类就是 抽象类。

需要用到 abstract 关键字

抽象方法

使用 abstract 关键字修饰方法,只有 方法的声明 ,没有实现 方法体

格式

修饰符 abstract 返回值类型 方法名 (参数列表);

例子:

public abstract void run();

注意

  • abstract 只能修饰 普通方法,此时方法 不能实现方法体
  • abstract 不能修饰 static 方法
  • abstract 不能修饰 构造方法

抽象类

使用 abstract 关键字修饰的类,就是抽象类
抽象类可以有抽象方法

格式

abstract class 类名{
    成员变量
    构造方法(){

    }
    方法(){//普通方法
        方法体
    } 
    abstract 方法(); //抽象方法
}

例子:

形状就是一个抽象的概念。不同的子类计算形状面积的方法是不一样的。可提供抽象方法来被不同的子类所实现。

abstract public class Shape{
    abstract double area()
}

抽象类的意义

  1. 为其子类提供一个公共的类型(父类引用指向子类对象)

  2. 封装子类中的重复内容(成员变量和方法)

  3. 将父类设计成抽象类后,既可借由父子继承关系限制子类的设计随意性,在一定程度上避免了无意义父类的实例化。

注意

  1. 抽象类 不能创建对象
    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

  3. 抽象类 可以 没有抽象方法,但是有抽象方法的类必定是抽象类。
    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设
    计。

  4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。
    理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

抽象类的成员

  • 成员变量:既可以是变量,也可以是常量。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和final
      被abstract修饰的方法强制子类重写
      被final修饰的不让子类重写,所以他俩是矛盾
      -abstract和private
      被abstract修饰的是为了让子类看到并强制重写
    • 被private修饰不让子类访问,所以他俩是矛盾的
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方法的类,