新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java基础练习选择题 3 已整理:用Swing做线程与JDBC错题自测面板

发布时间:2026/10/1 22:19:58来源:尧图网络
Java基础练习选择题 3 已整理:用Swing做线程与JDBC错题自测面板
1. 为什么我把 Java 错题本做成了 Swing 桌面面板刷 Java 基础选择题的时候很多人都有同一个痛点错题散落在纸质卷子、截图、备忘录里线程和 JDBC 这两块尤其容易反复错。线程状态转换、synchronized锁对象、PreparedStatement与Statement的区别、ResultSet游标方法这些知识点一旦混在一起光靠翻答案根本记不住。我试过用 Excel 记录结果每次复盘都要手动筛选也试过在线题库但题目顺序固定错题不能按知识点分组。后来干脆用 Swing 写了一个本地自测面板把线程、JDBC 相关的错题按知识点分组展示答完自动记录结果并统计正确率。整个程序不依赖网络双击就能跑适合放在桌面当日常复盘工具。这个面板能做什么简单说三件事第一从本地文件或数据库读取题目按「线程」「JDBC」「集合」「异常」等标签分组第二用单选按钮渲染选项点击「提交」后立即判断对错并更新统计第三把每次作答结果写入文件下次打开能看到历史正确率。适合谁正在准备 Java 基础考试、面试或者想系统梳理线程与 JDBC 知识点的同学。下面我会给出完整的 Swing 界面骨架、线程安全的答题状态更新写法以及 JDBC 错题数据读取配置。代码可以直接复制运行你只需要准备一个 MySQL 库或者用我提供的本地文件模式。2. 前置准备TaoToken 接入与开发环境配置在开始写 Swing 面板之前先说明一下模型调用部分。这个自测面板本身不依赖大模型但如果你想在答错时自动生成解析可以接入 TaoToken 的 API。TaoToken 是一个兼容 OpenAI 接口规范的模型调用平台官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。接入方式很简单注册后在控制台创建一个 API Key然后在代码里把 Base URL 指向https://taotoken.net/apiModel ID 填你需要的模型名称即可。如果你只是想做本地错题面板这一步可以跳过如果想加「错题解析」功能就继续往下看。开发环境方面你需要 JDK 8 或以上我用的 JDK 17。IDE 用 IntelliJ IDEA 或 Eclipse 都行。数据库部分我用 MySQL 8.0 存错题如果你不想装数据库可以用 SQLite 或者直接读 JSON 文件。下面给出 MySQL 的建表语句和 JDBC 配置。先建一个错题表CREATE TABLE java_quiz ( id INT PRIMARY KEY AUTO_INCREMENT, category VARCHAR(50) NOT NULL, question TEXT NOT NULL, option_a VARCHAR(255), option_b VARCHAR(255), option_c VARCHAR(255), option_d VARCHAR(255), answer CHAR(1) NOT NULL, explanation TEXT ); INSERT INTO java_quiz (category, question, option_a, option_b, option_c, option_d, answer, explanation) VALUES (线程, 线程调用了 sleep() 方法后该线程将进入 状态。, 可运行状态, 运行状态, 阻塞状态, 终止状态, C, sleep() 会让线程进入 TIMED_WAITING 状态属于阻塞的一种。), (线程, 线程控制方法中yield() 的作用是, 返回当前线程的引用, 使比其低的优先级线程执行, 强行终止线程, 只让给同优先级线程运行, D, yield() 让出 CPU但只让给同优先级的线程。), (JDBC, JDBC 中要显式地关闭连接的命令是, Connection.close(), RecordSet.close(), Connection.stop(), Connection.release(), A, 关闭连接用 Connection.close()。), (JDBC, 执行预编译的 SQL用, CallableStatement, Statement, PreparedStatement, CallStatment, C, PreparedStatement 用于预编译 SQL。), (JDBC, 接口 Statement 中定义的 executeQuery 方法返回的类型是, ResultSet, int, boolean, String, A, executeQuery 返回 ResultSet。);如果你用 TaoToken 做解析生成可以在explanation为空时调用模型补全。配置片段如下{ baseUrl: https://taotoken.net/api, apiKey: 你的_API_Key, model: gpt-4o-mini, timeout: 30 }注意API Key 不要硬编码在代码里放到环境变量或本地配置文件避免泄露。TaoToken 的 API Key 可以在控制台的 API Keys 页面创建地址是 https://taotoken.net/api-keys 。3. 可复制配置Swing 界面骨架与线程安全答题状态这一节给出完整的 Swing 界面代码。核心思路是主窗口用BorderLayout左侧是知识点分组列表右侧是题目面板底部是统计栏。答题状态用AtomicInteger和ConcurrentHashMap管理避免多线程更新时出现竞态。先看界面骨架import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; public class QuizPanel extends JFrame { private final JListString categoryList; private final DefaultListModelString categoryModel; private final JPanel questionPanel; private final JLabel statsLabel; private final JButton submitBtn; private final JButton nextBtn; // 线程安全的答题状态 private final ConcurrentHashMapInteger, String userAnswers new ConcurrentHashMap(); private final AtomicInteger correctCount new AtomicInteger(0); private final AtomicInteger totalCount new AtomicInteger(0); private ListQuizItem currentQuestions; private int currentIndex 0; private ButtonGroup optionGroup; public QuizPanel() { setTitle(Java 基础错题自测面板); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(900, 600); setLocationRelativeTo(null); setLayout(new BorderLayout(10, 10)); // 左侧分类列表 categoryModel new DefaultListModel(); categoryModel.addElement(全部); categoryModel.addElement(线程); categoryModel.addElement(JDBC); categoryList new JList(categoryModel); categoryList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); categoryList.setBorder(new EmptyBorder(10, 10, 10, 10)); categoryList.addListSelectionListener(e - { if (!e.getValueIsAdjusting()) { loadQuestions(categoryList.getSelectedValue()); } }); JScrollPane leftScroll new JScrollPane(categoryList); leftScroll.setPreferredSize(new Dimension(150, 0)); add(leftScroll, BorderLayout.WEST); // 右侧题目区 questionPanel new JPanel(); questionPanel.setLayout(new BoxLayout(questionPanel, BoxLayout.Y_AXIS)); questionPanel.setBorder(new EmptyBorder(20, 20, 20, 20)); add(new JScrollPane(questionPanel), BorderLayout.CENTER); // 底部统计与按钮 JPanel bottomPanel new JPanel(new BorderLayout()); statsLabel new JLabel(正确率0 / 0); statsLabel.setBorder(new EmptyBorder(5, 10, 5, 10)); bottomPanel.add(statsLabel, BorderLayout.WEST); JPanel btnPanel new JPanel(new FlowLayout(FlowLayout.RIGHT)); submitBtn new JButton(提交答案); nextBtn new JButton(下一题); btnPanel.add(submitBtn); btnPanel.add(nextBtn); bottomPanel.add(btnPanel, BorderLayout.EAST); add(bottomPanel, BorderLayout.SOUTH); // 事件绑定 submitBtn.addActionListener(e - submitAnswer()); nextBtn.addActionListener(e - nextQuestion()); // 默认加载全部 categoryList.setSelectedIndex(0); } private void loadQuestions(String category) { // 这里从数据库或文件读取后面 JDBC 部分会实现 currentQuestions QuizRepository.findByCategory(category); currentIndex 0; userAnswers.clear(); correctCount.set(0); totalCount.set(0); renderQuestion(); } private void renderQuestion() { questionPanel.removeAll(); if (currentQuestions null || currentQuestions.isEmpty()) { questionPanel.add(new JLabel(暂无题目)); questionPanel.revalidate(); questionPanel.repaint(); return; } QuizItem item currentQuestions.get(currentIndex); JLabel qLabel new JLabel(htmlb (currentIndex 1) . item.getQuestion() /b/html); qLabel.setAlignmentX(Component.LEFT_ALIGNMENT); questionPanel.add(qLabel); questionPanel.add(Box.createVerticalStrut(10)); optionGroup new ButtonGroup(); String[] options {item.getOptionA(), item.getOptionB(), item.getOptionC(), item.getOptionD()}; char[] labels {A, B, C, D}; for (int i 0; i options.length; i) { JRadioButton rb new JRadioButton(labels[i] . options[i]); rb.setActionCommand(String.valueOf(labels[i])); rb.setAlignmentX(Component.LEFT_ALIGNMENT); optionGroup.add(rb); questionPanel.add(rb); } questionPanel.add(Box.createVerticalStrut(10)); JLabel resultLabel new JLabel( ); resultLabel.setName(resultLabel); resultLabel.setAlignmentX(Component.LEFT_ALIGNMENT); questionPanel.add(resultLabel); questionPanel.revalidate(); questionPanel.repaint(); updateStats(); } private void submitAnswer() { if (optionGroup null || optionGroup.getSelection() null) { JOptionPane.showMessageDialog(this, 请先选择一个答案); return; } QuizItem item currentQuestions.get(currentIndex); String selected optionGroup.getSelection().getActionCommand(); userAnswers.put(item.getId(), selected); totalCount.incrementAndGet(); if (selected.equals(item.getAnswer())) { correctCount.incrementAndGet(); } // 显示结果 for (Component c : questionPanel.getComponents()) { if (c instanceof JLabel resultLabel.equals(c.getName())) { boolean ok selected.equals(item.getAnswer()); ((JLabel) c).setText(ok ? 回答正确 : 回答错误正确答案 item.getAnswer()); ((JLabel) c).setForeground(ok ? new Color(0, 128, 0) : Color.RED); } } updateStats(); } private void nextQuestion() { if (currentQuestions null || currentQuestions.isEmpty()) return; if (currentIndex currentQuestions.size() - 1) { currentIndex; renderQuestion(); } else { JOptionPane.showMessageDialog(this, 已经是最后一题了); } } private void updateStats() { int total totalCount.get(); int correct correctCount.get(); double rate total 0 ? 0 : (correct * 100.0 / total); statsLabel.setText(String.format(正确率%d / %d (%.1f%%), correct, total, rate)); } public static void main(String[] args) { SwingUtilities.invokeLater(() - new QuizPanel().setVisible(true)); } }上面代码里ConcurrentHashMap和AtomicInteger保证了即使你在后台线程更新答题状态界面统计也不会错乱。SwingUtilities.invokeLater确保界面在事件调度线程中创建。接下来是数据模型和仓库类public class QuizItem { private int id; private String category; private String question; private String optionA; private String optionB; private String optionC; private String optionD; private String answer; private String explanation; // getter/setter 省略实际使用时补全 public int getId() { return id; } public String getCategory() { return category; } public String getQuestion() { return question; } public String getOptionA() { return optionA; } public String getOptionB() { return optionB; } public String getOptionC() { return optionC; } public String getOptionD() { return optionD; } public String getAnswer() { return answer; } public String getExplanation() { return explanation; } }JDBC 读取配置import java.sql.*; import java.util.ArrayList; import java.util.List; public class QuizRepository { private static final String URL jdbc:mysql://localhost:3306/java_quiz?useSSLfalseserverTimezoneUTC; private static final String USER root; private static final String PASSWORD 你的密码; public static ListQuizItem findByCategory(String category) { ListQuizItem list new ArrayList(); String sql 全部.equals(category) ? SELECT * FROM java_quiz : SELECT * FROM java_quiz WHERE category ?; try (Connection conn DriverManager.getConnection(URL, USER, PASSWORD); PreparedStatement ps conn.prepareStatement(sql)) { if (!全部.equals(category)) { ps.setString(1, category); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { QuizItem item new QuizItem(); item.setId(rs.getInt(id)); item.setCategory(rs.getString(category)); item.setQuestion(rs.getString(question)); item.setOptionA(rs.getString(option_a)); item.setOptionB(rs.getString(option_b)); item.setOptionC(rs.getString(option_c)); item.setOptionD(rs.getString(option_d)); item.setAnswer(rs.getString(answer)); item.setExplanation(rs.getString(explanation)); list.add(item); } } } catch (SQLException e) { e.printStackTrace(); } return list; } }如果你用 TaoToken 生成解析可以在explanation为空时调用public static String generateExplanation(String question, String answer) { // 调用 TaoToken APIBase URL: https://taotoken.net/api // 具体 HTTP 调用代码略参考官方文档 return 解析生成中...; }注意JDBC 连接串里的useSSLfalse和serverTimezoneUTC根据你的 MySQL 版本调整。MySQL 8.0 需要加allowPublicKeyRetrievaltrue如果遇到公钥检索错误。4. 验证请求运行面板并确认题目加载与正确率统计代码写完后按以下步骤验证。第一步确认 MySQL 服务已启动并且java_quiz库和java_quiz表已创建。执行SELECT COUNT(*) FROM java_quiz;应该能看到至少 5 条记录。第二步把 MySQL 驱动 jar 包加入项目依赖。Maven 项目加dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency非 Maven 项目就手动下载 jar 并添加到 classpath。第三步运行QuizPanel.main()。窗口应该正常弹出左侧显示「全部」「线程」「JDBC」三个分类。点击「线程」右侧应该加载出两道线程题点击「JDBC」加载出三道 JDBC 题。第四步选择一道题点一个选项点「提交答案」。底部统计栏应该从「正确率0 / 0」变成「正确率1 / 1 (100.0%)」或「正确率0 / 1 (0.0%)」同时题目下方显示对错提示。第五步点「下一题」切换到下一道题选项重置结果标签清空。答完所有题后统计栏显示总正确率。如果你接入 TaoToken 做解析可以在提交后调用模型接口把返回的解析文本追加到结果标签下方。验证时注意 API Key 是否正确Base URL 是否为https://taotoken.net/api。模型对话功能可以在 https://taotoken.net/model-chat 测试确认 Key 有效后再集成到代码里。实测下来整个面板从启动到加载题目大约 1 秒切换分类无卡顿。如果你题目量很大比如上千道建议在loadQuestions里加一个后台线程用SwingWorker异步加载避免界面冻结。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节整理我在开发和接入过程中遇到的真实报错以及排查方法。报错一401 Unauthorized如果你调用 TaoToken API 时返回 401说明 API Key 无效或未正确传递。检查请求头connection.setRequestProperty(Authorization, Bearer apiKey);确认apiKey没有多余空格并且是在 https://taotoken.net/api-keys 创建的。如果 Key 刚创建等几秒再试。报错二local proxy failed这个报错通常出现在你本地设置了代理但代理不可用。检查系统环境变量HTTP_PROXY和HTTPS_PROXY如果不需要代理就清空。Java 代码里可以用System.setProperty(http.proxyHost, ); System.setProperty(http.proxyPort, );或者直接在 JVM 启动参数加-Dhttp.proxyHost -Dhttp.proxyPort。报错三reading choices这个报错一般出现在解析模型返回的 JSON 时字段名不匹配。比如你期望choices[0].message.content但实际返回结构不同。建议先打印完整响应体System.out.println(responseBody);确认字段路径后再解析。TaoToken 兼容 OpenAI 格式正常返回应该包含choices数组。报错四OAuth 相关错误如果你用 OAuth 方式接入报错可能是 token 过期或 scope 不足。检查 token 有效期重新生成。如果只是本地自测面板建议直接用 API Key不用 OAuth。报错五JDBC 连接失败Communications link failure说明 MySQL 没启动或端口不对。Access denied for user说明用户名密码错误。Unknown database说明库没建。逐个排查即可。报错六Swing 界面乱码如果中文显示为方块在 JVM 启动参数加-Dfile.encodingUTF-8或者用new JLabel(new String(中文.getBytes(UTF-8), UTF-8))。推荐统一用 UTF-8 编码。报错七线程安全统计错误如果你在后台线程直接更新JLabel可能抛NullPointerException或界面不刷新。所有界面更新必须放在SwingUtilities.invokeLater里SwingUtilities.invokeLater(() - statsLabel.setText(...));记住Swing 组件不是线程安全的只有事件调度线程能操作它们。6. 下一步把错题面板接入 Coding Plan 做长期复盘这个 Swing 面板已经能完成基本的错题分组、作答和统计。如果你想进一步自动化比如每天自动拉取新题、生成解析、记录历史趋势可以接入 TaoToken 的 Coding Plan。Coding Plan 适合长期编码和 Agent 场景地址是 https://taotoken.net/coding-plan 。具体做法写一个定时任务每天从数据库读取未掌握的错题调用模型生成解析然后把解析写回explanation字段。下次打开面板时错题旁边就会显示详细解析。模型调用部分参考接入文档 https://taotoken.net/doc 里面有完整的请求示例和参数说明。如果你更想先验证模型效果可以到模型对话页面 https://taotoken.net/model-chat 手动测试几道线程和 JDBC 题目确认解析质量后再集成。控制台 https://taotoken.net/console 可以查看调用量和余额。最后提醒一点错题面板的核心价值在于「反复练」而不是「一次做完」。建议把答错的题目标记为「待复习」下次打开时优先加载这些题。你可以在java_quiz表加一个wrong_count字段每次答错就加一加载时按wrong_count DESC排序。这样坚持一周线程和 JDBC 的高频错点自然就攻克了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

VulnHub靶机Bulldog完整渗透实战:从信息收集到Root提权 2026/10/1 23:16:12

VulnHub靶机Bulldog完整渗透实战:从信息收集到Root提权

靶机渗透这个圈子,玩到一定阶段都会有个感觉:光是看writeup、刷题库,不如老老实实拿一个靶机从信息收集打到提权,整个链路走一遍,比什么都长记性。最近我重新把VulnHub上的Bulldog拖出来打了一遍,这靶机难度…

阅读更多 →
Vivado增量实现:原理、复用报告与Tcl/GUI避坑指南 2026/10/1 23:16:05

Vivado增量实现:原理、复用报告与Tcl/GUI避坑指南

很多人第一次接触 Vivado 的效率问题,都是从"改一行代码等四十分钟"开始的。工程里只是把某个状态机的优先级调了一下,或者把一个计数器的位宽从 16 改成 20,明明改动量小到可以忽略,但impl_1一跑就是半小时起步&#x…

阅读更多 →
Python企业微信机器人开发实战:Webhook消息推送与自动回复 2026/10/1 23:16:05

Python企业微信机器人开发实战:Webhook消息推送与自动回复

1. 整体设计与思路拆解1.1 项目要解决什么问题微信机器人听起来很高大上,但其实用 Python 做这件事已经是非常成熟的技术路线了。我第一次接触这个需求,是帮朋友做一个自动回复脚本——他想在忙的时候让微信自动回复客户消息。当时第一反应是&#xff1a…

阅读更多 →
从线性DP到01背包:动态规划模型跃迁与刷题路线 2026/10/1 23:16:05

从线性DP到01背包:动态规划模型跃迁与刷题路线

如果你正在刷动态规划题单,做到某一篇时突然卡住,大概率卡在一个地方:不是看不懂状态转移方程,而是不知道这个状态为什么要这么定义。尤其当你做到“动态规划8”这个阶段,前面已经写过不少基础题,但一遇到线…

阅读更多 →
技术人认知破局:从思维天花板到系统化掌控职业成长 2026/10/1 23:16:05

技术人认知破局:从思维天花板到系统化掌控职业成长

干技术这一行超过十年,我见过太多有趣的悖论:一个人可以熟练地拆解最复杂的分布式系统瓶颈,却说不清自己为什么在职业的十字路口反复徘徊;可以在代码评审时一针见血地指出逻辑漏洞,却在面对“要不要离开舒适区”“该不…

阅读更多 →
小米便签深度指南:系统级笔记、本地优先同步与待办工作流 2026/10/1 23:16:05

小米便签深度指南:系统级笔记、本地优先同步与待办工作流

我换过四台小米手机,前三次换机后第一件事都是去应用商店找第三方笔记软件,第四次才发现系统里那个叫 小米便签 的东西已经悄悄长成了另一副样子——它不再是当年那个只能打几行字的记事本,而是一套带待办、清单、涂鸦、思维导图、标签体系…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉