習(xí)指南)
簡介這份 PDF 面向備戰(zhàn) Oracle Java SE 17 開發(fā)者認證Exam 1Z0-829的考生由 Jeanne Boyarsky 與 Scott Selikoff 編寫把考試大綱中的知識點轉(zhuǎn)化為可直接演練的模擬測試題適合已有一定 Java 基礎(chǔ)、希望在上考場前檢驗薄弱環(huán)節(jié)的中高級開發(fā)者。壓縮包內(nèi)為 1 個 PDF 文件約 20.96MB由 Wiley 出版發(fā)行內(nèi)容圍繞官方考試范圍編排覆蓋語言基礎(chǔ)、面向?qū)ο笤O(shè)計與實現(xiàn)、異常處理、集合與泛型、Stream 與 Lambda、模塊化系統(tǒng)、并發(fā)編程、I/O 與 JDBC 等模塊題目后附有解析與實用的復(fù)習(xí)策略便于理解考點原理而不只是死記答案。讀者可借助成套練習(xí)模擬真實考試節(jié)奏用錯題反查知識盲區(qū)據(jù)此安排沖刺階段的復(fù)習(xí)順序解析側(cè)重考點辨析也方便培訓(xùn)講師挑選課后練習(xí)。目前已有 268 人學(xué)習(xí)下載適合考前自測、查漏補缺與二輪強化。1. 一張地圖Java SE 17 認證考試到底在考什么先說一個反直覺的結(jié)論這門 Java SE 17 認證考試不考你寫不寫得出業(yè)務(wù)代碼考的是你能不能在兩分鐘內(nèi)判斷一段代碼是「編譯不過」「運行才炸」還是「看著別扭其實完全合法」。正因為如此市面上那套 1Z0-829 的實戰(zhàn)練習(xí)和模擬測試題才會顯得刁鉆題面短、選項近、錯誤藏在一行注釋后面。很多人做完整套題才發(fā)現(xiàn)自己真正丟分的地方不是不會寫類而是漏看了訪問修飾符、看錯了格式化模式字母的大小寫。把這十塊考綱攤開再疊起來其實只有四層語言地基日期時間、文本、數(shù)值、控制流、對象模型繼承、接口、記錄類、密封類、異常與斷言、數(shù)據(jù)結(jié)構(gòu)與流水線集合、泛型、Stream、并發(fā)、工程域I/O、模塊化、本地化。前兩層決定你能不能讀懂題后兩層決定你能不能排掉干擾項。適合已經(jīng)能寫 Java、想用一套系統(tǒng)復(fù)習(xí)策略把知識點串成地圖的人。2. 語言地基日期時間、文本、數(shù)值與控制流的復(fù)習(xí)重點2.1 日期時間不可變對象與 Period、Duration 的分工java.time這一族的題八成的失分不在 API 記不住而在忘了它們?nèi)遣豢勺儗ο?。LocalDate.plusDays(1)返回的是新實例原對象紋絲不動代碼里寫date.plusDays(1);而不接返回值編譯能過、運行不報錯、結(jié)果就是沒變——這種題專治「我以為它在原地改」。類型表示什么常用場景LocalDate/LocalTime/LocalDateTime不帶時區(qū)的日期、時間、日期時間大多數(shù)本地計算ZonedDateTime帶時區(qū)規(guī)則的完整時間跨時區(qū)、夏令時Instant時間線上的一個點UTC 紀(jì)元秒時間戳、機器時間Period年、月、日只能作用于日期類Duration時、分、秒、納秒作用于時間、Instant最容易混的是Period和Duration的作用范圍給LocalDate加Duration編譯就不過給LocalTime加Period也一樣。Duration.ofHours(30).toHours()是 30但toMinutes()得到的是 1800toMinutesPart()才是 0截斷和取余的區(qū)別在題里經(jīng)常只差一個詞。// 注意大小寫MM 是月mm 是分HH 是 24 小時制hh 是 12 小時制dd 是日DD 是一年中的第幾天 DateTimeFormatter fmt DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss); LocalDateTime dt LocalDateTime.of(2023, 3, 1, 9, 5, 0); System.out.println(fmt.format(dt)); // 2023-03-01 09:05:00 System.out.println(dt.plusDays(1).format(fmt)); // 原對象不變plusDays 返回新實例 // 解析失敗拋 DateTimeParseException它是 RuntimeException 的子類catch 寫成 IOException 會編譯不過 LocalDate parsed LocalDate.parse(2023-03-01, DateTimeFormatter.ISO_LOCAL_DATE);yyyy和YYYY不是同一個東西前者是紀(jì)元年后者是「基于周的年份」跨年那一周用YYYY會得到下一年的數(shù)字。同理MM和mm、HH和hh、ss和SSS這類字母大小寫在考察里幾乎是必出寫代碼時隨手用 IDE 的格式化提示反而察覺不到。2.2 文本塊與 String 新 API規(guī)則比方法更需要記文本塊有三條硬規(guī)則開頭的后面必須換行否則編譯不過內(nèi)容里的雙引號不需要轉(zhuǎn)義但連續(xù)三個引號要轉(zhuǎn)義收尾定界符的位置決定公共縮進被裁掉多少。第三條是重災(zāi)區(qū)——把結(jié)束的頂格寫縮進就全保留和最后一行內(nèi)容對齊縮進就被裁掉。String sql SELECT id, name FROM demo_table WHERE id %d .formatted(7); // 反斜杠放在行尾表示「這一行不換行」\s 表示一個不會被裁掉的行尾空格 String joined a\ b; System.out.println(joined); // ab方法層面strip()和trim()的差別在于前者認 Unicode 空白后者只處理 ASCII 碼小于等于 32 的字符indent(int)傳正數(shù)是加空格傳負數(shù)只負責(zé)移除空白而不補齊isBlank()判的是「是否只有空白」空串返回 true。String.repeat(0)返回空串而不是異常lines()返回的是StreamString這些細節(jié)單獨看都很小湊在四個選項里就能篩掉一半人。2.3 數(shù)值、包裝類與類型提升的玄學(xué)都在這包裝類的緩存機制是經(jīng)典翻車點Integer在 -128 到 127 之間復(fù)用同一批對象用比較得到 true超出范圍就是 false。Long、Short、Character有類似機制Float和Double則根本沒有緩存兩個Double.valueOf(1.0)用永遠是 false。Integer a 127, b 127; System.out.println(a b); // true包裝類緩存 -128~127 Integer c 128, d 128; System.out.println(c d); // false超出緩存范圍 // BigDecimal 的 equals 連精度一起比compareTo 只比數(shù)值 System.out.println(new BigDecimal(1.0).equals(new BigDecimal(1.00))); // false System.out.println(new BigDecimal(1.0).compareTo(new BigDecimal(1.00))); // 0字面量里的下劃線只能放在數(shù)字之間1_000合法_1000、1000_、1._0、0x_1全都不合法。三元運算符的類型提升也??糱oolean ? 1 : 2L的結(jié)果類型是long把int和double混在一起會提升成double而兩邊是包裝類時又要考慮拆箱true ? Integer.valueOf(1) : 1這種寫法在題里出現(xiàn)就得先想清楚會不會 NPE。2.4 控制流switch 表達式與標(biāo)簽跳轉(zhuǎn)switch能接受的類型有限byte、short、char、int、對應(yīng)的包裝類、String、枚舉。long、float、double、boolean一律不允許case標(biāo)簽必須是編譯期常量。Java 17 的箭頭形式不會穿透一段代碼塊要返回值時必須顯式寫yield作為表達式的switch還要求窮盡枚舉少寫一個常量又沒default就是編譯錯誤。// switch 表達式必須窮盡箭頭形式不穿透代碼塊里用 yield 交出值 String size switch (code) { case 1, 2 - small; case 3 - { String r medium; yield r; } default - unknown; };instanceof的模式匹配變量作用域要看清if (!(o instanceof String s)) return;之后的s是有效的但把條件寫成與||混合時變量的可用范圍會跟著短路邏輯走。標(biāo)簽跳轉(zhuǎn)outer:只能貼在最外層循環(huán)前break outer跳出整個循環(huán)continue outer只結(jié)束當(dāng)前這輪。至于不可達代碼while (false) { }后面的語句是編譯錯誤而if (false) { }后面的語句合法——同樣是常量條件一個報錯一個不報憑直覺猜很容易錯。3. 對象模型繼承、接口、記錄類、密封類與異常的判定題3.1 訪問修飾符與重寫規(guī)則訪問控制是這類題的第一道篩子四個修飾符的范圍必須閉著眼都能畫出來。修飾符同一個類同一個包子類任意位置private可見不可見不可見不可見默認無修飾符可見可見不可見不可見protected可見可見可見不可見public可見可見可見可見重寫要同時滿足四條方法簽名一致、返回類型協(xié)變或相同、不能降低可見性、不能拋出更寬的受檢異常。static方法不能被重寫子類寫同名同參只是隱藏private方法同理子類里寫一個「看起來像重寫」的方法完全合法只是不構(gòu)成多態(tài)。變量則根本沒有重寫這回事父類引用指向子類對象時取到的是父類那份字段值。class Parent { static { System.out.print(1); } // 父類靜態(tài)塊類初始化時執(zhí)行且只一次 { System.out.print(2); } // 父類實例塊按聲明順序 Parent() { System.out.print(3); } // 父類構(gòu)造器體 } class Child extends Parent { static { System.out.print(4); } { System.out.print(5); } Child() { System.out.print(6); } } new Child(); // 輸出 142356這個順序值得背下來父類靜態(tài)塊 → 子類靜態(tài)塊 → 父類實例塊與字段 → 父類構(gòu)造器 → 子類實例塊與字段 → 子類構(gòu)造器。this()和super()不能同時出現(xiàn)而且必須是構(gòu)造器的第一條語句所以題里那種「先打印一行再調(diào)super()」的寫法必然是編譯錯誤。3.2 接口、記錄類與密封類接口的三類方法要分清default方法可被繼承也可被重寫static方法屬于接口本身、不被繼承私有方法只能給接口內(nèi)部的default方法調(diào)用。兩個接口給出同簽名的default方法時實現(xiàn)類必須重寫否則編譯不過——這條幾乎是必考。接口里的字段隱式為public static final寫int X 1;之后想再賦值就是編譯錯誤。記錄類是 Java 17 的高頻考點規(guī)則密集但都很好記隱式final不能聲明實例字段組件訪問器是x()而不是getX()可以帶靜態(tài)字段和靜態(tài)方法可以實現(xiàn)接口不能繼承別的類。緊湊構(gòu)造器不寫參數(shù)列表用來做校驗如果要寫普通構(gòu)造器第一條語句必須委托給規(guī)范構(gòu)造器。record Point(int x, int y) { Point { // 緊湊構(gòu)造器不寫參數(shù)列表 if (x 0) throw new IllegalArgumentException(x 0); } Point(int v) { this(v, v); } // 其他構(gòu)造器必須委托給規(guī)范構(gòu)造器 }密封類看兩點permits列出的子類必須存在且每個子類必須標(biāo)注final、sealed或non-sealed三者之一如果都在同一個模塊里可以省掉permits讓編譯器從同一個文件里推斷。子類和父類不在同一個包未命名模塊時就編譯不過這條在選擇題里通常被包裝成「跨包繼承」的樣子。sealed interface Shape permits Circle, Square {} // 子類必須 final / sealed / non-sealed record Circle(double r) implements Shape {} final class Square implements Shape {}3.3 異常與斷言三層結(jié)構(gòu)與執(zhí)行順序異常體系先分三層Throwable之下是Error和ExceptionException之下再分RuntimeException非受檢和其他受檢異常。題里最愛考的是「哪些是受檢」NullPointerException、IllegalArgumentException、NumberFormatException、ClassCastException、ArithmeticException全是非受檢IOException、SQLException、InterruptedException是受檢。捕獲順序上子類異常必須寫在父類前面否則編譯錯誤多重捕獲catch (A | B e)里的e隱式final不能再賦值。try-with-resources的兩個細節(jié)關(guān)閉順序與聲明順序相反如果try塊和close()都拋異常close()的異常會作為 suppressed 掛在主異常上用getSuppressed()取。資源變量隱式final在try塊里重新賦值是編譯錯誤。finally里寫return會覆蓋try里的返回值和異常這屬于「編譯器不攔、運行結(jié)果反直覺」的典型。class R implements AutoCloseable { private final String name; R(String n) { name n; } public void close() { System.out.print(name); } } try (R a new R(A); R b new R(B)) { } // 輸出 BA關(guān)閉順序與聲明順序相反斷言要單獨記assert默認關(guān)閉必須用-ea打開才生效失敗時拋的是AssertionError而不是異常assert x 0 : msg;里的冒號后面是消息表達式。重寫方法時拋出的受檢異常不能比父類版本更寬但可以更窄甚至不拋非受檢異常則不受這條限制。4. 數(shù)據(jù)結(jié)構(gòu)與流水線集合、泛型、Stream 與并發(fā)的題眼4.1 集合實現(xiàn)類的選擇與代價集合題的套路是先看「能不能存 null」再看「是不是有序」最后看「并發(fā)下會不會炸」。實現(xiàn)類允許 null 元素有序性典型考點ArrayList允許插入順序隨機訪問快中間刪除慢LinkedList允許插入順序同時實現(xiàn)List與DequeArrayDeque不允許插入順序作?;蜿犃杏胮ush(null)拋 NPEHashSet/HashMap允許HashMap允許一個 null 鍵無序依賴hashCode與equalsTreeSet/TreeMap不允許排序元素必須可比較否則ClassCastExceptionConcurrentHashMap不允許無序鍵值都不能為 nullList.of()、Set.of()、Map.of()返回的都是不可變集合傳 null 直接 NPESet.of()里放重復(fù)元素拋IllegalArgumentException。Arrays.asList()是固定長度而非不可變add和remove拋UnsupportedOperationException但set可以改。遍歷時刪除元素要用Iterator.remove()或removeIf直接list.remove()會得到ConcurrentModificationException。4.2 泛型與比較器PECS 別背反泛型擦除決定了new ArrayListString().getClass() new ArrayListInteger().getClass()為 true也決定了不能寫new T[]、不能寫new ListString[10]。通配符的方向按 PECS 記List? extends Number是生產(chǎn)者只能讀不能寫List? super Integer是消費者能寫但讀出來只能是Object。題里常把這兩個寫反讓你判斷哪一行編譯不過。比較器則有兩條主線Comparable是「自己和自己比」compareTo返回 0 表示相等Comparator是外部比較器可以鏈?zhǔn)浇M合。Comparator.comparing(Person::name).thenComparingInt(Person::age)是先按名字再按年齡.reversed()只反轉(zhuǎn)緊挨著的那一段寫在整個鏈子末尾和寫在中間結(jié)果完全不同。用TreeSet裝沒實現(xiàn)Comparable又沒給Comparator的對象第一次add就拋ClassCastException而不是在編譯期報錯。4.3 Stream 管道的惰性與終止操作Stream 題的核心只有一句話中間操作是惰性的終止操作才觸發(fā)執(zhí)行而且一條流只能消費一次。map是一對一flatMap是一對多再攤平peek是中間操作只該用來觀察count()這類終止操作在元素個數(shù)已知時可能壓根不遍歷peek一行都不打印。ListString names List.of(Amy, Bob, Ada); MapInteger, ListString byLen names.stream() .collect(Collectors.groupingBy(String::length)); // 長度 - 名字列表 // peek 是中間操作只用于觀察流水線不能當(dāng) forEach 用 long n names.stream().peek(System.out::println).count();Collectors.toMap遇到重復(fù)鍵拋IllegalStateException需要傳合并函數(shù)才不炸partitioningBy返回的 Map 一定有兩個鍵true和falsefindFirst在并行流里仍保證取第一個findAny則不保證。Optional的orElse無論是否為空都會求值orElseGet才惰性這一條在帶副作用的例子里經(jīng)常被拿來出題。4.4 并發(fā)的四個必考點并發(fā)部分先分清Runnable和Callable前者run()無返回值不能拋受檢異常后者call()有返回值可以拋。ExecutorService.execute提交任務(wù)無返回submit返回Futureget()阻塞并可能拋ExecutionException、InterruptedException、TimeoutException。shutdown()只是不再接新任務(wù)、不阻塞awaitTermination才阻塞等待忘記關(guān)閉線程池JVM 就不退出。線程狀態(tài)六個值、synchronized與wait/notify必須在同步塊內(nèi)調(diào)用、AtomicInteger保證單變量原子性、CountDownLatch一次性而CyclicBarrier可重用這幾條屬于「記了就能拿分」。并行流用的是公共 ForkJoin 池lambda 里改共享的可變狀態(tài)結(jié)果順序和數(shù)值都不確定要保證順序得用forEachOrdered。5. 易錯點五個反復(fù)翻車的判定盲區(qū)5.1 格式化與本地化模式字母和資源文件命名易漏點yyyy與YYYY、MM與mm、HH與hh混用以及ResourceBundle的基名拼寫。資源文件的查找順序是「基名_語言_國家 → 基名_語言 → 基名 → 默認 locale 的對應(yīng)文件」少一級就落到別的文件上程序不報錯只是取到了錯誤的文案。為什么漏這些字符串長得太像寫代碼時 IDE 不校驗?zāi)J阶帜钢挥性谶\行時才發(fā)現(xiàn)輸出多了或少了幾個小時資源文件的命名錯誤也不會拋異?;赝藱C制把它悄悄兜住了。怎么補把模式字母抄成一張對照表貼在錯題本第一頁本地化部分固定用ResourceBundle.getBundle(messages, locale)加三個層級的屬性文件做一次實測看清楚每一級命中的是哪個文件再回頭做模擬測試?yán)锏耐愵}。5.2 泛型通配符與重載解析易漏點List? extends T里add編譯不過List? super T里get只能拿到Object兩個重載方法一個收List?、一個收ListString調(diào)用時選哪個。為什么漏方向背反了也能跑通大部分日常代碼因為平時寫的都是具體類型重載解析則牽涉泛型擦除ListString和ListInteger擦除后是同一個類型編譯器會挑「最具體」的那個。怎么補準(zhǔn)備一段五行以內(nèi)的小程序把上界、下界、無界三種寫法的讀寫操作各試一遍看編譯器在哪一行報錯重載那類題不要靠推理直接在 jshell 里敲一遍記住了報錯信息比記住結(jié)論更管用。5.3 資源關(guān)閉順序與 finally 的覆蓋易漏點try-with-resources的關(guān)閉順序是聲明的逆序close()拋出的異常會成為 suppressedfinally里的return會吞掉try中的異常。為什么漏正常路徑下順序無關(guān)緊要只有異常路徑才暴露差異而練習(xí)題恰恰專挑異常路徑finally的覆蓋行為編譯器完全不提示。怎么補寫一個實現(xiàn)AutoCloseable的小類聲明兩個實例并在try塊里拋異常把getSuppressed()的內(nèi)容打印出來凡是看到finally里帶return或賦值語句的題直接判定為反例。5.4 模塊路徑與類路徑的混用易漏點module-info.java里的requires、exports、opens語義搞混把模塊化的 jar 丟到類路徑上模塊描述符被忽略。為什么漏模塊系統(tǒng)在實際項目里用得少光是「能編譯能運行」就不容易觸發(fā)邊界exports與opens的差別前者是編譯期可訪問后者是運行期反射可訪問在單模塊場景里體現(xiàn)不出來。怎么補手動建兩個模塊一個導(dǎo)出包、一個requires它先跑通再把exports改成opens觀察編譯期和反射訪問分別在何時失敗。比起背定義失敗一次記得牢。5.5 并行流與線程池的狀態(tài)泄漏易漏點并行流里修改共享集合線程池創(chuàng)建后不shutdown程序跑完不退出submit返回的Future不get異常被靜默吞掉。為什么漏小數(shù)據(jù)量下并行流往往按順序執(zhí)行看不出問題異常藏在Future里不在主線程棧上控制臺一片安靜。怎么補寫一個IntStream.range(0, 10_000).parallel()往ArrayList里add的 Demo跑幾次看看結(jié)果長度是否穩(wěn)定線程池的練習(xí)一律加try/finally包住shutdown()和awaitTermination把這條當(dāng)成肌肉記憶。6. 復(fù)習(xí)清單把十個考點域壓成一張自測表復(fù)習(xí)到中后段最怕的是「每塊都看過、每塊都不敢說會」。我自己的做法是把十個考點域壓成一張表每行只填三件事這一域的必背結(jié)論、最容易錯的那一條、用什么方式自測。表格填不滿的地方就是下一輪該重點補的地方??键c域必背結(jié)論自測方式日期時間全部不可變Period歸日期、Duration歸時間手寫三種格式化模式并預(yù)測輸出文本與數(shù)值文本塊三條規(guī)則、包裝類緩存區(qū)間改一行代碼看是否編譯過控制流switch支持的類型與窮盡性把if/else改寫成switch表達式面向?qū)ο笾貙懰臈l件、初始化順序數(shù)一遍打印順序再運行記錄類與密封類緊湊構(gòu)造器、子類三選一故意漏寫final看報錯異常與斷言受檢非受檢分類、關(guān)閉逆序造 suppressed 異常打印鏈集合null 支持與有序性對照表換實現(xiàn)類跑同一段代碼泛型與比較器PECS 方向、thenComparing順序?qū)懮舷陆绺饕淮巫x寫Stream惰性、只能消費一次、toMap沖突加peek看是否被跳過并發(fā)與 I/O線程池關(guān)閉、Path與Files分工跑一遍-ea打開斷言自測環(huán)節(jié)有兩件事必須親自動手別在腦子里推。第一件是斷言默認關(guān)閉不打開就等于沒寫第二件是語法點猜錯一次比看十遍定義有用。# 斷言默認關(guān)閉必須顯式打開否則 assert 語句等于不存在 javac SelfCheck.java java -ea SelfCheck # 語法點直接在 jshell 里試比翻筆記快 jshell --class-path out做完一套模擬測試后的歸因也要固定流程錯題只分三類——沒記住的結(jié)論、看漏的關(guān)鍵字、理解偏的語義。第一類補記憶卡第二類練審題把修飾符、返回值、異常類型用鉛筆圈出來第三類才值得回爐重看。前兩類占多數(shù)的時候繼續(xù)刷題收益最高第三類一多就該停下來把整章重新梳理一遍而不是硬刷下一套。從那以后我每次做完一套實戰(zhàn)練習(xí)都會強制走一遍這套歸因流程先把錯題的錯因?qū)懗梢粋€短句再對回表格里的某一行最后只復(fù)習(xí)那一行涉及的知識點絕不整章重看。堅持幾套下來錯題會明顯收斂到固定的兩三個域上那時候再集中補效率比平均用力高得多。希望幫到你。本文還有配套的精品資源點擊獲取