一、实验内容
掌握抽象类和接口
1. 掌握抽象类与接口的基本概念。
(1)抽象类:
- 定义:使用 abstract 关键字 修饰的类,称为抽象类。
- 抽象方法:使用 abstract 修饰、只有方法声明(无方法体,即无 {} 实现)的方法,称为抽象方法。包含抽象方法的类,必须定义为抽象类。
- 核心特点:
- 抽象类不能直接通过 new 实例化,只能通过子类继承使用。
- 子类继承抽象类后,必须重写抽象类中所有的抽象方法;若子类也声明为抽象类,则可不用重写。
- 抽象类中可包含普通成员变量、普通方法、构造方法、静态方法和抽象方法。
- 抽象类遵循单继承原则,一个子类只能继承一个抽象类。
(2)接口:
- 定义:使用 interface 关键字定义的类型,称为接口,本质是一种行为规范和统一标准。
- 核心特点:
- 接口是完全抽象的约束,仅定义方法规范,不提供具体实现。
- 接口中默认规则:抽象方法默认修饰符为 public abstract,成员变量默认修饰符为 public static final(即常量)。
- 接口没有构造方法,不能直接实例化。
- 一个类可以实现多个接口(用 implements 关键字),弥补 Java 单继承的局限性。
- 接口之间可以通过 extends 关键字实现多继承。
2. 运行案例 5-1, 案例 5-2 , 案例 5-3。
案例 5-1:利用面向对象程序设计的思想来实现两个数的加法和减法——以复数为例。
// 定义两个数操作的接口
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);
}
}运行结果:
5+7i
-1-1i
案例 5-2:饲养员给动物喂食物。体现 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);
}
}运行结果:
小狗啃肉骨头
小猫吃鱼
案例 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() 方法。定义两个复数,实现复数的加减运算。
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();
}
}运行结果:
请输入第一个复数(格式: 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 关键字定义接口。
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”
// 接口: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);
}
}运行结果:
width = 1.0,height = 2.0,perimeter = 6.0,area = 2.0
three sides:3.0,4.0,5.0,perimeter = 12.0,area = 6.0
评论