保姆級(jí)學(xué)習(xí)路線(xiàn):從環(huán)境配置到面試手撕排序算法)
學(xué) Java 零基礎(chǔ)大部分人不是學(xué)不完而是被“看不到進(jìn)度”卡住了。搜索關(guān)鍵詞里每天都會(huì)出現(xiàn) java 基礎(chǔ)、java 學(xué)習(xí)路線(xiàn)、java 環(huán)境變量配置、java 面試題、冒泡排序 java、快速排序 java 實(shí)現(xiàn)這些內(nèi)容說(shuō)明大家缺的不是教程合集而是一條能按順序執(zhí)行、每步都能驗(yàn)證結(jié)果的路線(xiàn)。這篇文章把從環(huán)境搭建到面試手撕題的學(xué)習(xí)鏈路拆開(kāi)整理成一套保姆級(jí) Java 基礎(chǔ)教程筆記。你可以照著走但每個(gè)步驟都要親手敲一遍只看不敲等于沒(méi)學(xué)。1. 零基礎(chǔ)學(xué) Java先拆路線(xiàn)再談堅(jiān)持Java 的知識(shí)點(diǎn)非常密集如果按視頻目錄一集一集往下看很容易出現(xiàn)“前面看懂了后面全忘了”。更穩(wěn)的做法是先把路線(xiàn)拆成階段每個(gè)階段設(shè)一個(gè)驗(yàn)收標(biāo)準(zhǔn)驗(yàn)收通過(guò)再進(jìn)入下一階段。1.1 六段式學(xué)習(xí)路線(xiàn)不建議跳級(jí)階段一基礎(chǔ)語(yǔ)法。變量、數(shù)據(jù)類(lèi)型、運(yùn)算符、表達(dá)式、分支循環(huán)、數(shù)組、方法。階段二面向?qū)ο蟆n?lèi)、對(duì)象、構(gòu)造方法、封裝、繼承、多態(tài)、抽象類(lèi)、接口。階段三常用類(lèi)和集合。String、StringBuilder、包裝類(lèi)、日期時(shí)間、集合框架、泛型。階段四異常與函數(shù)式編程。異常體系、try-catch-finally、Lambda、Stream。階段五并發(fā)基礎(chǔ)。線(xiàn)程、線(xiàn)程池、CompletableFuture。階段六項(xiàng)目實(shí)戰(zhàn)與面試題。從控制臺(tái)項(xiàng)目開(kāi)始逐步過(guò)渡到 Web 后端。這個(gè)順序不是隨便排的。Java 是一門(mén)面向?qū)ο蟮恼Z(yǔ)言前面語(yǔ)法沒(méi)寫(xiě)熟后面面向?qū)ο髸?huì)非常吃力集合和 Lambda 用不熟寫(xiě)項(xiàng)目時(shí)每段代碼都會(huì)很啰嗦。跳級(jí)的代價(jià)不是你跟不上而是遇到報(bào)錯(cuò)時(shí)連從哪里開(kāi)始排查都不知道。1.2 每個(gè)階段怎么驗(yàn)收不要用“看完了”當(dāng)標(biāo)準(zhǔn)學(xué)習(xí)最怕用“看完第幾集”來(lái)衡量進(jìn)度。我建議每個(gè)階段結(jié)束時(shí)用獨(dú)立寫(xiě)代碼的方式做驗(yàn)收。基礎(chǔ)語(yǔ)法階段寫(xiě)一個(gè) Java 高級(jí)計(jì)算器支持加減乘除、括號(hào)、連續(xù)運(yùn)算不依賴(lài)任何第三方庫(kù)。面向?qū)ο箅A段寫(xiě)一個(gè)員工管理系統(tǒng)用類(lèi)和對(duì)象描述員工信息用數(shù)組或集合存儲(chǔ)能增刪改查。集合階段寫(xiě)一個(gè)學(xué)生成績(jī)統(tǒng)計(jì)程序用 Map 統(tǒng)計(jì)各分?jǐn)?shù)段人數(shù)用 List 排序并輸出前幾名。并發(fā)階段寫(xiě)一個(gè)異步任務(wù)示例用 CompletableFuture 同時(shí)請(qǐng)求多個(gè)模擬接口最后匯總結(jié)果。這些驗(yàn)收項(xiàng)目都不大但能真實(shí)暴露你對(duì)代碼的熟悉程度。寫(xiě)不出來(lái)就回頭補(bǔ)寫(xiě)出來(lái)了說(shuō)明這個(gè)階段的知識(shí)你已經(jīng)用到過(guò)一次了后面看面試題時(shí)會(huì)輕松很多。1.3 學(xué)習(xí)筆記怎么記才有復(fù)用價(jià)值筆記不要抄概念要記三類(lèi)內(nèi)容第一是易錯(cuò)點(diǎn)記錄比如數(shù)組越界、并發(fā)修改異常、比較器寫(xiě)反第二是調(diào)試過(guò)程遇到一個(gè)報(bào)錯(cuò)把報(bào)錯(cuò)信息、原因、解決方式記下來(lái)第三是能直接運(yùn)行的代碼片段方便以后復(fù)制和改造。記筆記最大的好處不是復(fù)習(xí)而是幫你建立自己的“排錯(cuò)庫(kù)”。真正到了面試和工作中你印象最深的往往不是某個(gè)理論而是你親手解決過(guò)的那個(gè)報(bào)錯(cuò)。2. JDK、環(huán)境變量和第一個(gè) Java 程序這關(guān)過(guò)了后面才順每次有同學(xué)問(wèn)“Java 該怎么學(xué)”我都會(huì)先問(wèn)一句你的 javac 命令能不能用如果環(huán)境都沒(méi)配好視頻看再多也無(wú)法轉(zhuǎn)化成動(dòng)手能力。這一部分是最容易被忽略也是卡住新手最多的地方。2.1 JDK 安裝與 JAVA_HOME 配置先裝 JDK再配置環(huán)境變量。JDK 里包含了編譯命令 javac、運(yùn)行命令 java以及 Java 虛擬機(jī)。習(xí)慣上大家會(huì)配置兩個(gè)變量JAVA_HOME 和 Path。# 驗(yàn)證環(huán)境是否正確 java -version javac -version配置步驟本身不復(fù)雜但要注意兩點(diǎn)。第一JAVA_HOME 指向的是 JDK 的根目錄不是 bin 目錄。比如安裝后路徑是C:\Program Files\Java\jdk-17那 JAVA_HOME 就是這一層不是里面的 bin。第二Path 里要追加%JAVA_HOME%\bin這樣系統(tǒng)才能在任何目錄下找到 java 和 javac 命令。為什么一定要配置環(huán)境變量因?yàn)槟阍诿钚欣镙斎朊顣r(shí)系統(tǒng)需要知道去哪里找可執(zhí)行程序。不配置你只能在 JDK 的 bin 目錄里運(yùn)行配置了任何目錄下都能編譯和運(yùn)行。2.2 新手最容易碰到的 5 個(gè)報(bào)錯(cuò)這類(lèi)問(wèn)題我?guī)缀趺扛魩滋炀湍苡龅揭淮斡绕涫莿倢W(xué) Java 的同學(xué)。報(bào)錯(cuò)信息常見(jiàn)原因解決思路‘java’ 不是內(nèi)部或外部命令環(huán)境變量沒(méi)配好或者配完后沒(méi)有重開(kāi)終端檢查 JAVA_HOME 和 Path重開(kāi)命令行警告: 源發(fā)行版 17 需要目標(biāo)發(fā)行版 17IDE 的 Java 編譯級(jí)別和 JDK 版本不匹配把 IDE 的 Project Structure 里的 SDK 和 Language Level 改成相同版本You arent using a compiler supported by lombokLombok 插件版本和 JDK 版本不兼容升級(jí) Lombok 依賴(lài)版本或調(diào)整 IDE 的 Annotation Processing 配置Exception: java.lang.OutOfMemoryError: insufficient memory啟動(dòng)參數(shù)堆內(nèi)存設(shè)置過(guò)大或過(guò)小或資源不足檢查-Xmx參數(shù)按機(jī)器內(nèi)存調(diào)整ArrayIndexOutOfBoundsException數(shù)組訪(fǎng)問(wèn)越界比如長(zhǎng)度 5 的元素訪(fǎng)問(wèn)了 index 5檢查循環(huán)邊界確認(rèn)是用而不是如果你用的是 IDE 而不是純命令行優(yōu)先檢查兩處項(xiàng)目的 SDK 版本和 Language Level 版本。絕大多數(shù)“源發(fā)行版 17 需要目標(biāo)發(fā)行版 17”都是版本不一致引起的這個(gè)問(wèn)題和代碼本身沒(méi)有關(guān)系。2.3 IDE 怎么選項(xiàng)目結(jié)構(gòu)怎么組織新手階段選 IntelliJ IDEA 社區(qū)版就夠用免費(fèi)且功能完善。如果你電腦配置比較低用 VS Code 或 Eclipse 也可以關(guān)鍵是穩(wěn)定。不要同時(shí)裝好幾個(gè) IDE學(xué) Java 基礎(chǔ)時(shí)一套環(huán)境就夠換工具會(huì)分散精力。項(xiàng)目結(jié)構(gòu)上先不用急著按照 Maven 或 Gradle 的標(biāo)準(zhǔn)建目錄。基礎(chǔ)階段可以這樣組織一個(gè)項(xiàng)目里放多個(gè)模塊或包每個(gè)知識(shí)點(diǎn)對(duì)應(yīng)一個(gè)類(lèi)。比如day01.HelloWorld、day02.Calculator、day05.SortDemo通過(guò)包名區(qū)分階段。等學(xué)到 Web 項(xiàng)目再切換到標(biāo)準(zhǔn)工程結(jié)構(gòu)。容易掉坑的是初學(xué)者喜歡把全部代碼寫(xiě)在一個(gè)類(lèi)里一個(gè)類(lèi)幾百行。這樣做短時(shí)間能跑但后面學(xué)面向?qū)ο髸r(shí)很難理解“一個(gè)類(lèi)只負(fù)責(zé)一件事”的意義。建議從第一天開(kāi)始一個(gè)類(lèi)盡量只做一類(lèi)事。3. 基礎(chǔ)語(yǔ)法和面向?qū)ο髲摹翱吹枚钡健皩?xiě)得熟”很多零基礎(chǔ)同學(xué)都會(huì)遇到一個(gè)現(xiàn)象視頻里老師寫(xiě)的代碼每一個(gè)字符都認(rèn)識(shí)但自己一動(dòng)手就不知道怎么開(kāi)始。這通常不是智力問(wèn)題而是練得太少。Java 基礎(chǔ)階段的任務(wù)就是把最常用的語(yǔ)法和面向?qū)ο蟾拍顚?xiě)到肌肉記憶里。3.1 變量、運(yùn)算符和流程控制不用背優(yōu)先級(jí)Java 有八種基本數(shù)據(jù)類(lèi)型byte、short、int、long、float、double、char、boolean。實(shí)際開(kāi)發(fā)中最常用的是 int、double、boolean 和 String。String 是引用類(lèi)型但它使用頻率太高可以當(dāng)成基礎(chǔ)語(yǔ)法先熟悉。運(yùn)算符和表達(dá)式不需要死記優(yōu)先級(jí)只要記住幾條規(guī)律括號(hào)優(yōu)先賦值運(yùn)算最后比較運(yùn)算先于邏輯運(yùn)算除法里整數(shù)相除會(huì)丟失小數(shù)位。比如System.out.println(5 / 2);輸出是 2不是 2.5。這個(gè)點(diǎn)面試題喜歡考實(shí)際開(kāi)發(fā)里也容易踩。流程控制里if-else、for、while 必須練熟。最推薦的練習(xí)方式是把同一個(gè)算法分別用 for 和 while 各寫(xiě)一遍。比如求 1 到 100 的和、打印九九乘法表、判斷一個(gè)數(shù)是不是素?cái)?shù)。這些題目沒(méi)有技術(shù)含量但能幫你快速熟悉循環(huán)邊界。3.2 類(lèi)和對(duì)象封裝、繼承、多態(tài)、接口面向?qū)ο笫?Java 零基礎(chǔ)到進(jìn)階的分水嶺。類(lèi)是對(duì)一類(lèi)事物的抽象對(duì)象是類(lèi)的具體實(shí)例。構(gòu)造方法負(fù)責(zé)初始化對(duì)象this 表示當(dāng)前對(duì)象的引用。封裝的核心是私有字段加公開(kāi)方法不讓外部直接修改內(nèi)部狀態(tài)。public class Dog { private String name; private int age; public Dog(String name, int age) { this.name name; this.age age; } public String getName() { return name; } public void setName(String name) { this.name name; } }繼承用 extends接口用 implements。接口定義規(guī)范抽象類(lèi)提供公共實(shí)現(xiàn)。多態(tài)的核心是“父類(lèi)引用指向子類(lèi)對(duì)象”這樣程序可以面向抽象編程。面向?qū)ο缶幊?java 這個(gè)搜索詞的熱度一直很高說(shuō)明大家知道概念重要但不知道如何落地。落地的方式很簡(jiǎn)單寫(xiě)代碼時(shí)不要只寫(xiě)“能跑”要多想“如果要擴(kuò)展怎么辦”。比如你做計(jì)算器不要寫(xiě)一個(gè)巨無(wú)霸方法而是把每個(gè)運(yùn)算抽成一個(gè)類(lèi)或方法以后加新運(yùn)算就不用改舊代碼。3.3 常用類(lèi)、枚舉和命名規(guī)范基礎(chǔ)階段必須掌握的常用類(lèi)包括 String、StringBuilder、包裝類(lèi)Integer、Double、LocalDate、LocalDateTime、枚舉。String 是不可變對(duì)象每次拼接都會(huì)生成新對(duì)象大量拼接時(shí)用 StringBuilder。判斷字符串內(nèi)容是否相等必須用 equals 而不是 。枚舉用來(lái)表示固定取值集合比如狀態(tài)、類(lèi)型。枚舉在很多開(kāi)源項(xiàng)目里用得非常多因?yàn)樗鼙苊饽Хㄖ禎M(mǎn)天飛。比如訂單狀態(tài)可以定義成枚舉而不是用 0、1、2 這種數(shù)字。public enum OrderStatus { CREATED, PAID, SHIPPED, COMPLETED, CANCELED }在 switch 中用枚舉可讀性會(huì)高很多。面試題里也常問(wèn)枚舉和常量的區(qū)別枚舉是類(lèi)型安全的能帶上方法常量只是普通變量無(wú)法限制取值范圍。Java 標(biāo)識(shí)符命名規(guī)則也要養(yǎng)成習(xí)慣類(lèi)名大駝峰方法名和變量名小駝峰常量全大寫(xiě)加下劃線(xiàn)包名全小寫(xiě)。命名不是考試題但團(tuán)隊(duì)協(xié)作時(shí)可讀性往往比功能優(yōu)先級(jí)更高。3.4 兩個(gè)練手項(xiàng)目計(jì)算器和圖形作業(yè)學(xué)完基礎(chǔ)語(yǔ)法后動(dòng)手寫(xiě)一個(gè)小項(xiàng)目比繼續(xù)往下看更有效。兩個(gè)選題比較適合這個(gè)階段一是“Java 高級(jí)計(jì)算器”支持連續(xù)運(yùn)算、括號(hào)解析和異常輸入處理二是“Java 仿射變換圖形”比如練習(xí)用 Java 繪制三角形、矩形并實(shí)現(xiàn)平移旋轉(zhuǎn)效果。計(jì)算器能鍛煉思路拆分圖形項(xiàng)目能鍛煉類(lèi)和對(duì)象設(shè)計(jì)。都不需要復(fù)雜的框架純 Java 基礎(chǔ)就能做。做完之后你會(huì)對(duì)方法調(diào)用、參數(shù)傳遞、異常處理形成真實(shí)感受這些東西靠看視頻是體會(huì)不到的。4. 集合、Lambda 和比較器日常開(kāi)發(fā)最常用的工具箱Java 集合框架是最常被問(wèn)到的板塊之一也是從基礎(chǔ)語(yǔ)法過(guò)渡到實(shí)際項(xiàng)目的橋梁。面試題里的 java 容器、java 集合、java 順序表代碼核心都指向這一塊。4.1 集合怎么選先看場(chǎng)景再記 APIArrayList底層是數(shù)組查詢(xún)快增刪在中間慢。日常最常用。LinkedList底層是鏈表頭部和中部插入刪除有優(yōu)勢(shì)但查詢(xún)慢。HashMap鍵值對(duì)存儲(chǔ)存取速度快不保證順序。TreeMap鍵自動(dòng)排序適合需要按序輸出的場(chǎng)景。HashSet去重底層是 HashMap。LinkedHashMap記錄插入順序適合做 LRU 緩存的基礎(chǔ)結(jié)構(gòu)。很多人喜歡把所有集合當(dāng)成“一個(gè)大數(shù)組”用但這會(huì)讓代碼失去性能優(yōu)勢(shì)。比如只需要按鍵查值用 Map 最合適需要去重用 Set需要頻繁按下標(biāo)訪(fǎng)問(wèn)用 ArrayList。每個(gè)集合都有它的適用場(chǎng)景不要拿一個(gè)走天下。4.2 手寫(xiě)一個(gè)簡(jiǎn)單順序表面試題里的 java 順序表代碼本質(zhì)上是考察你懂不懂動(dòng)態(tài)數(shù)組的擴(kuò)容機(jī)制。ArrayList 核心就是用數(shù)組存數(shù)據(jù)容量不夠時(shí)創(chuàng)建新數(shù)組并復(fù)制。public class MyArrayList { private Object[] data; private int size; public MyArrayList() { data new Object[10]; size 0; } public void add(Object value) { if (size data.length) { Object[] newData new Object[data.length * 2]; for (int i 0; i data.length; i) { newData[i] data[i]; } data newData; } data[size] value; } public Object get(int index) { if (index size || index 0) { throw new IndexOutOfBoundsException(索引越界: index); } return data[index]; } }我推薦手寫(xiě)一遍這個(gè)代碼。不是為了面試時(shí)背而是要理解擴(kuò)容帶來(lái)的性能開(kāi)銷(xiāo)以及為什么插入順序表中間位置效率低。4.3 Lambda 和 Stream讓代碼更短但別為了短忽略可讀性L(fǎng)ambda 表達(dá)式讓你能把函數(shù)當(dāng)成參數(shù)傳遞。它最常用的場(chǎng)景是遍歷、排序、過(guò)濾和映射。Java 8 引入 Lambda 后集合操作變得非常精簡(jiǎn)。對(duì)新手來(lái)說(shuō)不需要一上來(lái)就背 Stream 的所有 API但至少要掌握 forEach、filter、map、collect、sorted 這幾個(gè)。ListInteger numbers Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6); numbers.stream() .filter(n - n % 2 0) .sorted() .forEach(System.out::println);有人會(huì)覺(jué)得這種寫(xiě)法太“魔法”但實(shí)際上它是現(xiàn)代 Java 開(kāi)發(fā)的基本技能。以后讀開(kāi)源代碼時(shí)這種寫(xiě)法會(huì)經(jīng)常出現(xiàn)。4.4 Comparator.comparing把一個(gè)元素排到最前面這是很容易踩坑的場(chǎng)景列表排序時(shí)想把某個(gè)特殊值固定排到第一位其余按原有順序或字典序排列。ListString list new ArrayList(Arrays.asList(張三, 李四, 王五)); list.sort(Comparator.comparing((String s) - s.equals(李四) ? 0 : 1) .thenComparing(Comparator.naturalOrder()));這段代碼的原理是給每個(gè)元素計(jì)算一個(gè)權(quán)重目標(biāo)元素權(quán)重是 0其他元素是 1。比較器會(huì)先按權(quán)重排序權(quán)重相同再按自然順序排序。記住compare 返回值小于 0 表示第一個(gè)元素排在前面大于 0 表示第二個(gè)元素排在前面。很多人把比較器的正負(fù)寫(xiě)反導(dǎo)致排序結(jié)果反了排查時(shí)記得先確認(rèn)返回值語(yǔ)義。5. 數(shù)組、冒泡排序、快速排序面試手撕題的基本功排序是 Java 面試?yán)镒畛1灰笫謱?xiě)的題目。搜索熱詞里冒泡排序 java 和快速排序 java 實(shí)現(xiàn)出現(xiàn)頻率很高說(shuō)明這兩個(gè)算法是很多同學(xué)的薄弱點(diǎn)。這里不只要背代碼還要能解釋每一步在干什么。5.1 數(shù)組和數(shù)組越界最容易失分的基礎(chǔ)題數(shù)組在 Java 中是固定長(zhǎng)度的容器索引從 0 開(kāi)始。長(zhǎng)度為 5 的數(shù)組合法索引是 0 到 4。訪(fǎng)問(wèn) 5 會(huì)拋出 ArrayIndexOutOfBoundsException。很多越界問(wèn)題來(lái)自循環(huán)里的邊界判斷。int[] arr {1, 2, 3, 4, 5}; for (int i 0; i arr.length; i) { System.out.println(arr[i]); }注意循環(huán)條件用的是而不是。如果你用i arr.length - 1結(jié)果也一樣但可讀性不如 arr.length直觀。還有一個(gè)細(xì)節(jié)for-each 遍歷數(shù)組時(shí)不能修改數(shù)組元素。5.2 冒泡排序 Java 實(shí)現(xiàn)先保證對(duì)再想著優(yōu)化冒泡排序的思路是相鄰元素兩兩比較大的往后移動(dòng)。每一輪之后最大值會(huì)被移動(dòng)到末尾。所以下一輪可以少比較一個(gè)元素。public static void bubbleSort(int[] arr) { for (int i 0; i arr.length - 1; i) { boolean swapped false; for (int j 0; j arr.length - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped true; } } if (!swapped) { break; } } }這個(gè)版本加入了swapped標(biāo)記如果某一輪沒(méi)有任何交換說(shuō)明數(shù)組已經(jīng)有序可以提前結(jié)束。面試時(shí)能寫(xiě)出這種優(yōu)化會(huì)加分。但先別急著一上來(lái)就寫(xiě)優(yōu)化版先把基本版寫(xiě)對(duì)。排序結(jié)果對(duì)不對(duì)可以這樣驗(yàn)證int[] arr {5, 2, 9, 1, 5, 6}; bubbleSort(arr); System.out.println(Arrays.toString(arr));5.3 快速排序 Java 實(shí)現(xiàn)理解分治再記住代碼快速排序是面試高頻題。核心思路是選取一個(gè)基準(zhǔn)值把小于等于基準(zhǔn)的放左邊大于基準(zhǔn)的放右邊然后遞歸處理左右兩邊。public static void quickSort(int[] arr, int left, int right) { if (left right) { return; } int pivot arr[left (right - left) / 2]; int i left; int j right; while (i j) { while (arr[i] pivot) { i; } while (arr[j] pivot) { j--; } if (i j) { int temp arr[i]; arr[i] arr[j]; arr[j] temp; i; j--; } } if (left j) { quickSort(arr, left, j); } if (i right) { quickSort(arr, i, right); } }這個(gè)寫(xiě)法是“雙指針?lè)謪^(qū)”版本比教材上的“單指針 從左到右”版本更直觀。理解快排的關(guān)鍵是畫(huà)圖先把數(shù)組分成兩半再遞歸。不要在腦子里只記代碼要記住“比基準(zhǔn)大的往右小的往左”這個(gè)規(guī)則。5.4 排序算法怎么練斷點(diǎn)優(yōu)先于優(yōu)化我建議第一次練排序時(shí)不要直接用大型隨機(jī)數(shù)組測(cè)試。先用 5 個(gè)元素的數(shù)組在 IDE 里打上斷點(diǎn)單步執(zhí)行觀察每輪交換后數(shù)組變化。等你親眼看過(guò)一次循環(huán)是怎么執(zhí)行的后面就不會(huì)把 j 的邊界寫(xiě)錯(cuò)了。練熟冒泡和快排之后再去看歸并排序、堆排序、插入排序。零基礎(chǔ)階段不用全部背但至少知道歸并是穩(wěn)定排序、快排平均最快、堆排序可以處理 topK 問(wèn)題。這些是面試題的高頻考點(diǎn)。6. 異常處理、線(xiàn)程基礎(chǔ)和 CompletableFuture從“會(huì)跑”到“能處理問(wèn)題”很多零基礎(chǔ)教程到集合部分就停了導(dǎo)致初學(xué)者寫(xiě)出來(lái)的程序只要輸入一異常就會(huì)崩潰。其實(shí)異常處理和并發(fā)基礎(chǔ)才是 Java 從“能寫(xiě)玩具”到“能上生產(chǎn)”的關(guān)鍵。6.1 異常分類(lèi)與處理順序Java 異常分為受檢異常和非受檢異常。受檢異常必須在編譯時(shí)處理比如文件不存在、網(wǎng)絡(luò)連接失敗非受檢異常是運(yùn)行時(shí)才暴露的比如空指針、數(shù)組越界。處理異常用 try-catch-finallytry 中進(jìn)入 try 塊catch 捕獲并處理異常finally 無(wú)論是否異常都會(huì)執(zhí)行。try { int result 10 / 0; System.out.println(result); } catch (ArithmeticException e) { System.err.println(除零異常: e.getMessage()); } finally { System.out.println(結(jié)束); }捕獲異常時(shí)要注意順序子類(lèi)異常先捕獲父類(lèi)異常后捕獲。如果你先寫(xiě)catch (Exception e)再寫(xiě)catch (ArithmeticException e)編譯器會(huì)報(bào)錯(cuò)因?yàn)樽宇?lèi)異常永遠(yuǎn)不會(huì)被捕獲到。實(shí)際開(kāi)發(fā)中catch 里至少要用日志記錄一下異常信息不要只打印一句“出錯(cuò)了”。6.2 線(xiàn)程基礎(chǔ)和線(xiàn)程池為什么不要手動(dòng) new ThreadJava 并發(fā)基礎(chǔ)首先要理解什么是線(xiàn)程以及為什么多線(xiàn)程能提高效率。但真正落地上我更推薦從一開(kāi)始就使用線(xiàn)程池。手動(dòng) new Thread 的缺點(diǎn)是線(xiàn)程生命周期不好管理高并發(fā)時(shí)容易創(chuàng)建大量線(xiàn)程導(dǎo)致資源耗盡。線(xiàn)程池的簡(jiǎn)單用法ExecutorService pool Executors.newFixedThreadPool(4); for (int i 0; i 10; i) { final int taskId i; pool.submit(() - { System.out.println(Task taskId run on Thread.currentThread().getName()); }); } pool.shutdown();注意submit里的 taskId 必須是 final 或 effectively final否則 lambda 里無(wú)法引用。這是初學(xué)者經(jīng)常碰到的編譯報(bào)錯(cuò)和并發(fā)本身沒(méi)關(guān)系是“變量捕獲”規(guī)則。線(xiàn)程池為什么重要因?yàn)轭l繁創(chuàng)建銷(xiāo)毀線(xiàn)程的成本很高線(xiàn)程池里的線(xiàn)程可以復(fù)用同時(shí)還能控制并發(fā)數(shù)量避免拖垮機(jī)器。面試問(wèn)到線(xiàn)程池時(shí)重點(diǎn)考察的是核心參數(shù)核心線(xiàn)程數(shù)、最大線(xiàn)程數(shù)、隊(duì)列、拒絕策略。6.3 CompletableFuture異步任務(wù)的常用姿勢(shì)CompletableFuture 是 Java 8 引入的異步編程工具適合處理多個(gè)任務(wù)并行執(zhí)行、最后匯總結(jié)果這一類(lèi)場(chǎng)景。它的核心價(jià)值是把回調(diào)地獄變成鏈?zhǔn)秸{(diào)用。CompletableFutureString task1 CompletableFuture.supplyAsync(() - 第一份結(jié)果); CompletableFutureString task2 CompletableFuture.supplyAsync(() - 第二份結(jié)果); CompletableFutureString result task1.thenCombine(task2, (r1, r2) - r1 r2); System.out.println(result.join());如果你用默認(rèn)的 supplyAsync會(huì)使用 ForkJoinPool 公共線(xiàn)程池。在批量任務(wù)里建議自定義線(xiàn)程池避免公共線(xiàn)程池被其他任務(wù)占滿(mǎn)。CompletableFuture 不是銀彈它適合 I/O 密集型的異步任務(wù)如果是 CPU 密集型計(jì)算線(xiàn)程數(shù)不需要太大。還有一個(gè)常見(jiàn)的坑join()和get()的區(qū)別。get()會(huì)拋出檢查異常需要 try-catchjoin()不拋檢查異常更適合鏈?zhǔn)秸{(diào)用。但兩者都會(huì)阻塞當(dāng)前線(xiàn)程所以不要在 UI 線(xiàn)程或主線(xiàn)程里無(wú)腦調(diào)用。6.4 并發(fā)之后再看 Stream 和 IO學(xué)完異常和并發(fā)的零基礎(chǔ)同學(xué)可以補(bǔ)一下 IO 和 NIO 基礎(chǔ)。雖然日常開(kāi)發(fā)中很多框架把 IO 封裝好了但讀取配置文件、導(dǎo)出文件、上傳下載這些場(chǎng)景仍然需要自己寫(xiě) IO 代碼。這部分不必深究先把文件讀寫(xiě)跑通再去看字節(jié)流和字符流的區(qū)別。7. 面向面試和項(xiàng)目八股文怎么學(xué)項(xiàng)目怎么過(guò)渡基礎(chǔ)學(xué)完之后大多數(shù)人的目標(biāo)是找工作或應(yīng)付面試。這時(shí)候就會(huì)遇到 java 面試題、java 八股文、java 面試大全這些資料。我的建議是八股文不是不能背但不能只背你要能把答案轉(zhuǎn)成自己的代碼。7.1 八股文和面試題怎么用java 面試八股文通常包含 JVM、并發(fā)、集合、Spring、MySQL、Redis 等內(nèi)容。零基礎(chǔ)階段你不需要一上來(lái)就背全部八股文因?yàn)楹芏鄡?nèi)容你還沒(méi)有實(shí)際場(chǎng)景硬背會(huì)非常痛苦。更有效的順序是先把 Java 基礎(chǔ)面試題過(guò)一遍題目形式基本都是概念 代碼驗(yàn)證。對(duì)著題目寫(xiě)小 Demo驗(yàn)證答案是否正確。再去看 JVM 內(nèi)存模型、類(lèi)加載機(jī)制、垃圾回收這些進(jìn)階問(wèn)題。比如面試題常問(wèn)“HashMap 的底層結(jié)構(gòu)是什么”。如果你只是背出“數(shù)組 鏈表 紅黑樹(shù)”面試官再問(wèn)一句“為什么超過(guò) 8 會(huì)轉(zhuǎn)紅黑樹(shù)”就可能卡住。正確的做法是看源碼理解鏈表過(guò)長(zhǎng)時(shí)查找效率會(huì)下降轉(zhuǎn)為紅黑樹(shù)是為了將最壞復(fù)雜度從 O(n) 降到 O(log n)。7.2 值得手寫(xiě)的基礎(chǔ)題清單排序算法冒泡、快排、歸并。單例模式懶漢式、餓漢式、雙重檢查。數(shù)組反轉(zhuǎn)、鏈表反轉(zhuǎn)。用 Map 做分組統(tǒng)計(jì)。手寫(xiě)一個(gè)線(xiàn)程池或阻塞隊(duì)列的簡(jiǎn)化版。這些題覆蓋了 java 基礎(chǔ)面試題的高頻范圍。建議每個(gè)題目都寫(xiě)三遍第一遍看書(shū)第二遍合上書(shū)寫(xiě)第三遍不看任何提示直接寫(xiě)。第三遍能做到才算真的掌握。7.3 從 Java 基礎(chǔ)到 Spring Boot過(guò)渡路徑基礎(chǔ)學(xué)完后緊接著可以學(xué) MySQL、JDBC、HTTP、MyBatis、Spring Boot。很多新手直接跳到 Spring Boot 會(huì)懵因?yàn)榭蚣鼙澈笞隽舜罅糠庋b你不知道原理遇到問(wèn)題就不知道從哪里排查。更穩(wěn)妥的順序是JDBC直接用 Java 連數(shù)據(jù)庫(kù)把增刪改查跑一遍。雖然 JDBC 代碼很繁瑣但你能理解連接、預(yù)編譯、結(jié)果集這些概念。MySQL掌握建表、索引、常見(jiàn) SQL 查詢(xún)。HTTP理解請(qǐng)求和響應(yīng)的基本結(jié)構(gòu)。Spring Boot基于前面的知識(shí)寫(xiě)一個(gè)簡(jiǎn)單的 Web 項(xiàng)目。從這個(gè)階段開(kāi)始你會(huì)接觸到一個(gè)新詞叫接口安全。如果你以后做前后端對(duì)接經(jīng)常需要處理 API Key 的傳遞和校驗(yàn)。7.4 接口安全里的一個(gè)常見(jiàn)問(wèn)題API Key 怎么對(duì)接搜索熱詞里出現(xiàn)了 java springboot apikey 安全對(duì)接這是一個(gè)實(shí)戰(zhàn)中很常見(jiàn)的場(chǎng)景。最簡(jiǎn)單的做法是約定一個(gè)請(qǐng)求頭名稱(chēng)客戶(hù)端在每次請(qǐng)求時(shí)帶上 API Key服務(wù)端通過(guò)攔截器或過(guò)濾器校驗(yàn)。Component public class ApiKeyInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String apiKey request.getHeader(X-API-Key); if (!your-secret-key.equals(apiKey)) { response.setStatus(401); return false; } return true; } }這只是一個(gè)最小示例。真實(shí)項(xiàng)目中API Key 不應(yīng)該硬編碼在代碼里而應(yīng)該放在配置中心或環(huán)境變量中。還要考慮是否需要簽名、時(shí)間戳防重放、權(quán)限分級(jí)。你不需要在零基礎(chǔ)階段全做完但至少要知道“接口不是誰(shuí)都可以調(diào)”這個(gè)概念。7.5 學(xué)習(xí)資料和后面的路Java 的學(xué)習(xí)資料非常多但不要同時(shí)收藏多個(gè)教程選定一份主線(xiàn)資源走完就夠了。零基礎(chǔ)階段建議以官方文檔 一本 Java 教程書(shū) 手敲代碼為主。面試階段再去看面試題庫(kù)和歷年真題。學(xué)完基礎(chǔ)和 Spring Boot 之后你就有能力做一個(gè)小后端項(xiàng)目了。項(xiàng)目不在大而在完整有用戶(hù)登錄、有數(shù)據(jù)庫(kù)表、有接口文檔、有錯(cuò)誤處理。做完一個(gè)這樣的項(xiàng)目你對(duì) java 生態(tài)的“成熟分類(lèi)”就會(huì)從概念變成經(jīng)驗(yàn)后面無(wú)論是繼續(xù)學(xué)微服務(wù)還是大數(shù)據(jù)方向都會(huì)順利很多。踩過(guò)幾次環(huán)境問(wèn)題和項(xiàng)目調(diào)試的坑之后你會(huì)發(fā)現(xiàn)很多問(wèn)題并不是 Java 本身難而是輸入格式、路徑、依賴(lài)版本、資源占用這些細(xì)節(jié)沒(méi)有處理好。把基礎(chǔ)階段踩過(guò)的坑記錄下來(lái)比看一百個(gè)教程更有用。如果你暑假只剩兩個(gè)月建議把時(shí)間分成三段前三周環(huán)境加基礎(chǔ)語(yǔ)法中間五周面向?qū)ο蠹蛹虾团判蜃詈髢芍茏鲰?xiàng)目加面試題整理。堅(jiān)持手敲你會(huì)看到進(jìn)度的。