1Z0-829 题库实战:用 JDK 17 跑代码、建错题台账、三遍冲刺
简介面向准备 OCP Java SE 17 开发者认证1Z0-829的考生这份材料是 Jeanne Boyarsky 与 Scott Selikoff 合著的官方练习测试手册电子版用于在正式考试前进行高强度的刷题与查漏补缺。全书围绕考试涉及的语言特性、模块化、并发、流与异常处理等主题组织大量模拟题并给出解析与复习策略适合已有一定 Java 基础、希望以练代学的中高级学习者。压缩包共 1 个文件为单个 PDF体积约 20.96MB可直接用阅读器打开检索便于按章节定位题目与答案解析。目前已有 268 人学习说明该版本在备考群体中已有一定认可度。读者能获得贴近真题风格的成套模拟测试、逐题解析思路以及针对易错点的复习建议既能用于考前限时自测也可作为日常知识点梳理的对照材料帮助建立清晰的答题节奏与知识盲区清单。1. 一份 1Z0-829 练习题库值不值得花两周刷完刷题能不能过 Java SE 17 认证考试取决于刷的是哪一种题。如果只是一遍遍点选项、看一眼对错模拟测试做十套也未必涨分真正起作用的是把每道错题还原成一段能跑的代码让编译器和运行时替你判卷。这份实战练习题库的价值就在这儿题目贴着考纲主线解析落到具体语言规则配套的复习策略可以拆成按周执行的清单。它适合写过一两年 Java、打算两三个月内拿下 1Z0-829 的在职开发者也适合刚学完语法、想用题目反推知识盲区的新手。不适合指望背答案蒙过去的人因为干扰项设计得很贴近真实语法边界靠印象做题会反复栽在同一处。2. 题纲拆解把 1Z0-829 的考点映射成可勾选的清单题库最容易用错的地方是把它当普通卷子从头做到尾。这本练习册的结构其实是「题目 解析 考点回收」题与题之间并不按难度递进而是按考纲域聚簇。所以第一遍不要追求正确率先把题目拆成能勾选的考点条目后面才知道自己是在补哪块。2.1 三种题型对应三种不同的判卷方式题库里的题大致分三类每类的验证成本完全不同。第一类是语法判断题给你一段代码问能否编译、输出什么。这类必须落到本机javac里跑一遍因为很多错误只在编译期暴露脑子模拟容易漏。第二类是 API 行为题问某个方法返回什么、异常从哪抛出这类要做对照实验同一份输入换两种调用方式看差异。第三类是设计原则题比如接口与抽象类的取舍、模块导出规则这类没法跑代码只能靠解析里的规则条目建立判断树。我的做法是给三类标不同颜色能跑的题标红对照实验题标黄纯规则题标蓝。红题优先级最高因为它们是唯一能通过机器给出确定答案的部分也是错题台账里最有价值的数据。提示遇到「下列哪项正确」的多选题不要只看正确答案的解析把每个错误选项为什么错单独记一行。1Z0-829 的干扰项往往只差一个关键字比如final与effectively final、与equals、var能否用于成员变量。2.2 考纲域与题目分布的对应关系下表是我按反复出现的题目归纳的体感分布不是官方给出的比例只用来决定时间怎么分配。考纲域出现密度典型题型最容易错的点控制流与异常中try-with-resources、自定义异常资源关闭顺序、catch 顺序面向对象与设计高继承、接口默认方法、内部类静态上下文访问、重写规则泛型与集合高通配符、排序、Map 合并? super与? extends方向Stream 与 Optional高惰性求值、Collectors 分组中间操作未执行、短路日期时间与格式化中时区、Period 与 Duration不可变对象被忽略返回值并发中高线程池、并发集合、并行流异常被 Future 吞掉I/O 与 NIO.2中Path 解析、文件遍历绝对路径覆盖 resolve 参数JDBC中Connection、ResultSet、事务自动提交与资源关闭模块系统低但必考module-info、jdepsrequires transitive 的影响新语法record、sealed、文本块中构造器约束、穷尽性紧凑构造器赋值位置盯着这张表做计划比按题号顺序做有效得多。密度高的域先集中打密度低但必考的域用碎片时间过规则。2.3 第一轮怎么刷诊断卷、分桶、标记第一轮建议只用两天目标不是学知识是建索引。先从题库里取一套完整的模拟测试限时做完按真实考试节奏中途不查资料。做完只做一件事把错题按上表的域分桶算出每个域的错题数。这一步的意义在于你可能以为自己并发最弱结果数据显示是泛型通配符。分桶之后给每个域打三个标签规则不清解析看了还是不懂、手滑懂但选错、没见过题库里第一次出现的 API。手滑的题不进错题本只在台账里记一笔因为重复看它们会浪费时间。规则不清和没见过才是后面几轮的重点。第二轮开始按域推进一个域做完立刻建对应的验证工程目录目录名就用域编号。这样两周后回头看你能从目录结构直接看出自己补过哪些域而不是翻聊天记录找当时的结论。3. 把题库跑成工程JDK 17 环境、单文件验证与错题台账题库的价值在于解析但解析是别人写的结论只有自己跑一遍才会变成判断力。这一章把「做题」变成「做实验」需要的东西不多一个 JDK 17、一个目录、一个统计脚本。3.1 先把语言级别钉死在 171Z0-829 的题目默认按 Java 17 的语言级别判卷本机装成 21 或者更老的 8都会出现「我这儿能编译解析说不能」的情况。# 确认本机 JDK 版本必须是 17.x java -version javac -version # 如果有多个版本显式指定 JAVA_HOME路径按本机实际情况改 export JAVA_HOME/path/to/jdk-17 export PATH$JAVA_HOME/bin:$PATH # 再验证一次输出里应出现 17 javac -versionjava -version给的是运行时版本javac -version给的是编译器版本两个都要看。有些机器上 JRE 和 JDK 是分开装的java指向 17 而javac还停在旧版本这时候编译带record、sealed的题会直接报语法错而你会误判成「这题超纲」。版本钉死之后验证单个类的编译行为要用显式的语言级别参数避免默认值带来的差异# 强制按 17 的语言级别编译输出到临时目录 javac --release 17 -d out Q_Record_042.java # 想看编译器给出的完整上下文把 lint 打开 javac -Xlint:all --release 17 -d out Q_Record_042.java--release 17比-source 17 -target 17更严格它会同时约束可用的 API 集合防止你在 17 的语言级别下误用了更高版本的类库——虽然本机是 17 时差别不大但养成这个习惯换机器做题不会翻车。3.2 单文件源码启动一道错题一个文件从 JDK 11 开始可以直接运行单文件源码不需要先javac。刷题时这个特性非常省事每道需要验证的题存成一个.java文件文件名带题号跑完就留着当笔记。# 建立错题工程目录按考纲域分桶 mkdir -p ~/ocp17/{streams,concurrency,generics,io,jdbc,modules} cd ~/ocp17/streams # 单文件直接运行Java 会先隐式编译再执行 java Q_Stream_017.java目录按域划分而不是按题号顺序是因为复习时你会按弱项找文件不会按题号找。文件名带上题号方便回题库对照解析。单文件模式有个边界要知道它只对「一个文件里一个 public 类」的场景生效如果你的验证代码需要多个顶层类协作或者要引用同目录下另一个文件里的类就得回到javacjava的两步流程。题目里出现的内部类、嵌套类不受影响它们本来就在同一个文件里。遇到需要预览特性才能验证的语法编译和运行都要加参数两边必须一致# 预览特性需要编译期和运行期同时开启 java --enable-preview --release 17 Q_Switch_088.java这里有个血泪经验模式匹配的switch在 17 上是预览特性题库里如果有相关题目先确认考纲是否把它列入范围。我的做法是这类题只做规则记忆不深挖因为正式考试不会考预览语法把它们当扩展阅读别占主线时间。3.3 错题台账让正确率变成可算的数做完一轮如果只有「感觉错挺多」这种印象第二轮就没有靶子。用一个 CSV 记录每道错题再用脚本聚合弱项会自己浮出来。台账的列固定为五列题号、考纲域、错因标签、是否已复现、复现结论。错因标签只用三个值规则不清 / 手滑 / 没见过复用第 2 章的分桶口径。import csv import collections # 读取错题台账统计各考纲域的错题数与复现率 def summarize(path): by_domain collections.defaultdict(lambda: {total: 0, reproduced: 0}) by_reason collections.Counter() with open(path, newline, encodingutf-8) as f: for row in csv.DictReader(f): domain row[domain].strip() by_domain[domain][total] 1 # 复现列填 y 表示已经在本机跑通并确认结论 if row[reproduced].strip().lower() y: by_domain[domain][reproduced] 1 by_reason[row[reason].strip()] 1 for domain, stat in sorted(by_domain.items(), keylambda kv: -kv[1][total]): rate stat[reproduced] / stat[total] * 100 print(f{domain:12s} 错题 {stat[total]:3d} 已复现 {rate:5.1f}%) print(- * 40) for reason, cnt in by_reason.most_common(): print(f{reason}: {cnt}) if __name__ __main__: summarize(wrong_answers.csv)collections.defaultdict这里用来给每个域自动建计数器省掉「判断 key 是否存在」的分支sorted的key用负的错题数实现降序这样最弱的域排在最上面。reproduced列是关键它把「看过解析」和「亲自跑过」区分开——只填y的那些题才是你真的掌握了的。输出里如果某个域的复现率低于 50%说明你在这个域只停留在读解析第二轮必须补实验。如果手滑的计数超过总错题的三分之一那问题不在知识而在做题节奏要去调时间分配而不是继续加练。注意台账不要记正确答案。记了答案下次翻台账会不自觉地「认题」统计就失真了。只记错因和复现结论答案留在题库里。4. 避坑刷这份题库时最容易翻车的五件事4.1 现象本机跑出来的结果和解析对不上原因通常不是解析错而是环境不对。最常见的是 JDK 版本漂移——用 21 跑 17 的题某些 API 行为变了其次是字符编码题目里带中文或特殊符号终端默认编码不同导致输出比对失败还有一种是你复制代码时把全角空格带进去了编译器报的错和题目想问的完全不是一回事。解决方法是把环境检查做成固定动作每次开新终端先跑javac -version确认是 17跑带输出的题时统一加-Dfile.encodingUTF-8复制代码后先看缩进是否被编辑器改过。这三步做完还对不上再去怀疑解析。4.2 现象一道题看解析懂了隔一周重做还是错原因是「读懂」和「能判断」是两件事。解析给的是结论链条你在读的时候顺着链条走当然通顺但考试时没有链条只有四个选项。这类题属于典型的规则不清伪装成手滑。解决方式是合上解析用自己的话把规则写成一句话再去找一道同域的题验证这句话。比如? extends只能读不能写写成「生产者用 extends消费者用 super」然后立刻去做一道List? super Integer的题。写不出来或者验证失败说明规则没进脑子回去重读。4.3 现象错题本越记越厚正确率不涨原因是把「手滑题」也收进去了。手滑题的特征是看解析的瞬间你就知道自己错在哪而且不需要任何新知识。这类题重复看只会制造「我错了好多」的焦虑不产生任何信息增量。解决办法是给台账加一道闸错因标手滑的题只在台账留一行记录不进错题本、不做第二遍。如果同一个知识点连续三次标手滑那它就不是手滑改标规则不清回去补基础。4.4 现象并发和 Stream 的题本地跑十次结果都不一样原因是你验证的代码本身有不确定性。并发题里线程调度顺序不固定Stream 并行流的切分策略也依赖运行时这些题的「标准输出」本来就不是唯一值拿单次运行结果去对答案必然翻车。解决办法是改验证目标不问「输出是什么」改问「有几种可能的输出」。把代码跑二十次收集所有出现过的输出再去和解析对照。并发题真正考的是「哪些结果是合法的」你只要证明某个选项不可能出现就能排除它。4.5 现象模块系统的题完全跑不起来原因是模块题的验证方式和普通题不一样module-info.java必须放在源码根目录编译时要给--module-source-path运行时还要--module-path。很多人把module-info.java和普通类放在同一层编译器直接报「模块声明必须在模块的根目录」。解决办法是给模块题单独建目录结构按src/模块名/module-info.java的层级摆放# 模块题的目录必须严格分层 mkdir -p ~/ocp17/modules/demo/src/app.demo/com/example # 编译时指明模块源路径 javac --module-source-path src -d out $(find src -name *.java) # 运行时指明模块路径和入口模块 java --module-path out -m app.demo/com.example.Main跑通一次这个流程模块相关的大部分选择题就变成送分题了因为选项里那些「缺exports」「少requires」的错误你都在编译期见过。5. 用最小可运行代码验证题目结论五个高频争点题库里争议最大的五类题光靠读解析很难建立稳定判断。这一章给每类准备一个十行以内的小程序跑一遍比读十遍解析管用。5.1 文本块缩进到底被剥掉了多少public class Q_TextBlock_031 { public static void main(String[] args) { // 三个引号后面必须换行内容从下一行开始 String s alpha beta gamma ; System.out.println(行数: s.lines().count()); System.out.println(含两空格缩进: s.contains( beta)); System.out.println(以换行结尾: s.endsWith(\n)); } }运行结果是 3 行、包含两空格缩进、以换行结尾。lines()按换行切分且不计末尾空行所以三行内容得 3。文本块的缩进剥离规则是「以所有非空行中最小的公共缩进为准」这里gamma那行的缩进最小beta相对多出的两格被保留。末尾那个换行是文本块的固有部分不是多余字符。这三行输出覆盖了文本块题目的绝大部分问法行数、缩进、结尾换行。把这三点记住剩下的是个别转义细节比如\行尾续行符会吞掉换行。5.2 record紧凑构造器里到底能不能赋值public record Range(int lo, int hi) { // 紧凑构造器形参列表省略末尾隐式给字段赋值 public Range { if (lo hi) { throw new IllegalArgumentException(lo hi); } // 这里给 lo/hi 重新赋值是允许的会传给隐式赋值 lo Math.max(lo, 0); } }关键点有两个。紧凑构造器不能写this.lo lo那是普通构造器的写法在这里会编译报错但可以给形参lo重新赋值隐式赋值发生在构造器体之后拿的是改过的值。题目常在这里设陷阱给字段赋值报错给形参赋值合法。另外要记住 record 的隐式成员equals、hashCode、toString都是按组件生成的但你可以显式重写其中任意一个。重写equals时如果没同步重写hashCode编译不报错但语义错这类题靠读代码容易漏。5.3 sealed 与穷尽性为什么有的 switch 不需要 defaultsealed interface Shape permits Circle, Square {} record Circle(double r) implements Shape {} record Square(double a) implements Shape {} class Area { // 对 sealed 类型做 switch子类型穷尽时可以省略 default static double of(Shape s) { return switch (s) { case Circle c - Math.PI * c.r() * c.r(); case Square q - q.a() * q.a(); }; } }如果编译报「switch 表达式不完整」先检查permits列表是否被case全覆盖再看子类型是不是final或sealed。只要有一个子类型是开放的非 final 非 sealed编译器就不认穷尽必须加default。这段代码在 17 上需要--enable-preview才能编译这就回到第 3 章说的预览语法只做规则记忆。真正要掌握的是permits与final的关系以及 sealed 类必须和子类在同一模块或同一包未命名模块时这个约束。5.4 Stream中间操作什么时候真的执行import java.util.stream.Stream; public class Q_Stream_Lazy { public static void main(String[] args) { long n Stream.of(a, bb, ccc) .peek(x - System.out.println(peek: x)) .filter(x - x.length() 1) .count(); System.out.println(count n); } }跑出来你会发现一条peek都没打印count却是 2。原因是count()只需要元素个数而源是已知大小的流JDK 会跳过不改变元素数量的中间操作。这不是 bug是规范允许的优化。题目问「输出什么」时这类优化就是陷阱。判断方法看终端操作是否依赖元素本身。count、anyMatch、findFirst这类可能短路或推导的不能假设中间操作一定执行forEach、collect这类需要遍历每个元素的中间操作必然执行。5.5 并发与 JDBC异常和资源去哪了import java.util.concurrent.*; public class Q_Exec_Exception { public static void main(String[] args) throws Exception { ExecutorService pool Executors.newFixedThreadPool(2); // 任务里抛异常异常不会直接打印被包进 Future FutureInteger f pool.submit(() - { throw new IllegalStateException(boom); }); try { f.get(); } catch (ExecutionException e) { System.out.println(cause e.getCause().getMessage()); } finally { pool.shutdown(); } } }submit和execute的差别是高频考点execute抛出的异常会传到线程的未捕获异常处理器submit会把它包进ExecutionException只能通过get()拿到。很多人看到「任务里抛了异常但控制台没输出」就是踩了这个。JDBC 那侧对应的坑是资源关闭顺序和自动提交。try-with-resources的关闭顺序与声明顺序相反声明Connection再声明PreparedStatement关闭时先关语句再关连接反了就报错。事务题则要看setAutoCommit(false)之后有没有显式commit异常路径上有没有rollback——题目里不写这两行的通常就是错的那个选项。6. 从正确率到通过率错题三遍法与考前 72 小时的分配题库做到后期容易陷入一个虚假的安全感正确率 75% 就觉得稳了。但练习的正确率和真实通过之间隔着两件事——题目的重复率和你在时间压力下的判断速度。我的做法是给每道标记为规则不清的题安排三次接触每次间隔至少三天。第一遍是做题当天跑代码把结论写成一句话写不出来就说明还没懂。第二遍是三天后不看笔记直接重做原题做对了再去看当初写的那句话是否还站得住。第三遍是考前一周只看那句话不看题能在十秒内说出「这题考什么、陷阱在哪」才算过关。三次都过的题从台账里划掉剩下的继续循环。考前十天的分配我一般这么切前四天按考纲域做定向补漏只做复现率低于 50% 的域中间三天做完整套卷严格限时中途不查任何东西最后三天不碰新题只翻台账里的那句话清单每天两轮每轮四十分钟。这套节奏的核心是把「输入新知识」和「维持判断速度」分开最后三天再做新题只会打乱节奏。有一个技巧值得单独说把javac的报错当作复习材料。考前一周我会把台账里所有「编译期就能判死」的题集中过一遍看它们的错误行——incompatible types、non-static variable cannot be referenced、missing return statement这些报错文案在脑子里形成肌肉记忆之后考试时看到选项就能嗅出问题。这比记「第 X 条规则」有效得多因为它调动的是模式识别不是回忆。从那以后我每次准备认证类考试都强制走一遍「台账 跑代码验证 三遍接触」这套流程哪怕时间只够做半套题也不会跳过跑代码那一步。省下来的那点时间最后都会以「这题我明明看过」的形式还回去。希望帮到你。本文还有配套的精品资源点击获取