入門實(shí)戰(zhàn):從環(huán)境搭建到高頻報(bào)錯(cuò)排查全攻略)
看到“初入java”這個(gè)標(biāo)題我第一反應(yīng)是想起自己當(dāng)年捧著《Java編程思想》啃到深夜的日子。Java這門語言被喊了十幾年“流行”又被喊了好幾年“卷”結(jié)果到現(xiàn)在企業(yè)后端、大數(shù)據(jù)、Android開發(fā)這些主賽道里它依然是崗位最多、生態(tài)最全的那個(gè)。尤其是在國內(nèi)Java開發(fā)工程師的需求量從來沒有真正冷過。這篇內(nèi)容主要寫給兩類人一是完全零基礎(chǔ)、準(zhǔn)備自學(xué)的同學(xué)二是正在準(zhǔn)備校招面試或者藍(lán)橋杯這類競賽、需要把基礎(chǔ)重新捋一遍的同學(xué)。我不打算寫那種“XX天精通Java”的雞湯也不打算堆一堆聽不懂的術(shù)語。我想用實(shí)際踩坑的經(jīng)驗(yàn)告訴你Java入門到底要學(xué)哪些東西環(huán)境怎么搭哪些語法點(diǎn)是高頻坑遇到報(bào)錯(cuò)怎么排查以及學(xué)完基礎(chǔ)之后下一步該往哪兒走。內(nèi)容會(huì)盡量保持“能直接照著做”的實(shí)操性每一條都是我自己或身邊同事真實(shí)經(jīng)歷過的。1. 先把方向定下來零基礎(chǔ)Java學(xué)習(xí)路線怎么走1.1 初學(xué)Java前先弄明白它到底能做什么很多人學(xué)Java是被就業(yè)市場帶節(jié)奏學(xué)了兩周還不知道自己寫的東西跑在哪。其實(shí)理解Java能做什么比急著寫代碼更重要。Java最主流的陣地是服務(wù)端開發(fā)。你在網(wǎng)頁上點(diǎn)的每一個(gè)“登錄”“下單”背后的處理邏輯大概率跑在一臺(tái)裝有Java程序的服務(wù)器上。市面上大量的互聯(lián)網(wǎng)公司核心業(yè)務(wù)系統(tǒng)用的就是Java技術(shù)棧。除此之外Android應(yīng)用的核心邏輯層也用Java現(xiàn)在有Kotlin但底層依然是JVM生態(tài)大數(shù)據(jù)領(lǐng)域的Hadoop、Spark這些重量級(jí)框架同樣跑在JVM上??梢哉f只要和數(shù)據(jù)、業(yè)務(wù)邏輯打交道的系統(tǒng)Java都是絕對(duì)主力。初學(xué)者需要建立一個(gè)基本認(rèn)知Java程序并不是直接被電腦CPU執(zhí)行的而是先編譯成一種叫“字節(jié)碼”的中間文件再由一個(gè)叫“虛擬機(jī)”JVM的運(yùn)行時(shí)環(huán)境去解釋和編譯執(zhí)行。這也是Java當(dāng)年那句“一處編寫到處運(yùn)行”的底氣——只要目標(biāo)機(jī)器上裝了對(duì)應(yīng)版本的JVM同一個(gè)字節(jié)碼文件就能跑。所以網(wǎng)上偶爾看到有人說“Java是靜態(tài)鏈接的”這個(gè)說法并不準(zhǔn)確Java的類加載和鏈接其實(shí)發(fā)生在運(yùn)行時(shí)和C/C那種編譯期靜態(tài)鏈接完全是兩碼事。理解這一點(diǎn)對(duì)你后面看各種“程序啟動(dòng)失敗”“類找不到”之類的報(bào)錯(cuò)特別有幫助。1.2 我推薦的四階段學(xué)習(xí)路線不要一上來就奔著Spring Boot這類框架去??蚣苤皇枪ぞ叩鼗焕蔚脑捒蚣芾锏囊粋€(gè)異常都能讓你卡一整天。我比較推薦下面這條四階段路線整體周期大約4到6個(gè)月每天保證2到3小時(shí)有效學(xué)習(xí)時(shí)間。階段一Java基礎(chǔ)語法面向?qū)ο笏枷爰s2到3個(gè)月。內(nèi)容包括數(shù)據(jù)類型、變量、運(yùn)算符、流程控制if/for/while、方法定義與調(diào)用、數(shù)組然后是面向?qū)ο笕筇卣鞣庋b、繼承、多態(tài)。這個(gè)階段結(jié)束后你應(yīng)該能脫離教程獨(dú)立寫一個(gè)“學(xué)生成績管理系統(tǒng)”這樣的控制臺(tái)程序。階段二核心類庫與編程基本功約1到1.5個(gè)月。重點(diǎn)學(xué)集合框架List、Set、Map以及常用的ArrayList、HashMap的實(shí)現(xiàn)原理、異常處理機(jī)制、String字符串相關(guān)API、IO流文件讀寫、多線程基礎(chǔ)。這個(gè)階段可以做一些小練習(xí)比如用IO流做一個(gè)文件批量重命名工具用多線程模擬多個(gè)窗口同時(shí)賣票。階段三數(shù)據(jù)庫與JDBC約3到4周。后端開發(fā)離不開數(shù)據(jù)庫。學(xué)會(huì)MySQL的基本建表、增刪改查、索引和外鍵概念然后學(xué)JDBC——它是Java連接數(shù)據(jù)庫的標(biāo)準(zhǔn)接口。這個(gè)階段做完你應(yīng)該能用JDBC實(shí)現(xiàn)一個(gè)完整的注冊(cè)、登錄、信息查詢的控制臺(tái)程序。階段四Web開發(fā)入門約1到2個(gè)月。先補(bǔ)一點(diǎn)HTML、CSS、JavaScript基礎(chǔ)理解網(wǎng)頁長什么樣、怎么和后端交互。然后可以學(xué)Servlet和Tomcat的原理也可以直接切入Spring Boot——我個(gè)人建議是從Servlet源碼原理走一遍哪怕只是看懂請(qǐng)求是怎么被處理的再上Spring Boot就順很多。最后做一個(gè)“留言板”或“簡易博客”項(xiàng)目收尾。1.3 新手最容易走偏的三個(gè)地方第一個(gè)坑是“收藏即學(xué)會(huì)”。很多同學(xué)B站收藏夾里存了幾十個(gè)教程結(jié)果連第一個(gè)視頻的Demo都沒運(yùn)行過。學(xué)編程沒有捷徑必須手上敲代碼眼睛看報(bào)錯(cuò)。第二個(gè)坑是“死磕基礎(chǔ)不敢往下走”。有同學(xué)覺得繼承和多態(tài)沒完全搞懂就反復(fù)看視頻一個(gè)章節(jié)看兩星期。我的建議是基礎(chǔ)看一遍、看懂六七成就可以往前進(jìn)很多概念是后面寫項(xiàng)目時(shí)才真正理解的。你需要的不是一次學(xué)透而是建立“這個(gè)知識(shí)點(diǎn)存在”的索引等項(xiàng)目里遇到了再回頭深挖。第三個(gè)坑是“過早追求熱門框架”。上周有人問我“基礎(chǔ)學(xué)到集合能不能直接學(xué)Spring Cloud微服務(wù)”。這種想法相當(dāng)危險(xiǎn)。微服務(wù)涉及分布式、消息隊(duì)列、容器化一大堆進(jìn)階知識(shí)新手進(jìn)去除了背概念什么都得不到。先把單體Web應(yīng)用跑明白比什么都強(qiáng)。2. 環(huán)境搭建實(shí)操JDK安裝、環(huán)境變量與第一個(gè)程序2.1 JDK版本選型不要糾結(jié)先選LTS版本新手最容易在第一步就被版本問題勸退JDK 8、JDK 11、JDK 17、JDK 21到底裝哪個(gè)我的建議很明確直接選當(dāng)前最新的長期支持版本LTS也就是JDK 17或JDK 21。JDK 8雖然是國內(nèi)老項(xiàng)目的最愛很多公司生產(chǎn)環(huán)境還在用但它畢竟是2014年發(fā)布的版本對(duì)新手來說新版本在性能、語法糖、診斷工具上都要好不少。更重要的是學(xué)的時(shí)候用新版本面試時(shí)你能說清楚LTS版本之間的演進(jìn)差異這本身就是加分項(xiàng)。下載渠道方面如果不想折騰商業(yè)授權(quán)問題就選OpenJDK發(fā)行版。比如Adoptium社區(qū)構(gòu)建版完全免費(fèi)直接去官網(wǎng)下載對(duì)應(yīng)操作系統(tǒng)的安裝包即可。安裝時(shí)記住你的安裝路徑比如C:\Program Files\Java\jdk-17后面配置環(huán)境變量要用。Windows用戶建議直接下載.msi安裝包下一步下一步就裝完了不用手動(dòng)解壓配置。2.2 Windows 11環(huán)境變量配置完整步驟環(huán)境變量配置是勸退新手的第一座大山實(shí)際上原理非常簡單。你需要讓操作系統(tǒng)知道兩件事去哪找Java的開發(fā)工具以及去哪找Java的運(yùn)行環(huán)境。具體步驟如下我以Windows 11為例Win10操作完全一致在開始菜單搜索“環(huán)境變量”打開“編輯系統(tǒng)環(huán)境變量”點(diǎn)擊“環(huán)境變量”按鈕在“系統(tǒng)變量”區(qū)域點(diǎn)擊“新建”變量名填JAVA_HOME變量值填你的JDK安裝路徑比如C:\Program Files\Java\jdk-17找到Path變量雙擊它點(diǎn)擊“新建”加上%JAVA_HOME%\bin一路點(diǎn)確定關(guān)掉所有窗口然后重新打開一個(gè)命令行窗口注意是“重新打開”不是用原來的窗口在命令行輸入java -version如果輸出版本信息說明JRE正常識(shí)別再輸入javac -version如果能輸出版本信息說明編譯器也正常識(shí)別。這里回答一個(gè)高頻疑問為什么一定要配JAVA_HOME而不是直接把JDK的bin目錄寫進(jìn)Path因?yàn)椴恢姑钚幸肑ava你的IDE、Maven、Tomcat這些工具都會(huì)通過讀取JAVA_HOME這個(gè)變量來定位JDK。你只配PathIDE能跑但Maven可能就罷工了。而配好JAVA_HOME所有工具就都認(rèn)識(shí)了。順便提一句CLASSPATH。網(wǎng)上很多老教程讓你配CLASSPATH其實(shí)從JDK 1.5開始當(dāng)前目錄已經(jīng)默認(rèn)包含在類加載路徑中日常學(xué)習(xí)和開發(fā)不需要手動(dòng)配置。新手看到雜七雜八的教程不要慌按我上面這套來就夠了。2.3 編譯運(yùn)行原理與第一個(gè)Java程序配好環(huán)境我們先不急著打開IDE用最原始的方式感受一下Java程序的編譯運(yùn)行過程。在任意目錄創(chuàng)建一個(gè)HelloWorld.java文件用記事本或其他文本編輯器寫入public class HelloWorld { public static void main(String[] args) { System.out.println(Hello, Java!); } }然后在命令行切換到該文件所在目錄依次執(zhí)行javac HelloWorld.java java HelloWorldjavac是Java編譯器它把.java源碼文件編譯成.class字節(jié)碼文件java命令則是啟動(dòng)JVM去加載這個(gè)字節(jié)碼并執(zhí)行。這里有幾個(gè)新手常踩的細(xì)節(jié)第一public class的名稱必須和文件名完全一致包括大小寫。HelloWorld.java里面的類名就必須是HelloWorld寫成helloworld就編譯報(bào)錯(cuò)。第二main方法的簽名固定是public static void main(String[] args)這是JVM識(shí)別程序入口的唯一標(biāo)準(zhǔn)漏掉任何一個(gè)修飾符都會(huì)導(dǎo)致“找不到主方法”的錯(cuò)誤。第三如果你用Windows記事本保存源碼保存時(shí)編碼要選UTF-8否則中文注釋在編譯時(shí)會(huì)報(bào)“不可映射的字符”或亂碼。這個(gè)“編譯運(yùn)行”兩步走的過程本質(zhì)上就是Java跨平臺(tái)原理的縮影。你寫完的高層邏輯先交給編譯器翻譯成與平臺(tái)無關(guān)的字節(jié)碼再由不同平臺(tái)上的JVM翻譯成本地機(jī)器指令。所以排查“程序啟動(dòng)失敗”時(shí)腦子里要有一條清晰鏈源碼有沒有編譯成功、編譯出的class文件能不能被找到、JVM能不能正確加載并執(zhí)行它。這條鏈捋順了后面80%的報(bào)錯(cuò)你都能自己定位。2.4 IDE選擇IDEA社區(qū)版足夠用了寫完第一個(gè)控制臺(tái)程序就可以上IDE來提升效率了。我通常建議新手直接用IntelliJ IDEA社區(qū)版免費(fèi)且功能足夠。IDEA智能提示比Eclipse好一個(gè)數(shù)量級(jí)。你還沒輸入完的類名、方法名它都能給你補(bǔ)全編寫過程中的低級(jí)語法錯(cuò)誤比如少個(gè)分號(hào)、變量名寫錯(cuò)會(huì)立刻在編輯區(qū)標(biāo)紅。這是減少初學(xué)者挫敗感最有效的工具沒有之一。還有Eclipse的老用戶會(huì)說Eclipse啟動(dòng)快但真到大型項(xiàng)目里IDEA的索引和重構(gòu)能力優(yōu)勢非常明顯新人不建議折騰遷移成本。VSCode也可以寫Java裝上Java Extension Pack后補(bǔ)全、調(diào)試、Maven都支持。但對(duì)零基礎(chǔ)的人來說VSCode的配置門檻反而比IDEA高很多環(huán)境問題會(huì)把人勸退。所以新人的最優(yōu)解就是IDEA社區(qū)版沒有其他。3. 核心基礎(chǔ)語法精講數(shù)據(jù)類型、流程控制與面向?qū)ο?.1 數(shù)據(jù)類型與變量先分清基本類型和引用類型Java的變量分兩大類基本類型和引用類型?;绢愋鸵馕吨兞恐苯哟鎯?chǔ)值引用類型則存儲(chǔ)一個(gè)“地址”真實(shí)的對(duì)象在堆內(nèi)存里?;绢愋凸灿?種面試也常考我整理了一張表類型占用空間取值范圍或說明默認(rèn)值byte1字節(jié)-128 到 1270short2字節(jié)-32768 到 327670int4字節(jié)約 -21億 到 21億0long8字節(jié)更大范圍字面量需加L后綴0Lfloat4字節(jié)單精度浮點(diǎn)字面量需加F后綴0.0fdouble8字節(jié)雙精度浮點(diǎn)默認(rèn)浮點(diǎn)類型0.0char2字節(jié)單個(gè)字符用單引號(hào)括起\u0000boolean理論1字節(jié)true或falsefalse新手最容易踩的坑有幾個(gè)。第一是long longValue 12345678901234;編譯報(bào)錯(cuò)因?yàn)檎麛?shù)默認(rèn)按int處理超出int范圍必須寫成12345678901234L。第二是float f 1.1;報(bào)錯(cuò)因?yàn)樾?shù)默認(rèn)是double必須強(qiáng)制寫成float f 1.1F;。第三是浮點(diǎn)精度問題0.1 0.2的結(jié)果不是精細(xì)的0.3而是類似0.30000000000000004因?yàn)槎M(jìn)制無法精確表示所有十進(jìn)制小數(shù)。做金額計(jì)算時(shí)絕不直接用double要改用BigDecimal這也是面試題“浮點(diǎn)數(shù)能用來做金融計(jì)算嗎”的答案核心。3.2 標(biāo)識(shí)符命名規(guī)則與常見錯(cuò)誤給變量、方法、類起名字Java有明確的“語法規(guī)則”和約定俗成的“規(guī)范”很多新手分不清這兩者面試時(shí)也常被問。語法規(guī)則是硬性的違反就編譯錯(cuò)誤可以由字母、數(shù)字、下劃線_、美元符號(hào)$組成數(shù)字不能作為開頭不能是Java關(guān)鍵字如class、public、int、if都不行區(qū)分大小寫name和Name是兩個(gè)變量。下面是幾個(gè)典型的正誤對(duì)比寫法是否合法說明int 2abc;錯(cuò)誤數(shù)字開頭int $name;合法但不推薦雖然不報(bào)錯(cuò)見名知意的團(tuán)隊(duì)規(guī)范中盡量避免int class;錯(cuò)誤class是關(guān)鍵字int _age;合法但不建議下劃線開頭雖合法Java 9之后保留了這種風(fēng)格但盡量別用int maxValue;合法且推薦駝峰命名命名規(guī)范雖然不強(qiáng)制但代碼是給人讀的。類名用大駝峰如StudentManager方法名和變量名用小駝峰如getUserName、userName常量全大寫加下劃線如MAX_PAGE_SIZE。你在面試中寫一手規(guī)范命名的代碼面試官對(duì)你的印象分立刻不同。3.3 流程控制與冒泡排序?qū)崿F(xiàn)流程控制是寫程序的骨架順序、條件、循環(huán)。條件用if/else和switch循環(huán)用for和while。這里面沒有特別難理解的點(diǎn)關(guān)鍵是配合數(shù)組一起練。熱詞里多次出現(xiàn)“java排序”“冒泡排序java”說明這是個(gè)繞不過去的基礎(chǔ)算法我這里就用手寫數(shù)組排序來串一下。int[] arr {5, 3, 8, 1, 9, 2}; for (int i 0; i arr.length - 1; i) { 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; } } } System.out.println(Arrays.toString(arr));這段冒泡排序的思路是每一輪從頭開始比較相鄰兩個(gè)元素大的往后挪這樣一輪結(jié)束時(shí)最大的數(shù)就會(huì)“浮”到數(shù)組末尾第二輪再把第二大的數(shù)排到倒數(shù)第二個(gè)位置……所以外層循環(huán)走arr.length - 1輪內(nèi)層每輪要比較的次數(shù)逐漸減一。代碼里arr.length - 1 - i這個(gè)- i是精髓少了它程序不會(huì)報(bào)錯(cuò)但會(huì)做大量無意義的比較。冒泡排序的時(shí)間復(fù)雜度是O(n2)這個(gè)知識(shí)點(diǎn)面試常問。其實(shí)真正工程里很少用它Arrays.sort()內(nèi)部用更高效的排序算法但手寫冒泡的意義在于訓(xùn)練“循環(huán)嵌套數(shù)組索引”的基本功。很多新手寫這個(gè)程序時(shí)把內(nèi)層循環(huán)條件寫成j arr.length - 1 - i導(dǎo)致j1越界報(bào)錯(cuò)這就和下一節(jié)要講的數(shù)組越界問題相遇了。3.4 面向?qū)ο箢?、?duì)象、封裝繼承多態(tài)面向?qū)ο笫荍ava的核心思想也是新手從“寫腳本”邁向“做系統(tǒng)”的轉(zhuǎn)折點(diǎn)。我用一個(gè)生活類比解釋類相當(dāng)于建筑圖紙對(duì)象就是按照這張圖紙蓋出來的真實(shí)房子。你可以在同一個(gè)圖紙基礎(chǔ)上蓋無數(shù)棟外觀一樣的房子每棟房子可以有不同的主人、不同的裝修但結(jié)構(gòu)設(shè)計(jì)都遵循圖紙。代碼里也是這樣類定義“有什么屬性、能做什么行為”對(duì)象是這些描述在內(nèi)存里的具體實(shí)例。封裝是隱藏內(nèi)部細(xì)節(jié)對(duì)外提供穩(wěn)定接口。做法是把成員變量設(shè)為private然后提供public的getter和setter方法。比如一個(gè)User類的age字段你可以在setter里加判斷邏輯防止負(fù)年齡寫進(jìn)去這就是封裝的價(jià)值——保護(hù)數(shù)據(jù)不被外部隨意破壞。繼承用extends關(guān)鍵字子類復(fù)用父類的成員變量和公共方法。比如Dog繼承Animal就自動(dòng)擁有了eat()方法。多態(tài)則表現(xiàn)為“父類引用指向子類對(duì)象”代碼寫在父類類型上運(yùn)行時(shí)卻能調(diào)動(dòng)子類的具體行為。例如Animal a new Dog(); a.makeSound(); // 輸出狗叫而非動(dòng)物叫這里還有一個(gè)高頻面試區(qū)分點(diǎn)重載Overload和重寫Override。重載是同一個(gè)類里方法名相同、參數(shù)列表不同比如兩個(gè)print方法一個(gè)接int一個(gè)接String重寫是子類對(duì)父類同名同參方法的重新實(shí)現(xiàn)必須加Override注解。新手寫代碼時(shí)容易把兩者搞混記住一句口訣重載是“同一類內(nèi)換參數(shù)”重寫是“父子之間換實(shí)現(xiàn)”。3.5 String與常見字符判斷技巧String是Java里最特殊的對(duì)象面試出場率極高核心考點(diǎn)是“不可變性”。也就是說創(chuàng)建出的字符串對(duì)象一旦定型內(nèi)容就不能變。執(zhí)行String s abc; s s d;時(shí)并不是把原來的abc改成abcd而是新創(chuàng)建了一個(gè)abcd對(duì)象原來的abc變成垃圾等待回收。因此大量拼接字符串時(shí)直接運(yùn)算會(huì)不停創(chuàng)建新對(duì)象性能很差正確做法是用StringBuilder或StringBuffer。字符串比較是新手重災(zāi)區(qū)。判斷字符串內(nèi)容相不相等必須用equals()不能直接用。在比較引用類型時(shí)比較的是“兩個(gè)引用是否指向同一個(gè)對(duì)象”而字符串內(nèi)容相同并不代表它們是同一個(gè)對(duì)象。熱詞里有“java 判斷字符串中是否不是字母和數(shù)字”這可以用正則表達(dá)式一行解決String input abc123; boolean isAlphaNumeric input.matches([a-zA-Z0-9]); System.out.println(isAlphaNumeric); // true如果只是想判斷單個(gè)字符可以用Character.isLetterOrDigit(char)方法。實(shí)際開發(fā)中這種校驗(yàn)常出現(xiàn)在登錄注冊(cè)、表單參數(shù)校驗(yàn)場景后端必須做不能只靠前端攔截。3.6 異常機(jī)制與數(shù)組越界問題程序運(yùn)行出錯(cuò)不叫“崩潰”而是拋異常。Java用try-catch-finally機(jī)制兜住錯(cuò)誤避免程序直接終止?;窘Y(jié)構(gòu)是try { // 可能出錯(cuò)的代碼 } catch (ArrayIndexOutOfBoundsException e) { System.out.println(數(shù)組下標(biāo)越界了 e.getMessage()); } finally { // 無論如何都會(huì)執(zhí)行的代碼一般用來釋放資源 }熱詞里專門提到“java中數(shù)組越界異常”這是新手每天都會(huì)遇到的報(bào)錯(cuò)核心原因其實(shí)就一條訪問了不存在的下標(biāo)。比如數(shù)組長度是5有效下標(biāo)是0到4你寫arr[5]就必然觸發(fā)ArrayIndexOutOfBoundsException。這種錯(cuò)誤的排查思路很固定先看報(bào)錯(cuò)行號(hào)再看那行代碼訪問的是哪個(gè)數(shù)組的下標(biāo)最后看循環(huán)邊界或手動(dòng)下標(biāo)是不是超出長度減一。空指針異常NullPointerException則是另一個(gè)高頻異常還記得當(dāng)年同事半夜被線上空指針叫起來的場景。它的產(chǎn)生條件就是對(duì)象引用為null卻去調(diào)用它的方法或字段。排查空指針唯一務(wù)實(shí)的方法是在報(bào)錯(cuò)位置前后打日志或斷點(diǎn)確認(rèn)是哪個(gè)對(duì)象沒賦值。業(yè)務(wù)代碼里養(yǎng)成一個(gè)習(xí)慣接收外部參數(shù)時(shí)先判空能減少絕大多數(shù)空指針。4. 新手高頻報(bào)錯(cuò)與問題排查實(shí)錄4.1 環(huán)境配置類報(bào)錯(cuò)“javac不是內(nèi)部或外部命令”是環(huán)境配置階段最經(jīng)典的報(bào)錯(cuò)??吹竭@句話先別慌按順序排查三件事第一你是不是新開了命令行窗口環(huán)境變量是登錄時(shí)讀取的修改后舊窗口不會(huì)刷新一定要重開。第二JAVA_HOME和Path這兩項(xiàng)都檢查一遍Path里的內(nèi)容必須是%JAVA_HOME%\bin注意是英文半角符號(hào)不要復(fù)制進(jìn)去奇怪的引號(hào)或空格。第三確認(rèn)JDK安裝路徑真存在這個(gè)目錄打開資源管理器看一眼有可能你安裝到了D:\或其他位置而JAVA_HOME填成了默認(rèn)路徑。“找不到或無法加載主類”是另一種高頻問題。執(zhí)行java HelloWorld時(shí)JVM去當(dāng)前目錄找HelloWorld.class文件。找不到的原因可能是你忘記先執(zhí)行javac編譯步驟或者.class文件不在當(dāng)前目錄下。新手經(jīng)常在編譯成功但文件在別的文件夾的情況下去運(yùn)行命令行當(dāng)前路徑和文件路徑對(duì)不上就會(huì)出現(xiàn)這個(gè)錯(cuò)。還有一個(gè)隱蔽情況類聲明了package包名運(yùn)行就必須用全限定名比如java com.example.HelloWorld。4.2 編譯與編碼類問題“編碼GBK的不可映射字符”這報(bào)錯(cuò)基本是中文惹的禍。Windows命令行默認(rèn)編碼和源碼文件UTF-8編碼不一致時(shí)編譯中文注釋或中文字符串就會(huì)報(bào)錯(cuò)。解決辦法有兩個(gè)一是把所有源碼保存為GBK編碼不推薦跨平臺(tái)會(huì)亂二是用javac -encoding UTF-8 HelloWorld.java指定編譯編碼。如果你用IDEA就基本沒有這個(gè)問題IDE會(huì)統(tǒng)一管理源碼編碼。另外還有類名相關(guān)問題。public class修飾的類名與文件名不一致時(shí)編譯器直接報(bào)“類A是公共的應(yīng)在名為A.java的文件中聲明”。這提醒并不是Java強(qiáng)制限制而是JVM規(guī)范要求公共類的字節(jié)碼文件必須與類名匹配否則JVM無法正確加載。養(yǎng)成“一個(gè)Java文件只寫一個(gè)public類類名和文件名保持一致”的習(xí)慣能避開不少坑。4.3 代碼運(yùn)行類異常排查我整理了一份新手高頻異常速查表大家可以收藏起來對(duì)照異常現(xiàn)象根本原因排查方向ArrayIndexOutOfBoundsException訪問了不存在的數(shù)組下標(biāo)檢查循環(huán)邊界打印數(shù)組長度NullPointerException調(diào)用了null對(duì)象的方法找出哪個(gè)引用為null先判空再使用ClassCastException強(qiáng)制類型轉(zhuǎn)換失敗檢查對(duì)象真實(shí)類型用instanceof提前判斷InputMismatchExceptionScanner讀入類型與預(yù)期不符確認(rèn)輸入格式用hasNextXXX預(yù)檢查ArithmeticException除數(shù)為0檢查除法運(yùn)算分母先判0NumberFormatException字符串轉(zhuǎn)數(shù)字失敗確認(rèn)字符串內(nèi)容是純數(shù)字這里特別說一下NumberFormatException天天有人踩。Integer.parseInt(abc)或Integer.parseInt(12.5)都會(huì)拋這個(gè)異常因?yàn)樽址皇呛戏ǖ恼麛?shù)字面量。做用戶輸入解析時(shí)一定先捕獲異常或者用正則校驗(yàn)不要直接轉(zhuǎn)換。比如0021能轉(zhuǎn)成21但21.0就不行這個(gè)差異很多人不知道。4.4 程序啟動(dòng)失敗的一般排查思路熱詞里有“java啟動(dòng)失敗怎么解決”這類問題范圍很廣我提供一個(gè)通用的排查順序能解決絕大多數(shù)場景。第一步看日志。任何Java程序啟動(dòng)失敗都要先看控制臺(tái)完整錯(cuò)誤輸出不要只看紅色關(guān)鍵詞就慌。日志里一般會(huì)寫明異常類型和出錯(cuò)代碼行號(hào)。第二步分三層排查編譯層、運(yùn)行層、依賴層。編譯層看代碼有沒有語法錯(cuò)誤運(yùn)行層看類路徑是否正確、端口被占用依賴層看所需jar包是否齊全。第三步用命令驗(yàn)證基礎(chǔ)環(huán)境java -version能否輸出、javac -version是否可用、JAVA_HOME是否正確。如果是在IDE里點(diǎn)運(yùn)行按鈕啟動(dòng)失敗優(yōu)先檢查項(xiàng)目配置JDK版本是否選對(duì)熱詞里提到“pcl(java版啟動(dòng)器)”那類第三方啟動(dòng)器找不到Java的情況本質(zhì)也是JAVA_HOME路徑問題處理方式和環(huán)境變量排查完全一致。再補(bǔ)充一個(gè)實(shí)戰(zhàn)技巧任何奇怪的啟動(dòng)問題先重啟IDE和Java進(jìn)程很多資源占用或狀態(tài)未刷新的問題就迎刃而解了。這不是玄學(xué)Java進(jìn)程持有的資源沒釋放時(shí)二次啟動(dòng)會(huì)直接報(bào)端口綁定失敗重啟后一切恢復(fù)。5. 面向面試與競賽入門階段還可以這樣積累5.1 高頻Java基礎(chǔ)面試題一覽很多初學(xué)者學(xué)到一半會(huì)焦慮“面試考什么”。說實(shí)話校招面試官手一面基礎(chǔ)題時(shí)翻來覆去就那幾道關(guān)鍵是你能不能講得清晰、舉例到位。我挑了幾個(gè)最常見的基礎(chǔ)題附上回答要點(diǎn)面試題核心回答方向和equals的區(qū)別比較基本類型值時(shí)比較值比較引用類型時(shí)比較地址equals默認(rèn)也是比較地址但String等類重寫后比較內(nèi)容String、StringBuilder、StringBuffer的區(qū)別String不可變、拼接效率低StringBuilder線程不安全但快StringBuffer線程安全但慢基本類型和引用類型的區(qū)別基本類型存值引用類型存地址引用類型可以有方法調(diào)用可以賦nullArrayList和LinkedList的區(qū)別ArrayList基于動(dòng)態(tài)數(shù)組隨機(jī)訪問快、插入刪除慢LinkedList基于雙向鏈表插入刪除快、隨機(jī)訪問慢HashMap的底層結(jié)構(gòu)數(shù)組鏈表紅黑樹。鍵值對(duì)先算hash定位到數(shù)組下標(biāo)沖突時(shí)掛鏈表鏈表過長轉(zhuǎn)紅黑樹JDK 8靜態(tài)變量和實(shí)例變量的區(qū)別靜態(tài)變量屬于類內(nèi)存中只有一份實(shí)例變量屬于對(duì)象每個(gè)對(duì)象獨(dú)立一份重載和重寫的區(qū)別重載看參數(shù)列表重寫看繼承關(guān)系方法簽名必須相同記住基礎(chǔ)題考的不是背誦能力而是理解深度。比如問HashMap面試官往往追著問“為什么要用紅黑樹”“鏈表多長才轉(zhuǎn)紅黑樹”你哪怕答不完全能說清“為了解決hash沖突后鏈表過長導(dǎo)致查詢變慢”這個(gè)初衷就已經(jīng)超過一大批人。順便說一句“java八股文”這個(gè)詞。很多同學(xué)覺得背八股無用但從我的角度看八股本質(zhì)上是對(duì)基礎(chǔ)原理的一種歸納整理。你背的是答案但如果你連答案都說不全說明基礎(chǔ)確實(shí)存在盲區(qū)。更好的策略是以面試題為索引逐個(gè)回溯到源碼或?qū)嶒?yàn)中去理解把“背答案”變成“驗(yàn)答案”學(xué)完一遍基礎(chǔ)會(huì)非常扎實(shí)。5.2 競賽視角藍(lán)橋杯與OJ刷題的基本功熱詞里有“java 藍(lán)橋杯 數(shù)字題目”說明不少在校生是帶著競賽目的來學(xué)Java的。藍(lán)橋杯Java組的題目前期基本都是算法與數(shù)據(jù)結(jié)構(gòu)基本功這里分享一下我的刷題思路。第一吃透輸入輸出。競賽和LeetCode不一樣很多題需要自己控制標(biāo)準(zhǔn)輸入輸出。新手推薦用BufferedReader而不是Scanner因?yàn)榇髷?shù)據(jù)量下Scanner會(huì)超時(shí)BufferedReader reader new BufferedReader(new InputStreamReader(System.in)); int n Integer.parseInt(reader.readLine()); String[] arr reader.readLine().split( );第二熟悉工具類。排序用Arrays.sort()集合排序用Collections.sort()統(tǒng)計(jì)用HashMap。很多題目不需要你手寫排序樹等復(fù)雜結(jié)構(gòu)直接調(diào)庫不但省時(shí)間還能降低出錯(cuò)率。練習(xí)的重點(diǎn)應(yīng)該放在如何把題目問題抽象成數(shù)據(jù)結(jié)構(gòu)操作而不是重復(fù)造輪子。第三按專題刷題。我的推薦順序是模擬題培養(yǎng)讀題能力→ 字符串處理 → 排序 → 遞歸 → 搜索DFS/BFS→ 動(dòng)態(tài)規(guī)劃入門。每個(gè)專題刷10到15題再進(jìn)入下一個(gè)。不要一開始就死磕動(dòng)態(tài)規(guī)劃基礎(chǔ)不牢很容易放棄。競賽和考試有一點(diǎn)很像狀態(tài)穩(wěn)定比超常發(fā)揮重要。每次模擬練習(xí)都按真實(shí)考試時(shí)間限制來做訓(xùn)練自己的debug速度和準(zhǔn)確率。5.3 從入門到進(jìn)階下一步學(xué)什么學(xué)完基礎(chǔ)語法和面向?qū)ο笙喈?dāng)于掌握了一把工具但離真正工作還差一個(gè)“組裝能力”的環(huán)節(jié)。這里我按后端方向給出下一階段的建議。先學(xué)數(shù)據(jù)庫原理和SQL優(yōu)化MySQL是必選項(xiàng)。接著學(xué)Java Web核心HTTP協(xié)議、Servlet生命周期、Tomcat原理這一步讓你理解瀏覽器請(qǐng)求是怎么到達(dá)Java代碼并被處理的。然后學(xué)Spring和Spring Boot重點(diǎn)理解控制反轉(zhuǎn)IoC和面向切面AOP這兩個(gè)核心思想而不僅僅是會(huì)寫Controller注解。之后再接觸Redis緩存、消息隊(duì)列、微服務(wù)這些是分布式架構(gòu)層面的事屬于進(jìn)階話題但有了前面的基礎(chǔ)學(xué)起來就是水到渠成。熱詞里還提到了“java設(shè)計(jì)模式”“java定時(shí)任務(wù)框架”。設(shè)計(jì)模式不用刻意學(xué)寫代碼遇到“多個(gè)類都有同一個(gè)行為但實(shí)現(xiàn)不同”“某段邏輯頻繁變化需要抽象”這些場景時(shí)再去回頭翻模板方法、策略、觀察者這些模式理解會(huì)深很多。定時(shí)任務(wù)框架像Quartz、XXL-JOB是后端開發(fā)常見需求等你做完一兩個(gè)Web項(xiàng)目后接觸它們就不會(huì)覺得難它們本質(zhì)上是“幫你按時(shí)執(zhí)行某段邏輯”的工具包。說點(diǎn)掏心窩的話。我自己當(dāng)年初學(xué)Java踩得最深的一個(gè)坑就是總想等“完全準(zhǔn)備好”再開始寫東西結(jié)果視頻看了幾十集自己一行代碼沒敲。后來導(dǎo)師給我一句話點(diǎn)醒代碼是敲出來的不是看會(huì)的。從那以后我每周逼自己寫一個(gè)小程序哪怕是個(gè)貪吃蛇、哪怕是個(gè)待辦清單效果比看十小時(shí)視頻都強(qiáng)。最后再分享一個(gè)小技巧入門階段遇到報(bào)錯(cuò)直接把報(bào)錯(cuò)信息復(fù)制到搜索引擎里搜英文信息就搜英文中文信息就帶上Java關(guān)鍵字。這個(gè)方法能解決你90%的困惑。Java這條路很長但門檻真的不高只要你肯動(dòng)手敲、肯耐心讀報(bào)錯(cuò)、肯堅(jiān)持半年回頭看第一個(gè)HelloWorld跑通時(shí)那個(gè)興奮勁兒一切都是值得的。