一、拼图小游戏
1. Java Swing 图形界面 中处理「鼠标点击事件」的逻辑
@Override
public void mouseClicked(MouseEvent e) {if (e.getSource() == submit) { // 不太理解if (username.getText().length() == 0 || password.getText().length() == 0 || rePassword.getText().length() == 0) {showDialog("用户名和密码不能为空");return; // 不太理解}if (!password.getText().equals(rePassword.getText())) {showDialog("两次密码输入不一致");return;}if (!username.getText().matches("[a-zA-Z0-9]{4,16}")) {showDialog("用户名不符合规则");return;}if (!password.getText().matches("\\S*(?=\\S{6,})(?=\\S*\\d)(?=\\S*[a-z])\\S*")) {showDialog("密码不符合规则,至少包含一个小写字母,一个数字,长度至少6位");return;}if (containUsername(username.getText())) {showDialog("用户名已经存在,请重新输入");return;}allUsers.add(new User(username.getText(),password.getText()));FileUtil.writeLines(allUsers,"D:\\张锐彬\\Java\\01-登录注册学生练习代码\\puzzlegame\\puzzlegame\\userinfo.txt","UTF-8");showDialog("注册成功");this.setVisible(false); // 不太理解new LoginJFrame();} else if (e.getSource() == reset) {username.setText("");password.setText("");rePassword.setText("");}
}
关键逻辑 1:if (e.getSource() == submit)
if (e.getSource() == submit) { ... } else if (e.getSource() == reset) { ... }
e:是 MouseEvent 类型的对象,代表「鼠标点击事件」的所有信息(比如点击的位置、点击的组件等)。e.getSource():获取「触发这次鼠标点击事件的组件」(比如用户点击了「提交」按钮,就返回这个按钮对象;点击「重置」按钮,就返回重置按钮对象)。submit 和 reset:是你在界面中定义的两个按钮对象(比如 JButton submit = new JButton("提交");)。- 整个判断的意思:判断用户点击的是「提交」按钮还是「重置」按钮,分别执行不同逻辑。
关键逻辑 2: 注册校验:非空判断 + return
if (username.getText().length() == 0 || password.getText().length() == 0 || rePassword.getText().length() == 0) {showDialog("用户名和密码不能为空");return; // 不太理解
}
username、password、rePassword:是界面中的输入框组件(比如 JTextField username = new JTextField();)。getText():获取输入框中用户输入的文本。length() == 0:判断输入框是否为空(文本长度为 0)。showDialog(...):自定义方法(推测是弹出一个提示框,显示错误信息)。- 关键:
return 的作用:一旦发现「用户名 / 密码为空」,弹出提示后,立即终止当前方法的执行(后面的密码一致性校验、格式校验等逻辑都不会再执行)。为什么要加 return?比如用户没输入内容就点提交,此时已经不符合注册条件,再执行后面的逻辑毫无意义,还可能导致错误(比如空字符串的格式校验失败),所以直接返回,节省资源。
关键逻辑 3:注册成功后的逻辑
// 1. 将新用户添加到内存中的用户列表(allUsers 是自定义的集合,比如 List<User>)
allUsers.add(new User(username.getText(), password.getText()));// 2. 将用户列表写入本地文件(FileUtil 是自定义工具类,用于文件操作)
FileUtil.writeLines(allUsers, "D:\\...\\userinfo.txt", "UTF-8");// 3. 弹出注册成功提示
showDialog("注册成功");// 4. 隐藏当前注册窗口(this 指代当前的注册界面,比如 RegisterJFrame)
this.setVisible(false); // 不太理解// 5. 创建并显示登录窗口(LoginJFrame 是登录界面的类)
new LoginJFrame();
- 关键:
this.setVisible(false) 的作用:this 指代「当前的注册窗口对象」(因为这个方法所在的类是注册窗口类,比如 RegisterJFrame)。setVisible(false) 是 Swing 组件的方法,意思是「让当前窗口隐藏」(不再显示在屏幕上)。为什么要隐藏?注册成功后,用户需要跳转到「登录界面」,所以先把注册窗口关掉(隐藏),再创建登录窗口并显示。
关键逻辑 5:重置按钮的逻辑
else if (e.getSource() == reset) {username.setText(""); // 清空用户名输入框password.setText(""); // 清空密码输入框rePassword.setText(""); // 清空重复密码输入框
}
- 当用户点击「重置」按钮时,调用输入框的
setText("") 方法,将输入框中的文本设为空字符串,实现「清空输入」的功能。
2. 游戏存档和读档的功能
// 存档功能
else if(obj == saveItem0 || obj == saveItem1 || obj == saveItem2 || obj == saveItem3 ||obj == saveItem4){System.out.println("存档");JMenuItem item = (JMenuItem) obj; // 不太理解String str = item.getText();int index = str.charAt(2) - '0'; // 不太理解try {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\张锐彬\\Java\\02-游戏存档和读档学生练习代码\\puzzlegame\\puzzlegame\\save\\save" + index + ".txt"));GameInfo gi = new GameInfo(data,x,y,path,step); // 不太理解IoUtil.writeObj(oos,true,gi); // 不太理解} catch (IOException ex) {throw new RuntimeException(ex);}item.setText("存档" + index + "(" + step + "步)"); // 不太理解loadJMenu.getItem(index).setText("存档" + index + "(" + step + "步)");}
// 读档功能
else if(obj == loadItem0 || obj == loadItem1 || obj == loadItem2 || obj == loadItem3 ||obj == loadItem4){JMenuItem item = (JMenuItem) obj;String str = item.getText();int index = str.charAt(2) - '0';GameInfo gi = null;try {ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\张锐彬\\Java\\02-游戏存档和读档学生练习代码\\puzzlegame\\puzzlegame\\save\\save" + index + ".txt"));gi = (GameInfo)ois.readObject(); // 不太理解ois.close();} catch (IOException ex) {throw new RuntimeException(ex);} catch (ClassNotFoundException ex) {throw new RuntimeException(ex);}data = gi.getData(); // 不太理解path = gi.getPath();step = gi.getStep();x = gi.getX();y = gi.getY();initImage();
}
关键逻辑 1:
(1) 存档功能 (else if 块)
关键逻辑 1:else if(obj == saveItem0 || obj == saveItem1 || ...)
- 这是一个条件判断,用来识别用户点击的是哪个菜单项。
obj 是触发事件的组件对象,这里就是被点击的菜单项。- 整个判断的意思是:如果用户点击的是
saveItem0 到 saveItem4 这五个菜单项中的任意一个,就执行下面的存档逻辑。
关键逻辑 2:JMenuItem item = (JMenuItem) obj;
- 核心目的:我们需要调用这个菜单项的特定方法(如
getText() 和 setText()),但 obj 的类型是 Object,编译器不知道它具体是 JMenuItem 类型。 (JMenuItem):这是强制类型转换。我们明确告诉编译器:“我知道 obj 实际上是一个 JMenuItem 对象,请把它当作 JMenuItem 来处理”。
关键逻辑 3:int index = str.charAt(2) - '0';
- 这是一个从字符串中提取数字的常用技巧。
str.charAt(2):获取字符串 str 中索引为 2 的字符。对于 "存档0(空)",这个字符就是 '0'。'0':在 Java 中,字符用单引号表示。这里的关键是,字符 '0' 到 '9' 在计算机内部是按顺序存储的。str.charAt(2) - '0':这是一个数学运算。计算机会使用字符的 ASCII 码值来计算。例如,'3' - '0' 的结果是整数 3。- 最终目的:通过这种方式,我们从菜单项文本中提取出了存档的编号(0 到 4),并将其存储在整数变量
index 中。
关键逻辑 4:ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("...save" + index + ".txt"));
- 这行代码创建了一个用于对象序列化的输出流。
new FileOutputStream("..."):创建一个文件输出流,指定了文件保存的路径和文件名。"save" + index + ".txt" 会动态生成文件名,如 save0.txt, save1.txt 等。new ObjectOutputStream(...):将文件输出流包装成对象输出流。ObjectOutputStream 可以将 Java 对象(如我们的游戏状态)转换成字节流,以便写入文件。
关键逻辑 5:GameInfo gi = new GameInfo(data, x, y, path, step);
GameInfo 是一个你自定义的类,它的作用就像一个数据容器或快照。- 这行代码创建了一个
GameInfo 对象,并将当前游戏的所有关键状态(data 数组、空白块位置 x,y、图片路径 path、步数 step)作为参数传递给它的构造函数。 - 这样,
gi 对象就完整地记录了游戏在这一刻的状态。
关键逻辑 6:item.setText("存档" + index + "(" + step + "步)");
- 存档成功后,更新当前点击的存档菜单项的显示文本。
- 例如,将 “存档 0 (空)” 更新为 “存档 0 (15 步)”。
- 目的:给用户一个清晰的视觉反馈,让用户知道该存档位已经使用,并且记录了游戏进行到第几步。
(2) 读档功能
关键逻辑 1:ObjectInputStream ois = new ObjectInputStream(new FileInputStream("...save" + index + ".txt"));
- 这行代码创建了一个用于对象反序列化的输入流。
new FileInputStream("..."):创建一个文件输入流,用于读取指定的存档文件。new ObjectInputStream(...):将文件输入流包装成对象输入流。ObjectInputStream 可以从字节流中读取数据,并将其还原成一个 Java 对象。
关键逻辑 2:gi = (GameInfo) ois.readObject();
ois.readObject():从输入流中读取一个对象。这个方法返回的是一个 Object 类型的引用。(GameInfo):因为我们知道文件里存储的是一个 GameInfo 对象,所以需要进行强制类型转换,将读取到的 Object 转换成 GameInfo 类型。