AI 摘要
AI
正在生成摘要...
本文内容可能已过时或不再适用,请结合实际情况谨慎参考。

一、实验内容

实验目的:

掌握抽象类和接口

1. 掌握抽象类与接口的基本概念。

(1)抽象类:

  • 定义:使用 abstract 关键字 修饰的类,称为抽象类。
  • 抽象方法:使用 abstract 修饰、只有方法声明(无方法体,即无 {} 实现)的方法,称为抽象方法。包含抽象方法的类,必须定义为抽象类。
  • 核心特点:
    • 抽象类不能直接通过 new 实例化,只能通过子类继承使用。
    • 子类继承抽象类后,必须重写抽象类中所有的抽象方法;若子类也声明为抽象类,则可不用重写。
    • 抽象类中可包含普通成员变量、普通方法、构造方法、静态方法和抽象方法。
    • 抽象类遵循单继承原则,一个子类只能继承一个抽象类。

(2)接口:

  • 定义:使用 interface 关键字定义的类型,称为接口,本质是一种行为规范和统一标准。
  • 核心特点:
    • 接口是完全抽象的约束,仅定义方法规范,不提供具体实现。
    • 接口中默认规则:抽象方法默认修饰符为 public abstract,成员变量默认修饰符为 public static final(即常量)。
    • 接口没有构造方法,不能直接实例化。
    • 一个类可以实现多个接口(用 implements 关键字),弥补 Java 单继承的局限性。
    • 接口之间可以通过 extends 关键字实现多继承。

2. 运行案例 5-1, 案例 5-2 , 案例 5-3。

案例 5-1:利用面向对象程序设计的思想来实现两个数的加法和减法——以复数为例。

JAVA
// 定义两个数操作的接口
interface IOperator {
    // 数的加法
    void addition(Object o1, Object o2);
    // 数的减法
    void subtraction(Object o1, Object o2);
    // 打印结果
    void printResult(Object o);
}

// 复数类型:实部+虚部
class Complex {
    // 私有成员变量:封装实部和虚部
    private int real;
    private int imaginary;

    // getter/setter:提供私有变量的访问和修改入口
    public int getReal() {
        return real;
    }

    public void setReal(int real) {
        this.real = real;
    }

    public int getImaginary() {
        return imaginary;
    }

    public void setImaginary(int imaginary) {
        this.imaginary = imaginary;
    }
}

// 复数运算接口实现类
class OperatorComplexImpl implements IOperator {
    // 复数加法:强制类型转换 Object -> Complex
    public void addition(Object o1, Object o2) {
        // 向下转型:父类引用转为子类对象,必须确保传入的是Complex类型
        Complex c1 = (Complex) o1;
        Complex c2 = (Complex) o2;
        // 创建新对象存储运算结果
        Complex result = new Complex();
        // 实部相加
        result.setReal(c1.getReal() + c2.getReal());
        // 虚部相加
        result.setImaginary(c1.getImaginary() + c2.getImaginary());
        // 调用打印方法输出结果
        printResult(result);
    }

    // 复数减法
    public void subtraction(Object o1, Object o2) {
        Complex c1 = (Complex) o1;
        Complex c2 = (Complex) o2;
        Complex result = new Complex();
        // 实部相减
        result.setReal(c1.getReal() - c2.getReal());
        // 虚部相减
        result.setImaginary(c1.getImaginary() - c2.getImaginary());
        printResult(result);
    }

    // 格式化打印复数,处理虚部为负数的场景
    public void printResult(Object o) {
        Complex c = (Complex) o;
        int real = c.getReal();
        int imaginary = c.getImaginary();
        // 优化打印格式:避免出现 5+-1i 这种错误格式
        if (imaginary >= 0) {
            System.out.println(real + "+" + imaginary + "i");
        } else {
            System.out.println(real + "" + imaginary + "i");
        }
    }
}

public class ObjectDemo {
    public static void main(String[] args) {
        // 初始化第一个复数:2 + 3i
        Complex c1 = new Complex();
        c1.setReal(2);
        c1.setImaginary(3);

        // 初始化第二个复数:3 + 4i
        Complex c2 = new Complex();
        c2.setReal(3);
        c2.setImaginary(4);

        // 接口多态:父接口引用指向子类实现对象
        IOperator operator = new OperatorComplexImpl();
        // 执行加法运算
        operator.addition(c1, c2);
        // 执行减法运算
        operator.subtraction(c1, c2);
    }
}

运行结果:

JAVA
5+7i
-1-1i

图片

案例 5-2:饲养员给动物喂食物。体现 Java 中的面向对象思想、接口(抽象类)的用处。

JAVA
// 动物接口:定义吃的行为
interface Animal {
    // 抽象方法:动物吃食物
    void eat(Food food);
}

// 抽象食物类:所有食物的父类
abstract class Food {
    // 食物名称,protected 子类可直接访问
    protected String name;

    // 获取食物名称
    public String getName() {
        return name;
    }

    // 设置食物名称
    public void setName(String name) {
        this.name = name;
    }
}

// 鱼类:继承食物抽象类
class Fish extends Food {
    // 构造方法:初始化食物名称
    public Fish(String name) {
        this.name = name;
    }
}

// 骨头类:继承食物抽象类
class Bone extends Food {
    // 构造方法:初始化食物名称
    public Bone(String name) {
        this.name = name;
    }
}

// 猫类:实现动物接口
class Cat implements Animal {
    // 重写接口的吃方法
    public void eat(Food food) {
        System.out.println("小猫吃" + food.getName());
    }
}

// 狗类:实现动物接口
class Dog implements Animal {
    // 重写接口的吃方法
    public void eat(Food food) {
        System.out.println("小狗啃" + food.getName());
    }
}

// 饲养员类:负责喂食
class Feeder {
    // 喂食方法:多态参数,可接收所有Animal和Food子类对象
    public void feed(Animal animal, Food food) {
        animal.eat(food);
    }
}

// 测试主类
public class TestFeeder {
    public static void main(String[] args) {
        // 创建饲养员对象
        Feeder feeder = new Feeder();
        
        // 多态:父类引用指向子类对象(Animal -> Dog)
        Animal animal = new Dog();
        // 多态:父类引用指向子类对象(Food -> Bone)
        Food food = new Bone("肉骨头");
        // 饲养员喂狗
        feeder.feed(animal, food);

        // 多态:引用重新指向猫对象
        animal = new Cat();
        // 多态:引用重新指向鱼对象
        food = new Fish("鱼");
        // 饲养员喂猫
        feeder.feed(animal, food);
    }
}

运行结果:

JAVA
小狗啃肉骨头
小猫吃鱼

图片

案例 5-3:编写一个复数类 Complex。

该类有属性 realpart 和 imaginpart,分别表示实部和虚部。该类还有修改设置(set)和读取(get)属性 realpart() 和 imaginpart() 的方法。编写一个抽象类 Compute。该类有属性 a 和 b,它们的数据类型为 Complex,代表参加运算的 2 个复数。该类还提供一个子类进行计算的抽象方法 abstract void solve()。编写一个 ComplexAdd 类,它继承自抽象类 Compute。该类有继承自父类的属性 Complex a,b 及方法 void solve() 计算两个复数相加并输出结果。编写一个 ComplexSub 类,它继承自抽象类 Complex。该类有继承自父类的属性 Complex a,b 及方法 void solve() 计算两个复数相减并输出结果。编写一个测试类 TestComplex,其包含 main() 方法。定义两个复数,实现复数的加减运算。

JAVA
import java.util.Scanner;

class Complex1 {
    // 私有成员变量:实部、虚部(规范命名,见名知意)
    private int realPart;
    private int imaginaryPart;

    // 获取实部
    public int getRealPart() {
        return realPart;
    }

    // 设置实部
    public void setRealPart(int realPart) {
        this.realPart = realPart;
    }

    // 获取虚部
    public int getImaginaryPart() {
        return imaginaryPart;
    }

    // 设置虚部
    public void setImaginaryPart(int imaginaryPart) {
        this.imaginaryPart = imaginaryPart;
    }

    // 复数加法:当前复数 + 传入复数,返回新的复数对象
    public Complex1 plus(Complex1 c) {
        // 实部相加
        int real = this.realPart + c.realPart;
        // 虚部相加
        int imag = this.imaginaryPart + c.imaginaryPart;
        // 创建结果复数
        Complex1 result = new Complex1();
        result.setRealPart(real);
        result.setImaginaryPart(imag);
        return result;
    }

    // 复数减法:当前复数 - 传入复数,返回新的复数对象
    public Complex1 minus(Complex1 c) {
        // 实部相减
        int real = this.realPart - c.realPart;
        // 虚部相减
        int imag = this.imaginaryPart - c.imaginaryPart;
        // 创建结果复数
        Complex1 result = new Complex1();
        result.setRealPart(real);
        result.setImaginaryPart(imag);
        return result;
    }

    // 格式化复数:处理正负虚部,输出标准格式 a+bi / a-bi
    public String format() {
        if (this.imaginaryPart < 0) {
            // 虚部为负,直接拼接
            return this.realPart + "" + this.imaginaryPart + "i";
        } else {
            // 虚部为正,添加+号
            return this.realPart + "+" + this.imaginaryPart + "i";
        }
    }
}

// 测试类
public class TestComplex {
    // 字符串解析为复数:核心方法,处理 a+bi、a-bi 格式
    public static Complex1 parseComplex(String str) {
        Complex1 complex = new Complex1();
        int real;
        int imaginary;

        // 判断虚部是否为负数
        if (str.contains("-")) {
            // 按-分割
            String[] split = str.split("-");
            real = Integer.parseInt(split[0]);
            // 负数虚部,去除i
            imaginary = -Integer.parseInt(split[1].replace("i", ""));
        } else {
            // 按+分割
            String[] split = str.split("\\+");
            real = Integer.parseInt(split[0]);
            // 正数虚部,去除i
            imaginary = Integer.parseInt(split[1].replace("i", ""));
        }

        complex.setRealPart(real);
        complex.setImaginaryPart(imaginary);
        return complex;
    }

    public static void main(String[] args) {
        // 创建扫描器,接收控制台输入
        Scanner in = new Scanner(System.in);

        System.out.println("请输入第一个复数(格式: a+bi 或 a-bi): ");
        String complexStr1 = in.nextLine();

        System.out.println("请输入第二个复数(格式: a+bi 或 a-bi): ");
        String complexStr2 = in.nextLine();

        // 解析字符串为复数对象
        Complex1 c1 = parseComplex(complexStr1);
        Complex1 c2 = parseComplex(complexStr2);

        // 运算并输出结果
        System.out.println("第一个复数+第二个复数: " + c1.plus(c2).format());
        System.out.println("第一个复数-第二个复数: " + c1.minus(c2).format());

        // 关闭扫描器,避免资源泄漏
        in.close();
    }
}

运行结果:

JAVA
请输入第一个复数(格式: a+bi 或 a-bi): 
1+2i
请输入第二个复数(格式: a+bi 或 a-bi): 
2+1i
第一个复数+第二个复数: 3+3i
第一个复数-第二个复数: -1+1i

图片

3. 问答题

(1)什么是接口?为什么要定义接口?接口与类有何异同?
答案:
  • 接口是 Java 中一种特殊引用类型,是行为规范与契约,只声明方法定义,不提供具体实现,不能实例化。
  • 弥补 Java类单继承局限,实现多实现;统一行为标准,制定开发规范;面向接口编程,降低耦合、程序解耦;便于程序扩展、维护,符合开闭原则。
  • 相同点:都属于引用类型;都可以定义方法、被继承 / 实现。
  • 不同点:
    • 定义关键字:类用class,接口用interface;
    • 成员变量:类可定义普通变量,接口只能是常量;
    • 构造方法:类有构造方法,接口没有构造方法;
    • 继承:类只能单继承,接口可多继承、类可多实现;
    • 实例化:类可 new 实例化,接口不能直接实例化;
    • 权限:接口只能 public / 默认权限,类可使用各种访问修饰符。
(2)如何定义接口?使用什么关键字?
答案:
  • 使用 interface 关键字定义接口。
JAVA
public interface 接口名{
    // 常量、抽象方法、默认方法、静态方法
}
(3)一个类如何实现接口?实现接口的类是否一定要重写该接口中的所有抽象方法?
答案:

类使用 implements 关键字实现接口,可同时实现多个接口,逗号分隔。

分两种情况:

  • 普通实体类:必须重写接口中所有抽象方法。
  • 抽象类:可以不重写接口抽象方法,由子类负责实现。

4. 编程题

编写一个完整的 Java Application 程序。包含接口 ShapeArea,MyRectangle 类,MyTriangle 类及 Test 类,具体要求如下:

(1)接口 ShapeArea:

double getArea():求一个形状的面积

double getPerimeter ():求一个形状的周长

(2)类 MyRectangle:

实现ShapeArea接口,并有以下属性和方法:

①属性

width:double 类型,表示矩形的长

height:double 类型,表示矩形的高

②方法

MyRectangle(double w, double h):构造函数

toString() 方法:输出矩形的描述信息,如“width = 1.0,height = 2.0,perimeter = 6.0,area = 2.0”

(3)类 MyTriangle:

实现 ShapeArea 接口,并有以下属性和方法:

①属性

a,b,c:double 型,表示三角形的三条边

s:周长的 1/2(注:求三角形面积公式为 ,s = (x + y + z)/2 ,开方可用 Math.sqrt(double) 方法)

②方法

MyTriangle(double x,double y,double z):构造函数,给三条边和s赋初值。

toString():输出矩形的描述信息,如“three sides:3.0,4.0,5.0,perimeter = 12.0,area = 6.0”

JAVA
// 接口:ShapeArea
interface ShapeArea {
    // 求面积
    double getArea();
    // 求周长
    double getPerimeter();
}

// 矩形类:实现接口
class MyRectangle implements ShapeArea {
    // 属性:宽、高
    private double width;
    private double height;

    // 构造方法
    public MyRectangle(double w, double h) {
        this.width = w;
        this.height = h;
    }

    // 实现周长
    @Override
    public double getPerimeter() {
        return 2 * (width + height);
    }

    // 实现面积
    @Override
    public double getArea() {
        return width * height;
    }

    // 描述信息
    @Override
    public String toString() {
        return "width = " + width +
               ",height = " + height +
               ",perimeter = " + getPerimeter() +
               ",area = " + getArea();
    }
}

// 三角形类:实现接口
class MyTriangle implements ShapeArea {
    // 三条边
    private double a;
    private double b;
    private double c;
    // 周长的一半
    private double s;

    // 构造方法
    public MyTriangle(double x, double y, double z) {
        this.a = x;
        this.b = y;
        this.c = z;
        this.s = (a + b + c) / 2;
    }

    // 周长
    @Override
    public double getPerimeter() {
        return a + b + c;
    }

    // 面积:海伦公式
    @Override
    public double getArea() {
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    // 描述信息
    @Override
    public String toString() {
        return "three sides:" + a + "," + b + "," + c +
               ",perimeter = " + getPerimeter() +
               ",area = " + getArea();
    }
}

// 测试主类
public class Test {
    public static void main(String[] args) {
        // 创建矩形对象
        MyRectangle rect = new MyRectangle(1.0, 2.0);
        // 创建三角形对象
        MyTriangle tri = new MyTriangle(3.0, 4.0, 5.0);

        // 输出
        System.out.println(rect);
        System.out.println(tri);
    }
}

运行结果:

JAVA
width = 1.0,height = 2.0,perimeter = 6.0,area = 2.0
three sides:3.04.05.0,perimeter = 12.0,area = 6.0

图片

二、课后习题:

第五章:抽象类与接口

一、填空题
二、问答题
三、编程题

评论