资讯详情

资讯详情

Java面向对象实验代码全解析:从类到异常处理

简介这份资源是东北大学Java面向对象实验课程第1至6次的完整代码合集面向正在学习Java OOP的高校学生与自学者帮助解决实验无从下手、缺少参考实现的问题。压缩包共124个文件约51KB以java源码与class字节码为主辅以html说明页面、dat数据文件、xml配置及少量gif与jar覆盖类与对象、封装、继承、多态、构造器、接口、异常处理、集合框架、文件流、数据结构与设计模式等核心知识点从内容预览可见还包含GourmetCoffeeGUI等图形界面与目录加载相关类实验场景较为完整。目前已有2002人学习下载说明该套代码在课程学习者中认可度较高。读者可据此对照实验要求梳理类结构与方法调用关系理解私有变量与公共接口的封装思路、子类对父类的扩展方式以及接口实现与多态调用并参考异常捕获与集合存储的写法快速搭建可运行工程、排查编译与运行错误为后续复杂Java应用开发打下扎实基础。1. 从六份 Java 实验代码里我拆出了面向对象最该练的几件事带过几轮 Java 实验的人都有个共识面向对象这东西光看语法是学不会的必须动手把类、继承、多态、接口、集合、异常这些点一个个敲进代码里才能形成肌肉记忆。这份「Java 面向对象实验全部实验代码 1 到 6」就是一套按实验顺序排好的完整代码包覆盖了从类与对象基础、封装继承、多态与抽象类、接口与内部类到集合框架、异常处理与文件 IO 的典型练习。它解决的不是「教你 Java 语法」的问题而是「给你一套能跑、能改、能对照的参考实现」让你在做实验卡壳时有个能翻的底本。适合正在上 Java 面向对象课、需要交实验报告的学生也适合想系统补一遍 OOP 基本功的转行者。下面我按实验顺序拆开讲重点放在每个实验的设计意图、代码结构、参数怎么调以及我踩过的那些坑。2. 实验 1 到 3类、封装、继承的代码骨架怎么搭前三个实验是整个面向对象的地基很多人觉得简单就跳过结果后面写多态和接口时连构造方法链都理不清。这三个实验的代码量不大但每一行都在训练「把现实问题抽象成类」的思维。2.1 实验 1类与对象的最小闭环实验 1 通常要求定义一个简单的类比如学生类或矩形类包含私有属性、构造方法、getter/setter再在 main 里创建对象并调用方法。这份代码里用的是「学生成绩管理」的场景核心结构如下// Student.java public class Student { private String name; // 姓名私有属性 private int score; // 成绩私有属性 // 无参构造 public Student() { this(未知, 0); // 调用有参构造避免重复赋值 } // 有参构造 public Student(String name, int score) { this.name name; this.score score; } public String getName() { return name; } public void setName(String name) { this.name name; } public int getScore() { return score; } // 设置成绩时做合法性校验 public void setScore(int score) { if (score 0 || score 100) { throw new IllegalArgumentException(成绩必须在 0-100 之间); } this.score score; } Override public String toString() { return Student{name name , score score }; } }这段代码的关键点有三个一是this(未知, 0)这种构造方法链能减少重复赋值逻辑二是setScore里加了范围校验这是封装的意义——不让外部随意改坏内部状态三是重写了toString()方便打印调试。参数方面score用int而不是String是为了后续排序和比较方便如果你实验里要求成绩带小数改成double即可但要注意浮点数比较的精度问题。2.2 实验 2继承与方法重写实验 2 一般会引入继承关系比如让GraduateStudent继承Student增加导师、研究方向等字段。这份代码里的做法是// GraduateStudent.java public class GraduateStudent extends Student { private String supervisor; // 导师 private String researchArea; // 研究方向 public GraduateStudent(String name, int score, String supervisor, String researchArea) { super(name, score); // 必须先调用父类构造 this.supervisor supervisor; this.researchArea researchArea; } Override public String toString() { return super.toString() , supervisor supervisor ; } }这里最容易翻车的地方是super(name, score)的位置——它必须是子类构造方法的第一条语句否则编译不过。另外如果父类没有无参构造子类就必须显式调用super(...)这个规则在实验 3 里会反复出现。参数上supervisor和researchArea用String存储如果实验要求关联到某个导师对象那就得再定义一个Teacher类把这里改成Teacher supervisor这就是从「字符串建模」到「对象建模」的升级。2.3 实验 3多态与抽象类的落地实验 3 通常要求把父类改成抽象类或者定义抽象方法让不同子类给出不同实现。这份代码里把Student改成了抽象类增加了一个抽象方法getDescription()// Student.java 改为抽象类 public abstract class Student { protected String name; protected int score; public Student(String name, int score) { this.name name; this.score score; } // 抽象方法子类必须实现 public abstract String getDescription(); public int getScore() { return score; } }然后在GraduateStudent和UndergraduateStudent里分别实现// UndergraduateStudent.java public class UndergraduateStudent extends Student { private String major; public UndergraduateStudent(String name, int score, String major) { super(name, score); this.major major; } Override public String getDescription() { return 本科生 name 专业 major; } }多态的价值在这里体现得很直接你可以写一个ListStudent往里放本科生和研究生遍历时调用getDescription()运行时会自动找到对应子类的实现。参数上要注意抽象类不能直接new必须通过子类实例化如果某个子类没实现抽象方法编译器会直接报错这是好事能提前暴露遗漏。提示实验 1 到 3 的代码建议按顺序跑一遍每跑完一个实验就手动改几个参数比如把成绩改成负数看校验是否生效把super调用删掉看编译报错这种「故意改坏」的练习比只看代码有效得多。3. 实验 4 到 5接口、内部类与集合框架的配合实验 4 和 5 开始进入 Java 面向对象里更「工程化」的部分——接口定义契约、内部类简化回调、集合管理批量数据。这三个东西单独学都不难但放在一起用的时候初学者很容易写出「接口当类用、内部类乱嵌套、集合遍历时删元素」这类问题。3.1 实验 4接口与内部类的典型写法实验 4 一般要求定义一个接口比如Sortable或Comparable的自定义版本然后让某个类实现它。这份代码里定义了一个Scorable接口// Scorable.java public interface Scorable { int getScore(); default boolean isPass() { return getScore() 60; // 默认方法子类可覆盖 } }然后Student实现这个接口并在内部用一个静态内部类做比较器// Student.java 片段 public class Student implements Scorable, ComparableStudent { private String name; private int score; // ... 构造和 getter 省略 Override public int getScore() { return score; } Override public int compareTo(Student other) { return Integer.compare(this.score, other.score); } // 静态内部类按姓名排序的比较器 public static class NameComparator implements ComparatorStudent { Override public int compare(Student s1, Student s2) { return s1.name.compareTo(s2.name); } } }这里有两个关键设计一是default方法让接口有了默认实现子类不必强制重写二是静态内部类NameComparator不持有外部类实例适合做工具型比较器。参数上Integer.compare比手写this.score - other.score更安全因为后者在极端值下可能溢出。如果你实验里要求匿名内部类可以把NameComparator改成new ComparatorStudent() { ... }的形式效果一样只是不能复用。3.2 实验 5集合框架的增删改查与遍历实验 5 通常要求用ArrayList、HashMap或HashSet管理一批学生数据并实现按条件筛选、排序、统计。这份代码里用的是ArrayList加HashMap的组合import java.util.*; public class StudentManager { private ListStudent students new ArrayList(); private MapString, Student indexByName new HashMap(); public void addStudent(Student s) { students.add(s); indexByName.put(s.getName(), s); // 建立姓名索引 } // 按成绩筛选 public ListStudent filterByScore(int minScore) { ListStudent result new ArrayList(); for (Student s : students) { if (s.getScore() minScore) { result.add(s); } } return result; } // 按成绩排序 public void sortByScore() { Collections.sort(students); // 依赖 Comparable 实现 } // 按姓名排序 public void sortByName() { students.sort(new Student.NameComparator()); } }这段代码里最值得说的是indexByName这个HashMap——它让按姓名查找从 O(n) 降到 O(1)这是集合选型的实际意义。参数上filterByScore的minScore是包含边界的如果你要改成不包含把改成即可。另外Collections.sort和List.sort的区别在于前者是静态工具方法后者是 Java 8 引入的默认方法两者底层都是归并排序稳定性有保证。注意遍历ArrayList时如果同时删除元素必须用Iterator.remove()或removeIf()直接调list.remove()会抛ConcurrentModificationException这是实验 5 里最常见的翻车点。4. 实验 6异常处理与文件 IO 的完整链路实验 6 通常是综合练习要求把前面定义的学生对象写入文件再从文件读回来同时处理各种异常。这份代码里用的是「文本文件 缓冲流」的方案没有上序列化因为实验环境里更看重对try-catch-finally和try-with-resources的掌握。4.1 自定义异常与校验逻辑先定义一个业务异常用来区分「文件格式错误」和「IO 错误」// InvalidStudentFormatException.java public class InvalidStudentFormatException extends Exception { public InvalidStudentFormatException(String message) { super(message); } }然后在解析每一行时抛出private Student parseLine(String line) throws InvalidStudentFormatException { String[] parts line.split(,); if (parts.length ! 2) { throw new InvalidStudentFormatException(字段数量不对 line); } try { String name parts[0].trim(); int score Integer.parseInt(parts[1].trim()); return new Student(name, score); } catch (NumberFormatException e) { throw new InvalidStudentFormatException(成绩不是数字 line); } }这里的设计意图是NumberFormatException是运行时异常直接往外抛会让调用方难以区分「格式问题」和「文件不存在」包一层自定义异常后上层就能用catch (InvalidStudentFormatException e)单独处理。4.2 文件读写与资源关闭写文件和读文件都用try-with-resources避免忘记关流// 写入 public void saveToFile(String path) throws IOException { try (BufferedWriter writer new BufferedWriter(new FileWriter(path))) { for (Student s : students) { writer.write(s.getName() , s.getScore()); writer.newLine(); } } } // 读取 public void loadFromFile(String path) throws IOException, InvalidStudentFormatException { try (BufferedReader reader new BufferedReader(new FileReader(path))) { String line; while ((line reader.readLine()) ! null) { if (line.trim().isEmpty()) continue; // 跳过空行 students.add(parseLine(line)); } } }参数上FileWriter默认是覆盖模式如果要追加需要传第二个参数true。BufferedReader的readLine()返回null表示文件结束这个判断不能少。另外文件路径建议用Paths.get(data, students.txt)这种跨平台写法直接拼/在 Windows 上虽然也能跑但不够规范。提示实验 6 跑完后手动把数据文件里的某一行改成「张三,abc」再运行读取程序看自定义异常是否被正确捕获并打印出可读信息。这个验证步骤能帮你确认异常链路是通的。5. 六个实验跑下来这几个坑我替你踩过了5.1 构造方法链断裂导致编译失败现象子类构造方法里写了super(name, score)但父类只有无参构造编译器报「无法将类中的构造器应用到给定类型」。原因Java 规定子类构造必须调用父类某个构造如果父类没有无参构造子类必须显式super(...)且必须是第一句。解决要么给父类补一个无参构造要么在子类第一行写super(...)。我一般建议父类保留无参构造减少子类负担。5.2 重写 equals 没重写 hashCode现象把Student对象放进HashSet后明明两个对象属性一样却都被保留没去重。原因HashSet先用hashCode定位桶再用equals比较。只重写equals不重写hashCode两个相等对象的哈希值不同会被分到不同桶。解决用 IDE 自动生成或手写hashCode确保equals为 true 的两个对象hashCode也相等。实验 5 里如果用到HashSet这个坑必踩。5.3 遍历集合时删除元素抛异常现象for (Student s : students) { if (s.getScore() 60) students.remove(s); }运行时报ConcurrentModificationException。原因增强 for 循环底层用Iterator但remove调的是集合自己的方法导致迭代器状态和集合状态不一致。解决改用Iterator.remove()或者直接用students.removeIf(s - s.getScore() 60)后者更简洁。5.4 文件路径写死导致换环境就找不到文件现象在本机跑得好好的换台机器或换个目录就报FileNotFoundException。原因用了绝对路径如D:/code/data.txt或者用了相对路径但工作目录变了。解决用Paths.get(System.getProperty(user.dir), data, students.txt)获取当前工作目录下的路径或者把文件放在src同级目录并用相对路径。实验 6 里建议把数据文件放在项目根目录的data文件夹下。5.5 抽象类里写了构造方法却以为不能有现象看到抽象类里有构造方法以为写错了删掉后子类编译报错。原因抽象类可以有构造方法只是不能直接new它的构造方法是给子类super()调用的。解决保留抽象类的构造方法用来初始化父类字段。实验 3 里如果父类有name和score就应该在抽象类构造里赋值子类通过super(name, score)传递。6. 把实验代码改成自己的项目三个进阶改法这套代码如果只是跑一遍交作业有点浪费。我一般会做三件事把它变成能写进简历的小项目。第一把Student换成你熟悉的业务对象比如「订单」「商品」「传感器读数」然后重新设计字段和校验规则。这一步练的是抽象能力——同样的继承、接口、集合结构换一个领域能不能快速套上去。第二给StudentManager加一个命令行菜单用Scanner读取用户输入实现增删改查的交互。这个改动会逼你处理输入校验、循环控制、异常捕获比单纯写类更有工程感。代码结构大概是这样public static void main(String[] args) { StudentManager manager new StudentManager(); Scanner scanner new Scanner(System.in); while (true) { System.out.println(1.添加 2.按成绩筛选 3.保存 4.读取 0.退出); int choice scanner.nextInt(); scanner.nextLine(); // 吃掉换行符 switch (choice) { case 1: System.out.print(姓名); String name scanner.nextLine(); System.out.print(成绩); int score scanner.nextInt(); manager.addStudent(new Student(name, score)); break; case 2: System.out.print(最低分); int min scanner.nextInt(); manager.filterByScore(min).forEach(System.out::println); break; case 3: try { manager.saveToFile(data/students.txt); } catch (IOException e) { System.out.println(保存失败 e.getMessage()); } break; case 0: return; } } }第三把文件存储从文本改成 JSON 或 CSV用第三方库如 Jackson 或 OpenCSV 做序列化。这一步会接触到依赖管理Maven/Gradle和库的 API 设计是从「实验代码」到「真实项目」的关键一跳。参数上Jackson 的ObjectMapper默认要求类有无参构造和 getter/setter如果字段是private且没有 getter需要加JsonProperty注解。从那以后我每次拿到一套实验代码都会先跑通、再改坏、最后换场景重写一遍这三步走完代码才真正变成自己的。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →