
简介这份资源是东北大学Java面向对象课程实验1至6的完整代码合集面向正在学习Java OOP的高校学生与自学者帮助解决实验无从下手、缺少参考实现的问题。压缩包共124个文件约51KB以java源码与class字节码为主辅以html说明页面、dat数据文件、xml配置及少量gif、jar等覆盖类与对象、封装、继承、多态、构造器、接口、异常处理、集合框架、文件流、复杂数据结构与设计模式等核心知识点从内容预览可见还包含GourmetCoffeeGUI等图形界面与目录加载相关类实验场景较为完整。目前已有2002人学习下载说明该套代码在同类课程资料中认可度较高。读者可据此对照实验要求梳理类结构与方法调用关系理解私有变量与公共接口的封装思路、子类对父类的扩展方式以及接口实现与多态调用并参考异常捕获与集合存储的写法快速完成实验并巩固面向对象编程基础。1. 从一份 Java 实验代码说起六个实验到底练了什么很多同学学完 Java 语法写个Hello World没问题一到“面向对象”就卡壳——类怎么设计、继承怎么用、多态到底解决什么问题书本上的例子看懂了自己动手还是懵。这份资源就是一套完整的 Java 面向对象实验代码从实验一到实验六覆盖了类与对象、继承与多态、接口与抽象类、集合框架、异常处理、文件 I/O 这几个核心模块。每个实验都是独立可运行的工程不是零散代码片段适合正在上 Java 课、需要交实验报告或者想通过动手把 OOP 思想吃透的开发者。如果你已经能写基本语法但一写多类协作就乱这套代码值得拆一遍。2. 实验一到六的模块拆解每个实验在练什么2.1 实验一类与对象的基础建模实验一通常从最简单的类定义开始比如定义一个Student类包含姓名、学号、成绩等属性再写构造方法、getter/setter最后在main里创建对象并调用方法。这个实验看起来简单但它是后面所有实验的地基。我见过不少人直接跳过这个实验去写继承结果连this和构造方法的重载都搞不清楚。核心代码结构一般是这样// Student.java public class Student { private String name; private String id; private double score; // 无参构造 public Student() {} // 带参构造用于初始化对象 public Student(String name, String id, double score) { this.name name; this.id id; this.score score; } // getter/setter 省略注意封装性 public void printInfo() { System.out.println(姓名 name 学号 id 成绩 score); } }这里的关键是理解private修饰符的作用——属性私有对外提供公共方法访问。很多初学者会直接把属性写成public编译也能过但这就失去了封装的意义。实验一的评分点往往就在“是否使用了封装”。2.2 实验二继承与方法重写实验二引入继承关系典型场景是Person作为父类Student和Teacher作为子类。子类通过extends继承父类的属性和方法同时可以重写父类的方法来实现自己的行为。这个实验的核心是理解super关键字和Override注解。// Person.java public class Person { protected String name; protected int age; public Person(String name, int age) { this.name name; this.age age; } public void introduce() { System.out.println(我是 name 今年 age 岁); } } // Student.java public class Student extends Person { private String major; public Student(String name, int age, String major) { super(name, age); // 调用父类构造方法 this.major major; } Override public void introduce() { super.introduce(); // 复用父类逻辑 System.out.println(我的专业是 major); } }super(name, age)必须放在子类构造方法的第一行这是 Java 的硬性规定。Override注解不是必须的但加上之后编译器会帮你检查方法签名是否匹配避免拼写错误导致重写失败。实验二常见的坑是把父类属性写成private子类访问不到这时候要么改成protected要么通过 getter/setter 访问。2.3 实验三多态与抽象类实验三开始涉及多态——父类引用指向子类对象运行时根据实际类型调用对应的方法。典型例子是定义一个抽象类Shape包含抽象方法area()然后Circle、Rectangle分别实现。这个实验的目的是让你理解“面向接口编程”而不是“面向实现编程”。// Shape.java public abstract class Shape { public abstract double area(); // 抽象方法子类必须实现 } // Circle.java public class Circle extends Shape { private double radius; public Circle(double radius) { this.radius radius; } Override public double area() { return Math.PI * radius * radius; } } // 测试类 public class TestShape { public static void main(String[] args) { Shape s1 new Circle(5.0); Shape s2 new Rectangle(3.0, 4.0); System.out.println(s1.area()); // 运行时决定调用哪个 area() System.out.println(s2.area()); } }多态的好处在于你可以写一个方法接收Shape类型的参数不管传入的是圆还是矩形都能正确计算面积。如果后面新增一个Triangle类不需要修改任何已有代码直接继承Shape就行。这就是开闭原则的体现。2.4 实验四接口与集合框架实验四一般会把接口和集合结合起来用。比如定义一个Comparable接口的实现然后用ArrayList存储对象再调用Collections.sort()排序。这个实验的难点在于理解接口和抽象类的区别——接口定义“能做什么”抽象类定义“是什么”。// 定义接口 public interface Printable { void print(); } // 实现类 public class Book implements Printable, ComparableBook { private String title; private double price; public Book(String title, double price) { this.title title; this.price price; } Override public void print() { System.out.println(title price 元); } Override public int compareTo(Book other) { return Double.compare(this.price, other.price); } } // 使用集合 ListBook books new ArrayList(); books.add(new Book(Java编程, 59.9)); books.add(new Book(数据结构, 45.0)); Collections.sort(books); // 按价格排序 for (Book b : books) { b.print(); }Comparable接口的compareTo方法返回值决定了排序顺序负数表示当前对象小正数表示大零表示相等。这里用Double.compare是标准做法避免直接相减导致的精度问题。集合框架这块ArrayList和LinkedList的区别、HashMap的键值对操作实验四通常都会涉及。2.5 实验五异常处理实验五专门练异常。一般会要求自定义一个异常类然后在方法中throw调用方用try-catch捕获。这个实验的核心是理解 checked exception 和 unchecked exception 的区别以及finally块的作用。// 自定义异常 public class InvalidScoreException extends Exception { public InvalidScoreException(String message) { super(message); } } // 业务方法 public void setScore(double score) throws InvalidScoreException { if (score 0 || score 100) { throw new InvalidScoreException(成绩必须在0到100之间 score); } this.score score; } // 调用方 try { student.setScore(150); } catch (InvalidScoreException e) { System.out.println(输入有误 e.getMessage()); } finally { System.out.println(成绩设置操作结束); }继承Exception的是 checked exception调用方必须处理继承RuntimeException的是 unchecked编译器不强制捕获。实验五的常见错误是自定义异常继承了RuntimeException然后调用方不写try-catch运行时直接崩溃。另外finally块里的代码无论是否发生异常都会执行常用来释放资源。2.6 实验六文件 I/O 与综合应用实验六通常是综合前面的知识点做一个带文件读写的小系统。比如学生信息管理系统支持从文件读取学生数据、增删改查、再写回文件。这个实验会用到File、FileReader、BufferedReader、FileWriter等类。// 读取文件 public ListStudent loadFromFile(String path) throws IOException { ListStudent list new ArrayList(); try (BufferedReader br new BufferedReader(new FileReader(path))) { String line; while ((line br.readLine()) ! null) { String[] parts line.split(,); list.add(new Student(parts[0], parts[1], Double.parseDouble(parts[2]))); } } return list; } // 写入文件 public void saveToFile(String path, ListStudent list) throws IOException { try (BufferedWriter bw new BufferedWriter(new FileWriter(path))) { for (Student s : list) { bw.write(s.getName() , s.getId() , s.getScore()); bw.newLine(); } } }try-with-resources是 Java 7 引入的语法括号里声明的资源会在 try 块结束后自动关闭不用手动写finally里的close()。文件路径建议用相对路径比如data/students.txt这样换台机器也能跑。实验六的评分点通常包括文件不存在时是否创建、读写是否用了缓冲流、异常是否妥善处理。3. 怎么把这份代码跑起来环境配置与运行步骤3.1 JDK 安装与环境变量配置这份代码基于标准 Java SE没有用到第三方框架所以只需要装 JDK 就行。推荐 JDK 8 或 JDK 11这两个版本在学校环境里最稳。安装完之后配置JAVA_HOME和PATH# Windows 下在系统环境变量里添加 JAVA_HOME C:\Program Files\Java\jdk1.8.0_301 PATH %JAVA_HOME%\bin;%PATH% # macOS/Linux 下编辑 ~/.bash_profile 或 ~/.zshrc export JAVA_HOME/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home export PATH$JAVA_HOME/bin:$PATH配置完之后在终端执行java -version和javac -version能正确输出版本号就说明环境没问题。如果提示“不是内部或外部命令”说明PATH没配好检查一下路径里有没有空格或拼写错误。3.2 用命令行编译和运行每个实验的代码通常放在一个以实验编号命名的文件夹里比如experiment1、experiment2。进入对应目录后先编译再运行# 编译所有 .java 文件 javac -d out src/*.java # 运行主类 java -cp out com.experiment.Main-d out表示把编译后的.class文件放到out目录保持包结构。-cp指定 classpath告诉 JVM 去哪里找类文件。如果代码里用了包声明比如package com.experiment;编译后的目录结构会自动对应。3.3 用 IDE 导入项目如果习惯用 IDEIntelliJ IDEA 或 Eclipse 都可以直接导入。以 IDEA 为例File - New - Project from Existing Sources选择实验代码的根目录IDEA 会自动识别src目录和包结构。如果识别不出来手动把src标记为 Sources Root。运行的时候右键主类选择Run Main.main()就行。提示如果导入后报“找不到符号”或“包不存在”先检查src目录是否被正确标记再检查 JDK 版本是否和代码兼容。实验代码一般不会用到高版本特性JDK 8 足够。4. 避坑与常见问题跑代码时容易翻车的几个点4.1 中文乱码现象控制台输出的中文变成问号或方块。原因Windows 默认编码是 GBK而代码文件可能是 UTF-8编译时没指定编码。解决编译时加-encoding UTF-8运行时加-Dfile.encodingUTF-8javac -encoding UTF-8 -d out src/*.java java -Dfile.encodingUTF-8 -cp out com.experiment.Main4.2 空指针异常现象运行时报NullPointerException定位到某一行。原因对象没初始化就调用了方法或访问了属性。比如Student s; s.getName();s还是 null。解决创建对象时确保调用了构造方法Student s new Student();。集合操作时先判断是否为 null或者用Optional包装。4.3 文件路径错误现象实验六读写文件时报FileNotFoundException。原因用了绝对路径换台机器路径不存在或者相对路径的基准目录不是项目根目录。解决统一用相对路径并且在代码里打印当前工作目录确认System.out.println(System.getProperty(user.dir));然后把文件放在这个目录下或者用getClass().getResourceAsStream()从 classpath 读取。4.4 集合遍历时删除元素现象用for-each遍历ArrayList时删除元素抛ConcurrentModificationException。原因for-each底层用的是Iterator遍历过程中修改集合结构会导致迭代器失效。解决用Iterator显式遍历调用iterator.remove()IteratorStudent it list.iterator(); while (it.hasNext()) { Student s it.next(); if (s.getScore() 60) { it.remove(); // 安全删除 } }4.5 重写 equals 和 hashCode现象把对象放进HashSet或作为HashMap的键去重或查找失败。原因只重写了equals没重写hashCode或者两个都没重写导致两个内容相同的对象被认为是不同的键。解决同时重写equals和hashCodeIDEA 可以自动生成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); }5. 二次开发与改造从实验代码到能用的项目5.1 把控制台输出改成图形界面实验代码基本都是控制台交互如果想改成 Swing 或 JavaFX 界面核心思路是把main里的Scanner输入换成按钮事件把System.out.println换成JTextArea.append。比如实验六的学生管理系统可以加一个JFrame上面放表格JTable显示学生列表下面放输入框和按钮。数据层不用动只改 UI 层。// 简单的 Swing 改造示例 JFrame frame new JFrame(学生管理系统); JTable table new JTable(new StudentTableModel(studentList)); JButton addBtn new JButton(添加); addBtn.addActionListener(e - { // 弹出输入对话框添加学生后刷新表格 String name JOptionPane.showInputDialog(姓名); // ... 省略其他字段 studentList.add(new Student(name, id, score)); table.updateUI(); }); frame.add(new JScrollPane(table), BorderLayout.CENTER); frame.add(addBtn, BorderLayout.SOUTH); frame.setSize(600, 400); frame.setVisible(true);StudentTableModel需要继承AbstractTableModel实现getRowCount、getColumnCount、getValueAt三个方法。这样表格就能自动绑定数据源增删改后调用fireTableDataChanged()刷新。5.2 把数据存储从文件换成数据库实验六用的是文本文件实际项目里更常用数据库。改造步骤加 JDBC 驱动依赖把loadFromFile和saveToFile换成select和insert语句。以 MySQL 为例// 加载驱动JDBC 4.0 之后可以省略 Class.forName(com.mysql.cj.jdbc.Driver); // 获取连接 Connection conn DriverManager.getConnection( jdbc:mysql://localhost:3306/school?useSSLfalseserverTimezoneUTC, root, password ); // 查询 PreparedStatement ps conn.prepareStatement(SELECT * FROM student); ResultSet rs ps.executeQuery(); while (rs.next()) { list.add(new Student(rs.getString(name), rs.getString(id), rs.getDouble(score))); } // 插入 PreparedStatement insert conn.prepareStatement( INSERT INTO student(name, id, score) VALUES(?, ?, ?) ); insert.setString(1, s.getName()); insert.setString(2, s.getId()); insert.setDouble(3, s.getScore()); insert.executeUpdate();用PreparedStatement而不是Statement可以防止 SQL 注入而且参数设置更清晰。连接用完记得关闭或者用连接池比如 HikariCP管理。5.3 加日志和单元测试实验代码里通常用System.out.println打印调试信息实际项目里应该用日志框架。把println换成Loggerprivate static final Logger logger Logger.getLogger(Student.class.getName()); // 替代 System.out.println logger.info(学生信息已加载共 list.size() 条); logger.warning(成绩超出范围 score);单元测试用 JUnit给每个实验的核心方法写测试用例Test public void testSetScoreValid() throws InvalidScoreException { Student s new Student(); s.setScore(85); assertEquals(85, s.getScore(), 0.01); } Test(expected InvalidScoreException.class) public void testSetScoreInvalid() throws InvalidScoreException { Student s new Student(); s.setScore(-10); // 应该抛异常 }5.4 一个我踩过的坑刚开始改实验六的时候我直接把文件读写换成了数据库结果忘了处理SQLException。因为SQLException是 checked exception编译器强制要求捕获我图省事直接在方法签名上throws SQLException一路往上抛最后在main里 catch 了一个寂寞——什么都没做异常被吞了程序出错也不报。后来我养成一个习惯每个catch块里至少打一条日志或者用throw new RuntimeException(e)包装后继续往上抛绝不写空 catch。从那以后我每次改数据层代码都强制走一遍“异常路径测试”——故意传错参数、断开数据库连接看程序是不是能给出有意义的错误提示而不是静默失败。希望这个习惯也能帮到你。本文还有配套的精品资源点击获取