jmu-Java-PTA题解 (jmu-Java-03面向对象基础-04-形状-继承) 网安2312陈卓

问题要求

前言

前面题目形状中我们看到,为了输出所有形状的周长与面积,需要建立多个数组进行多次循环。这次试验使用继承与多态来改进我们的设计。

本题描述

1.定义抽象类Shape
属性:不可变静态常量double PI,值为3.14,
抽象方法:public double getPerimeter(),public double getArea()

2.RectangleCircle类均继承自Shape类。
Rectangle类(属性:int width,length)、Circle类(属性:int radius)。
带参构造方法为Rectangle(int width,int length),Circle(int radius)。
toString方法(Eclipse自动生成)

3.编写double sumAllArea方法计算并返回传入的形状数组中所有对象的面积和与
double sumAllPerimeter方法计算并返回传入的形状数组中所有对象的周长和。

4.main方法
4.1 输入整型值n,然后建立n个不同的形状。如果输入rect,则依次输入宽、长。如果输入cir,则输入半径。
4.2 然后输出所有的形状的周长之和,面积之和。并将所有的形状信息以样例的格式输出。
提示:使用Arrays.toString。
4.3 最后输出每个形状的类型与父类型.使用类似shape.getClass() //获得类型, shape.getClass().getSuperclass() //获得父类型;

注意:处理输入的时候使用混合使用nextInt与nextLine需注意行尾回车换行问题。

思考

  1. 你觉得sumAllArea和sumAllPerimeter方法放在哪个类中更合适?
  2. 是否应该声明为static?

输入样例:

4
rect
3 1
rect
1 5
cir
1
cir
2

输出样例:

38.84
23.700000000000003
[Rectangle [width=3, length=1], Rectangle [width=1, length=5], Circle [radius=1], Circle [radius=2]]
class Rectangle,class Shape
class Rectangle,class Shape
class Circle,class Shape
class Circle,class Shape

关键点

  • 抽象类与多态:通过抽象类 Shape 定义统一接口,子类 Rectangle 和 Circle 重写方法,实现 “同一接口,不同实现”,父类数组 Shape[] 存储子类对象,循环遍历时自动调用子类重写的方法(多态特性)。
  • 输入处理技巧:混合使用 nextInt() 和 nextLine() 时,需用 nextLine() 手动消耗残留的换行符,避免输入混乱。
  • 静态方法设计:sumAllArea 和 sumAllPerimeter 声明为静态方法,直接操作数组,无需依赖对象实例,提升代码效率。
  • 反射获取类信息:shape.getClass() 获取对象实际类型,getSuperclass() 获取父类,动态输出类层级关系。

解题步骤

第一步:设计抽象类 Shape

定义所有形状的公共属性和抽象方法,统一接口:

abstract class Shape {  
    static final double PI = 3.14; // 静态常量  
    public abstract double getPerimeter(); // 周长抽象方法  
    public abstract double getArea(); // 面积抽象方法  
}  

第二步:实现子类 Rectangle 和 Circle

继承 Shape 并实现具体计算逻辑:

class Rectangle extends Shape {  
    private int width, length;  
    public Rectangle(int width, int length) {  
        this.width = width;  
        this.length = length;  
    }  
    @Override  
    public double getPerimeter() { return 2 * (width + length); } // 矩形周长公式  
    @Override  
    public double getArea() { return width * length; } // 矩形面积公式  
    @Override  
    public String toString() { // 自动生成的字符串表示  
        return "Rectangle [width=" + width + ", length=" + length + "]";  
    }  
}  

class Circle extends Shape {  
    private int radius;  
    public Circle(int radius) { this.radius = radius; }  
    @Override  
    public double getPerimeter() { return 2 * PI * radius; } // 圆形周长公式  
    @Override  
    public double getArea() { return PI * radius * radius; } // 圆形面积公式  
    @Override  
    public String toString() { // 自动生成的字符串表示  
        return "Circle [radius=" + radius + "]";  
    }  
}  

第三步:编写静态工具方法

计算数组中所有形状的周长和与面积和:

public static double sumAllPerimeter(Shape[] shapes) {  
    double sum = 0;  
    for (Shape s : shapes) sum += s.getPerimeter(); // 多态调用子类方法  
    return sum;  
}  

public static double sumAllArea(Shape[] shapes) {  
    double sum = 0;  
    for (Shape s : shapes) sum += s.getArea(); // 多态调用子类方法  
    return sum;  
}  

第四步:主方法逻辑实现

使用 nextInt() 获取整数,nextLine() 消耗换行符,避免输入错位:

Scanner in = new Scanner(System.in);  
int n = in.nextInt();  
in.nextLine(); // 消耗换行符  
Shape[] shapes = new Shape[n];  

for (int i = 0; i < n; i++) {  
    String type = in.nextLine().trim();  
    if ("rect".equals(type)) {  
        int w = in.nextInt(), l = in.nextInt();  
        in.nextLine(); // 消耗换行符  
        shapes[i] = new Rectangle(w, l);  
    } else {  
        int r = in.nextInt();  
        in.nextLine(); // 消耗换行符  
        shapes[i] = new Circle(r);  
    }  
}  
System.out.println(sumAllPerimeter(shapes)); // 周长和  
System.out.println(sumAllArea(shapes)); // 面积和  
System.out.println(Arrays.toString(shapes)); // 形状信息  
for (Shape s : shapes) {  
    System.out.println(s.getClass() + ", " + s.getClass().getSuperclass()); // 类型与父类型  
}  

整体流程图:

整体代码:

import java.util.Arrays;
import java.util.Scanner;
abstract class Shape{
    static double PI = 3.14;
    public abstract double getPerimeter();
    public abstract double getArea();
}

class Rectangle extends Shape{
    private int width;
    private int length;
    public Rectangle(int width,int length) {
        this.width = width;
        this.length = length;
    }
    public double getPerimeter() {
        return 2*(this.width+this.length);
    }
    public double getArea() {
        return this.width*this.length;
    }
    public String toString() {
        return "Rectangle [width=" + width + ", length=" + length + "]";
    }
}

class Circle extends Shape {
    private int radius;

    public Circle(int radius) {
        this.radius = radius;
    }

    public double getPerimeter() {
        return PI * this.radius * 2;
    }

    public double getArea() {
        return PI * this.radius * this.radius;
    }

    public String toString() {
        return "Circle [radius=" + radius + "]";
    }
}

public class Main{
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        in.nextLine();
        Shape[] shape = new Shape[n];

        for (int i = 0; i < n; i++) {
            String e = in.nextLine();
            if(e.equals("rect")) {
                int width = in.nextInt();
                int length = in.nextInt();
                in.nextLine();
                shape[i] = new Rectangle(width, length);
            }
    		else {
                int radius = in.nextInt();
                in.nextLine();
                shape[i] = new Circle(radius);
            }
        }
        System.out.println(sumAllPerimeter(shape));
        System.out.println(sumAllArea(shape));
        System.out.println(Arrays.toString(shape));
        for(int i=0;i<n;i++){
            System.out.println(shape[i].getClass()+","+shape[i].getClass().getSuperclass());
        }
    }

    public static double sumAllArea(Shape[] shapes) {
        double sum = 0;

        for(Shape s:shapes) {
            sum+=s.getArea();
        }
        return sum;
    }
    public static double sumAllPerimeter(Shape[] shapes) {
        double sum = 0;

        for(Shape s:shapes) {
            sum+=s.getPerimeter();
        }
        return sum;
    }
}

思考:关于sumAllArea和sumAllPerimeter方法的设计,需从面向对象编程的封装性、扩展性和代码复用性角度综合考量。这两个方法的核心功能是对形状数组进行遍历计算,本质上属于处理 “形状集合” 的工具性操作,而非某个具体形状对象的属性或行为。因此,将其放置在Main类中最为合适 —— 这类方法不依赖于Shape及其子类的实例状态,仅通过参数接收形状数组并执行计算,符合 “工具方法” 的定位,避免污染Shape抽象类或其子类的职责,保持类结构的清晰性。从是否声明为static的问题来看,声明为静态方法具有多方面优势。首先,静态方法属于类本身而非对象实例,调用时无需创建任何对象,直接通过类名即可访问,这对于仅涉及数组遍历和计算的逻辑而言,能显著提升代码执行效率,减少内存开销。其次,静态方法的无状态性使其更具通用性 —— 无论程序中存在多少个形状对象,方法的逻辑始终保持一致,不会因对象状态的变化而产生副作用。此外,将工具方法声明为静态,符合开发者对 “全局辅助函数” 的认知习惯,便于代码的阅读和维护。进一步延伸思考,这种设计体现了 “单一职责原则”:Shape抽象类及其子类专注于描述单个形状的属性和行为(如计算自身周长和面积),而集合层面的批量计算则由独立的静态方法处理,确保每个类或方法仅负责单一功能。同时,静态方法的设计也为后续扩展提供了便利 —— 若未来需要增加其他统计功能(如计算所有形状的平均面积),只需在Main类中新增静态方法即可,无需修改现有类的结构,符合 “开闭原则”。在输入输出处理方面,混合使用nextInt和nextLine时需特别注意行尾换行符的处理,这是因为nextInt等数值读取方法不会消耗输入缓冲区中的换行符,残留的换行符会被后续的nextLine读取,导致输入错位。通过在每次读取数值后显式调用nextLine()消耗换行符,可以确保输入逻辑的准确性,这一细节体现了 Java 输入处理中对缓冲区管理的严谨性。最后,反射机制的应用(如shape.getClass())用于动态获取对象类型及其父类,这不仅实现了题目要求的输出功能,更展示了 Java 在运行时自省的能力。这种能力在框架开发、序列化、依赖注入等场景中至关重要,而在此题中作为拓展点,帮助开发者理解类的层级关系,为后续学习反射的高级应用奠定基础。

posted @ 2025-05-19 16:33  取名字比写博客还难  阅读(52)  评论(0)    收藏  举报