简介这份资源是东北大学Java面向对象实验1至6的完整代码合集面向正在修读该课程的学生以及希望系统练习OOP核心语法的自学者可解决实验无从下手、代码结构混乱、知识点难以串联等问题。压缩包共124个文件约51KB以java源码与class字节码为主辅以html说明页、dat数据文件、xml配置、gif图示及少量jar包覆盖类与对象、封装、继承、多态、构造器、接口、异常处理、集合框架、文件流、复杂数据结构与设计模式等实验主题从内容预览可见还包含GourmetCoffeeGUI等图形界面与目录加载相关类能直观感受多类协作与事件监听的组织方式。目前已有2002人学习下载说明该实验体系在课程学习者中认可度较高。读者可据此对照实验要求梳理每个实验的类结构、方法职责与调用关系理解私有字段与公共接口的配合、父类子类扩展方式以及接口实现多态的具体写法并借助dat与xml文件观察数据持久化与配置读取思路从而快速完成实验并夯实面向对象编程基础。1. 从一份 Java 实验代码说起六个实验到底练了什么很多人学 Java 卡在“语法都会一写就废”的阶段。课本上的for循环、extends、interface看得懂真让你从零写一个能跑的小系统脑子里却一片空白。这份面向对象实验代码合集恰好就是冲着这个痛点来的——它把 Java 面向对象的核心知识点拆成六个递进的实验每个实验都是一个能独立编译运行的小项目从类与对象、封装继承多态一路做到集合框架、异常处理和文件 IO。适合正在上 Java 课、需要交实验报告的学生也适合想系统补一遍 OOP 基础的自学者。代码结构清晰注释到位拿来当模板改一改就能交作业也能当复习提纲用。下面我按“能干什么 → 怎么跑 → 怎么改”的顺序把这份资源拆开讲。2. 实验一到六的知识点分布每个实验在练什么2.1 六个实验的递进关系这份代码不是六个孤立的练习而是一条有依赖关系的知识链。实验一通常从最简单的类定义入手让你写一个Student或Circle类理解成员变量、构造方法、this关键字的用法。实验二引入封装把字段改成private配上getter/setter再加重载构造方法。实验三进入继承和多态一般会设计一个Shape抽象类派生出Circle、Rectangle用Override重写area()方法。实验四往往涉及接口和抽象类的区别比如定义一个Payable接口让不同类去实现。实验五开始碰集合框架用ArrayList、HashMap管理对象数据。实验六综合前面所有内容加上异常处理和文件读写做一个能存盘的小型管理系统。这种安排的好处是每个实验只引入一两个新概念不会一次性把你压垮。我见过不少同学一上来就想写完整系统结果类之间的关系理不清写到一半就崩了。按这个顺序走每步都有可运行的产出心里踏实。2.2 每个实验的典型代码结构以实验三的图形面积计算为例核心代码大概长这样// 抽象基类定义所有图形的公共行为 abstract class Shape { private String name; public Shape(String name) { this.name name; } // 抽象方法子类必须实现 public abstract double area(); public String getName() { return name; } } // 圆形子类重写 area 方法 class Circle extends Shape { private double radius; public Circle(double radius) { super(圆形); this.radius radius; } Override public double area() { return Math.PI * radius * radius; } } // 矩形子类 class Rectangle extends Shape { private double width; private double height; public Rectangle(double width, double height) { super(矩形); this.width width; this.height height; } Override public double area() { return width * height; } }这段代码的关键在于abstract和Override两个点。Shape类本身不能被实例化它只负责定义“所有图形都有面积”这个契约。子类各自实现area()调用的时候用父类引用指向子类对象运行时自动找到正确的方法——这就是多态。参数方面radius、width、height都是double类型保证计算精度。如果你把area()的返回类型改成int编译不会报错但结果会丢失小数部分这是新手常踩的坑。实验五的集合操作通常是这样组织的import java.util.*; public class StudentManager { // 用 ArrayList 存储学生对象保持插入顺序 private ListStudent students new ArrayList(); public void addStudent(Student s) { students.add(s); } // 按学号查找返回 Optional 避免空指针 public OptionalStudent findById(String id) { for (Student s : students) { if (s.getId().equals(id)) { return Optional.of(s); } } return Optional.empty(); } // 按成绩降序排列 public void sortByScore() { students.sort((a, b) - Double.compare(b.getScore(), a.getScore())); } }这里用了List接口而不是直接写ArrayList是为了后面换实现类方便。Optional是 Java 8 引入的用来显式处理“可能找不到”的情况比返回null安全。排序用了 Lambda 表达式Double.compare处理浮点数比较比直接相减更可靠——直接相减在差值极小时可能返回 0导致排序不稳定。2.3 知识点与实验编号对照实验编号核心知识点典型类/接口难度实验一类与对象、构造方法Student、Circle入门实验二封装、方法重载BankAccount入门实验三继承、多态、抽象类Shape、Circle、Rectangle中等实验四接口、内部类Payable、Comparator中等实验五集合框架、泛型ArrayList、HashMap中等实验六异常、文件 IO、综合FileWriter、BufferedReader进阶这张表可以帮你快速定位自己薄弱的部分。如果你已经会写类了直接从实验三开始看如果集合框架不熟重点啃实验五。3. 把代码跑起来环境配置与编译执行3.1 JDK 安装与环境变量这份代码用的是标准 Java SE没有依赖任何第三方库所以只需要装一个 JDK 就行。推荐 JDK 8 或 JDK 11这两个版本在学校机房里最常见兼容性也最好。安装完之后验证一下# 检查 JDK 版本确认安装成功 java -version javac -version如果两条命令都能输出版本号说明环境没问题。java是运行命令javac是编译命令两个都要有。有些同学只装了 JRE结果javac用不了编译都过不去。环境变量方面JAVA_HOME指向 JDK 安装目录PATH里加上%JAVA_HOME%\binWindows或$JAVA_HOME/binMac/Linux。配完之后最好重开一个终端窗口不然环境变量不生效。3.2 目录结构与编译方式代码按实验编号分目录每个目录下是独立的.java文件。常见结构是experiment1/ ├── Student.java └── Main.java experiment2/ ├── BankAccount.java └── Main.java ...每个实验目录里都有一个Main.java作为入口里面包含public static void main(String[] args)方法。编译的时候进入对应目录# 进入实验三目录 cd experiment3 # 编译所有 java 文件 javac *.java # 运行主类 java Mainjavac *.java会把当前目录下所有 Java 文件一起编译生成对应的.class文件。如果某个文件有编译错误会直接报出来按行号去改就行。运行的时候写java Main不要写java Main.class后者会报错。如果类在包里比如package com.exp3;编译和运行都要带上包路径不过这份实验代码一般不带包简化处理。3.3 用 IDE 打开项目如果你习惯用 IDEIntelliJ IDEA 或 Eclipse 都可以直接打开。以 IDEA 为例File → Open选择实验目录IDEA 会自动识别 Java 文件并配置 SDK。如果提示没有 SDK去Project Structure → SDKs里添加你安装的 JDK 路径。运行的时候右键Main.java选Run Main.main()就行。用 IDE 的好处是调试方便可以在任意一行打断点看变量当前的值。比如实验三的多态调用你在area()方法里打个断点运行的时候就能看到实际调用的是哪个子类的方法。这个对理解多态特别有帮助比光看代码强得多。提示如果 IDEA 打开后代码全是红色报错先检查File → Project Structure → Project里的 Language Level 是不是设成了 8 以上。有些老版本默认是 5不支持 Lambda 和Optional。4. 核心模块拆解从类设计到数据流转4.1 实验六的综合管理系统架构实验六通常是前面所有知识点的汇总做一个带文件存储的管理系统。它的类结构一般分三层实体类、管理类、入口类。实体类负责存数据管理类负责增删改查入口类负责和用户交互。// 实体类只负责存数据不掺业务逻辑 public class Student { private String id; private String name; private double score; public Student(String id, String name, double score) { this.id id; this.name name; this.score score; } // getter/setter 省略实际代码里都有 public String toCsv() { return id , name , score; } public static Student fromCsv(String line) { String[] parts line.split(,); return new Student(parts[0], parts[1], Double.parseDouble(parts[2])); } }toCsv()和fromCsv()是一对负责对象和字符串之间的转换。存文件的时候调toCsv()读文件的时候调fromCsv()。这种设计叫序列化/反序列化是文件存储的基础。注意fromCsv()里用了Double.parseDouble如果文件里的成绩不是合法数字这里会抛NumberFormatException需要在调用处捕获。管理类负责所有业务操作import java.io.*; import java.util.*; public class StudentManager { private ListStudent students new ArrayList(); private static final String FILE_PATH students.csv; // 从文件加载数据文件不存在时返回空列表 public void loadFromFile() { File file new File(FILE_PATH); if (!file.exists()) { return; } try (BufferedReader reader new BufferedReader(new FileReader(file))) { String line; while ((line reader.readLine()) ! null) { if (!line.trim().isEmpty()) { students.add(Student.fromCsv(line)); } } } catch (IOException e) { System.err.println(读取文件失败 e.getMessage()); } } // 保存数据到文件 public void saveToFile() { try (BufferedWriter writer new BufferedWriter(new FileWriter(FILE_PATH))) { for (Student s : students) { writer.write(s.toCsv()); writer.newLine(); } } catch (IOException e) { System.err.println(写入文件失败 e.getMessage()); } } public void addStudent(Student s) { students.add(s); } public ListStudent getAll() { return Collections.unmodifiableList(students); } }这里有几个值得说的点。第一try-with-resources语法自动关闭流不用手动写finally块代码更干净。第二Collections.unmodifiableList返回一个只读视图防止外部代码直接修改内部列表这是封装思想的体现。第三文件路径用的是相对路径students.csv文件会生成在程序运行目录下。如果你在 IDE 里运行文件可能出现在项目根目录而不是src里找不到文件的时候先确认运行目录。4.2 异常处理的分层策略实验六会涉及异常处理常见的做法是分两层底层方法抛出异常上层方法捕获并处理。比如fromCsv解析失败时抛IllegalArgumentExceptionloadFromFile捕获后决定是跳过这一行还是终止加载。public static Student fromCsv(String line) { String[] parts line.split(,); if (parts.length ! 3) { throw new IllegalArgumentException(CSV 格式错误 line); } try { double score Double.parseDouble(parts[2]); return new Student(parts[0], parts[1], score); } catch (NumberFormatException e) { throw new IllegalArgumentException(成绩不是合法数字 parts[2], e); } }这样做的好处是异常信息更明确调用方知道具体哪里出了问题。IllegalArgumentException表示参数不合法NumberFormatException是它的子类这里包了一层并保留了原始异常第二个参数e方便排查。很多同学写异常处理就是e.printStackTrace()完事其实把异常信息写清楚后面调试能省很多时间。4.3 集合选型的实际考量实验五和实验六会用到集合选ArrayList还是LinkedList选HashMap还是TreeMap要看具体场景。这份代码里学生列表用ArrayList因为主要是遍历和追加随机访问多ArrayList的数组结构更合适。如果要频繁在中间插入删除LinkedList更好但实验场景一般用不上。HashMap用来做学号到学生的映射查找时间复杂度 O(1)。如果需要对学号排序输出可以换成TreeMap它会自动按 key 排序但插入和查找变成 O(log n)。我一般会先问自己这个数据是用来查的还是用来排的查得多就用HashMap排得多就用TreeMap。注意HashMap的 key 如果是自定义对象必须正确重写hashCode()和equals()否则查找会失效。实验里一般用String或Integer做 key不会遇到这个问题但二次开发时要留意。5. 避坑与排查编译运行中的常见问题5.1 中文乱码现象程序输出到控制台的中文变成问号或方块。原因Windows 默认编码是 GBK而 Java 源文件可能是 UTF-8编译时没指定编码。解决编译时加-encoding UTF-8即javac -encoding UTF-8 *.java。如果用的是 IDEA去Settings → Editor → File Encodings把全局编码和项目编码都设成 UTF-8。5.2 找不到主类现象运行java Main时报Could not find or load main class Main。原因要么没编译要么类名和文件名不一致要么带了包名但没建对应目录。解决先确认Main.class文件存在再检查Main.java里public class Main的类名是否和文件名完全一致大小写敏感。如果类在包里运行时要写全限定名比如java com.exp1.Main。5.3 文件读写路径错误现象实验六运行时提示FileNotFoundException或文件生成了但找不到。原因相对路径是相对于“当前工作目录”不是相对于.java文件所在目录。在 IDE 里运行工作目录通常是项目根目录在命令行运行工作目录是你执行java命令时所在的目录。解决用绝对路径测试或者打印System.getProperty(user.dir)看当前工作目录到底在哪。5.4 空指针异常现象运行时抛NullPointerException但不知道哪一行。原因调用了null对象的方法或访问了null对象的字段。解决在可能为null的地方加判断或者用Optional包装返回值。调试时看异常堆栈的第一行会指出具体是哪个类的哪一行。实验五的findById如果返回null而不是Optional调用方忘了判空就会中招。5.5 集合遍历时删除元素现象用for-each遍历ArrayList时删除元素抛ConcurrentModificationException。原因for-each底层用的是迭代器遍历过程中修改集合结构会让迭代器失效。解决用Iterator.remove()方法删除或者用removeIf()。比如students.removeIf(s - s.getScore() 60)一行就能删掉所有不及格的比手动遍历安全。6. 二次开发与改造思路从实验代码到课程设计6.1 把控制台程序改成图形界面实验代码都是控制台交互如果你想拿它做课程设计加一个 Swing 或 JavaFX 界面会加分不少。改造思路是保留StudentManager不动新建一个MainFrame类把按钮事件绑定到管理类的方法上。比如“添加学生”按钮的监听器里从文本框读数据构造Student对象调manager.addStudent()然后刷新表格。// Swing 按钮事件绑定示例 addButton.addActionListener(e - { String id idField.getText().trim(); String name nameField.getText().trim(); double score Double.parseDouble(scoreField.getText().trim()); manager.addStudent(new Student(id, name, score)); refreshTable(); // 刷新 JTable 显示 });这样改的好处是业务逻辑和界面分离管理类不用动测试也方便。我一般会先保证控制台版本跑通再加界面不然出了问题分不清是逻辑错还是界面错。6.2 换存储方式从 CSV 到数据库CSV 文件适合小数据量但并发写入和查询效率都不行。二次开发时可以换成 SQLite不用装数据库服务器一个文件就是一个库。用 JDBC 连接把loadFromFile和saveToFile换成 SQL 语句。// 用 JDBC 替换文件存储 public void saveToDb(Student s) { String sql INSERT INTO student(id, name, score) VALUES(?, ?, ?); try (Connection conn DriverManager.getConnection(jdbc:sqlite:student.db); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getId()); ps.setString(2, s.getName()); ps.setDouble(3, s.getScore()); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } }用PreparedStatement而不是Statement拼接字符串能防止 SQL 注入也更高效。改造的时候注意异常类型从IOException变成SQLException捕获逻辑要跟着改。6.3 加单元测试保证改造不出错改代码最怕改出 bug。加 JUnit 测试是个好习惯每次改完跑一遍心里有底。比如给StudentManager写测试import org.junit.Test; import static org.junit.Assert.*; public class StudentManagerTest { Test public void testAddAndFind() { StudentManager manager new StudentManager(); manager.addStudent(new Student(001, 张三, 85.5)); assertEquals(1, manager.getAll().size()); assertEquals(张三, manager.getAll().get(0).getName()); } }测试覆盖增删改查和边界情况空列表、重复学号、非法成绩改代码的时候就有安全网。我一般会先写测试再改代码这样能确保改造不破坏原有功能。6.4 一个容易忽略的细节equals 和 hashCode如果你在实验五里用Student对象做HashMap的 key或者用List.contains()判断学生是否存在必须重写equals()和hashCode()。默认的实现比较的是对象地址两个内容相同但地址不同的对象会被认为不相等。Override public boolean equals(Object o) { if (this o) return true; if (o null || getClass() ! o.getClass()) return false; Student student (Student) o; return Objects.equals(id, student.id); } Override public int hashCode() { return Objects.hash(id); }这里用学号作为判断依据因为学号是唯一的。Objects.equals和Objects.hash是 Java 7 引入的工具方法能自动处理null比手写if (id ! null id.equals(...))简洁。重写之后list.contains(new Student(001, ...))就能正确返回true了。从那以后我每次改完代码都会先跑一遍原有的测试用例确认没破坏已有功能再提交。这个习惯帮我省了不少返工的时间。希望这份实验代码和上面的改造思路能帮你把 Java 面向对象真正用起来而不只是应付考试。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?