入門學(xué)習(xí)路線:環(huán)境搭建與核心語法要點(diǎn))
從零開始學(xué)Java最該先想明白的一件事你學(xué)它到底是為了寫什么。是為了學(xué)校課程、轉(zhuǎn)行做后端、還是想搞Android開發(fā)目標(biāo)不同發(fā)力點(diǎn)完全不一樣。但不管什么目標(biāo)Java的基礎(chǔ)部分繞不開那幾塊東西語法、面向?qū)ο?、集合框架、異常處理、IO流、并發(fā)。今天這篇我就按自己帶新人時(shí)總結(jié)出來的路線把入門階段最核心的內(nèi)容、最容易踩的坑、以及每個(gè)環(huán)節(jié)的學(xué)習(xí)方法一次講清楚看完你就可以照著往下走了。1. 入門之前先把Java這輛車的結(jié)構(gòu)搞清楚1.1 JDK、JRE、JVM不是三個(gè)名詞是三層層關(guān)系很多初學(xué)者上來就背“JDK是開發(fā)工具包、JRE是運(yùn)行環(huán)境、JVM是虛擬機(jī)”背完就混了。我換一個(gè)生活化的說法你把Java當(dāng)成一臺(tái)能跑程序的機(jī)器。JVMJava虛擬機(jī)就是這臺(tái)機(jī)器的“心臟”負(fù)責(zé)執(zhí)行Java程序編譯出來的字節(jié)碼。沒有它程序跑不起來。JRE運(yùn)行環(huán)境是“心臟電源基礎(chǔ)零件”包含JVM以及運(yùn)行Java程序所需的類庫。只運(yùn)行Java程序裝JRE就夠了。JDK開發(fā)工具包是“整條生產(chǎn)線”不僅包含JRE還包含javac編譯器、調(diào)試工具、打包工具等。你要寫代碼、編譯代碼必須裝JDK。所以開發(fā)階段你只裝JDK就行因?yàn)镴DK里已經(jīng)包含了JRE和JVM。裝了JDK你既能編譯又能運(yùn)行。1.2 JVM跨平臺(tái)到底怎么實(shí)現(xiàn)的還是那個(gè)類比Java宣傳語叫“一次編譯到處運(yùn)行”。你以為它是像C語言那樣針對(duì)不同系統(tǒng)分別編譯出不同的可執(zhí)行文件不是。Java的做法是你的源代碼先編譯成字節(jié)碼——一種JVM認(rèn)識(shí)的中間語言后綴是.class。然后不同操作系統(tǒng)上有各自的JVM版本W(wǎng)indows版JVM負(fù)責(zé)把字節(jié)碼翻譯成Windows能執(zhí)行的內(nèi)容Linux版JVM翻譯成Linux能執(zhí)行的內(nèi)容。翻譯工作由JVM承擔(dān)你的字節(jié)碼文件不需要改一個(gè)字。所以你寫Java程序時(shí)不需要關(guān)心目標(biāo)機(jī)器是什么系統(tǒng)只需要保證那臺(tái)機(jī)器裝了對(duì)應(yīng)版本的JRE或JDK。這個(gè)設(shè)計(jì)在多年前解決了軟件分發(fā)的大難題也是Java后面在企業(yè)級(jí)開發(fā)里站穩(wěn)腳跟的原因之一。1.3 為什么入門就要學(xué)這一層因?yàn)閳?bào)錯(cuò)會(huì)告訴你答案環(huán)境問題在初學(xué)者遇到的Bug中占比極高。頭一個(gè)星期你遇到的報(bào)錯(cuò)十有八九跟代碼邏輯沒關(guān)系而是“找不到或無法加載主類”“找不到符號(hào)”“類路徑不匹配”。如果你搞不清JVM、編譯器、類路徑這些概念對(duì)著報(bào)錯(cuò)信息你完全無從下手。反過來只要你弄懂了編譯和運(yùn)行是兩步很多報(bào)錯(cuò)看一眼就知道問題出在哪。編譯失敗先找語法問題運(yùn)行失敗先找類路徑和依賴問題這個(gè)思路從第一天就要建立。2. 搭環(huán)境這一步藏著大量勸退細(xì)節(jié)2.1 版本怎么選別追求最新追求LTSJava現(xiàn)在每半年出一個(gè)新版本3月一個(gè)、9月一個(gè)。初學(xué)者最容易犯的錯(cuò)誤是看見官網(wǎng)有最新版就裝結(jié)果裝了個(gè)非長(zhǎng)期支持版用幾個(gè)月就到期還得折騰升級(jí)。老老實(shí)實(shí)選擇LTS長(zhǎng)期支持版本。所謂LTS就是官方承諾長(zhǎng)時(shí)間維護(hù)的版本適合生產(chǎn)環(huán)境。目前市面上主力項(xiàng)目用的最廣的幾個(gè)LTS版本是Java 8、Java 11、Java 17。新項(xiàng)目推薦Java 17或更高版本老項(xiàng)目維護(hù)場(chǎng)景Java 8仍然大量存在。入門階段選17就行了太老的版本缺不少新特性太新的版本網(wǎng)上資料少。另外一個(gè)坑是發(fā)行版選擇。Oracle JDK是官方原版但商業(yè)用途有限制。大多數(shù)公司和個(gè)人開發(fā)者用的是OpenJDK或它的發(fā)行版比如某企業(yè)版OpenJDK。這些在使用上幾乎沒有差別入門階段裝哪個(gè)都行不用糾結(jié)。我個(gè)人的建議是別花太多時(shí)間在這個(gè)選擇上先讓它跑起來以后換發(fā)行版幾分鐘的事。2.2 安裝JDK后為什么要配置JAVA_HOME和PATHWindows用戶安裝完JDK后會(huì)看到一個(gè)安裝目錄形如C:\Program Files\Java\jdk-17。如果你不配置環(huán)境變量直接敲java -version系統(tǒng)會(huì)提示“不是內(nèi)部或外部命令”。原理是這樣的你在命令行輸入命令時(shí)系統(tǒng)會(huì)按照PATH環(huán)境變量里列出的目錄逐個(gè)去找那個(gè)命令對(duì)應(yīng)的可執(zhí)行文件。JDK的bin目錄下放著java.exe和javac.exe但你沒說它在哪系統(tǒng)自然找不到。所以你要把bin目錄的路徑追加到PATH里。而JAVA_HOME又是什么很多編譯工具、服務(wù)器腳本會(huì)主動(dòng)讀取這個(gè)環(huán)境變量來找JDK的位置。所以規(guī)范做法是新建一個(gè)變量叫JAVA_HOME指向JDK安裝目錄然后在PATH里加%JAVA_HOME%\bin。這樣以后你升級(jí)JDK版本只需要改JAVA_HOME一個(gè)地方PATH里的引用自動(dòng)跟著變。實(shí)操檢查方法打開命令行輸入java -version確認(rèn)能輸出版本號(hào)。輸入javac -version確認(rèn)編譯器可用。java和javac輸出不同版本號(hào)的話十有八九是PATH里有多個(gè)Java路徑舊的排在前面了需要把沖突的路徑清掉。2.3 IDE選擇別再用記事本折磨自己第一周可以選擇一款順手的IDE集成開發(fā)環(huán)境。雖然理論上記事本加命令行也能編譯運(yùn)行Java但實(shí)際開發(fā)中沒人這么干。主流選擇就兩個(gè)IntelliJ IDEA目前最主流的Java開發(fā)工具社區(qū)版免費(fèi)功能足夠入門使用。Eclipse老牌工具早年流行現(xiàn)在用的人相對(duì)少了不過仍然是穩(wěn)定選擇。我個(gè)人帶新人時(shí)統(tǒng)一推薦IDEA社區(qū)版。理由很簡(jiǎn)單IDEA的智能提示對(duì)新手更友好寫錯(cuò)的地方當(dāng)場(chǎng)就給你標(biāo)紅省去很多低級(jí)錯(cuò)誤來回編譯的煩惱。而Eclipse的界面相對(duì)老派快捷鍵和IDEA差異較大轉(zhuǎn)IDEA還要重新適應(yīng)。2.4 第一個(gè)程序不要用IDE的模板手動(dòng)敲一遍新建項(xiàng)目、新建包、新建類這個(gè)流程IDEA里點(diǎn)幾步就完成了。但我強(qiáng)烈建議第一遍你打開編輯器手動(dòng)輸入這段代碼public class HelloWorld { public static void main(String[] args) { System.out.println(Hello, World!); } }注意文件必須保存為HelloWorld.java類名必須和文件名完全一致包括大小寫。第一步就吃透這個(gè)規(guī)則后面少掉很多編譯錯(cuò)誤。然后在這個(gè)文件所在的目錄打開命令行執(zhí)行javac HelloWorld.java java HelloWorldjavac把.java源文件編譯成.class字節(jié)碼文件java把字節(jié)碼放到JVM里運(yùn)行。你親眼看到這兩個(gè)步驟分開發(fā)生才算真正理解Java程序的運(yùn)行機(jī)制。IDE幫你把這兩步合并成一個(gè)按鈕方便是方便但代價(jià)是你對(duì)過程的感知變?nèi)趿恕?. 語法地基入門階段必須啃下的五塊內(nèi)容3.1 變量與數(shù)據(jù)類型內(nèi)存里發(fā)生的事Java是強(qiáng)類型語言意思是你聲明一個(gè)變量時(shí)必須明確告訴編譯器“它是什么類型”。這一點(diǎn)和JavaScript、Python這類動(dòng)態(tài)語言有本質(zhì)區(qū)別?;绢愋陀邪朔Nbyte、short、int、long、float、double、boolean、char。入門階段你必須記住int和double就夠其他幾個(gè)用到再說。為什么要強(qiáng)調(diào)類型因?yàn)槊糠N類型在內(nèi)存中占的空間不一樣。int占4字節(jié)long占8字節(jié)類型決定了你聲明一個(gè)變量時(shí)JVM要分配多少內(nèi)存空間。類比來說就像你租儲(chǔ)物柜不同尺寸價(jià)格不同你得先告訴管理員你需要多大的柜子他才能給你分配對(duì)應(yīng)的格子。實(shí)際寫代碼時(shí)最常見的兩個(gè)坑整數(shù)除法丟失小數(shù)。5 / 2的結(jié)果是2不是2.5。因?yàn)閮蓚€(gè)int相除結(jié)果仍然是int小數(shù)部分直接被丟棄。想要精確結(jié)果至少其中一個(gè)要寫成浮點(diǎn)數(shù)。超過范圍的溢出。int的最大值是2147483647你給int變量賦一個(gè)更大的值編譯或運(yùn)行時(shí)會(huì)出現(xiàn)意外結(jié)果。寧可多用long也別在數(shù)字大時(shí)省這點(diǎn)內(nèi)存。3.2 控制流if、for、while怎么選控制流的語法很機(jī)械背一遍就記住了。難點(diǎn)在于“什么時(shí)候用哪種”。我總結(jié)一個(gè)簡(jiǎn)單的判斷邏輯知道具體循環(huán)次數(shù)用for。不知道次數(shù)只知道“滿足某個(gè)條件就繼續(xù)”用while。無論如何先執(zhí)行一次再看條件用do-while這個(gè)場(chǎng)景實(shí)際生產(chǎn)中比較少。條件是離散值且判斷分支多用switch。循環(huán)里有兩個(gè)關(guān)鍵字初學(xué)者容易弄混break是跳出整個(gè)循環(huán)continue是跳過本次循環(huán)、進(jìn)入下一次迭代。寫嵌套循環(huán)時(shí)break只跳出最內(nèi)層循環(huán)。面試?yán)锝?jīng)常問的“如何跳出多層循環(huán)”常規(guī)解法是給外層循環(huán)加標(biāo)簽然后break 標(biāo)簽名知道有這個(gè)玩法即可。3.3 面向?qū)ο蟛皇钦Z法是組織代碼的思路面向?qū)ο蟀?、?duì)象、封裝、繼承、多態(tài)。初學(xué)者最常見的困惑是我知道class怎么寫但不知道為什么要這么寫。我建議你不要把面向?qū)ο螽?dāng)成Java獨(dú)有的規(guī)則而是當(dāng)成“程序員組織代碼時(shí)的一種傾向”。它想解決的核心問題是代碼多了之后怎么維護(hù)起來不累。類的本質(zhì)是“數(shù)據(jù)和操作數(shù)據(jù)的方法打包在一起”。封裝的意思是類內(nèi)部的數(shù)據(jù)不允許外部直接亂改只能通過類提供的方法來操作。就像銀行不會(huì)讓你把手伸進(jìn)金庫但你可以通過柜員窗口存錢取錢。繼承表達(dá)的是“is-a”關(guān)系學(xué)生是人Student類可以繼承Person類的屬性和方法。多態(tài)表達(dá)的是“同一種調(diào)用不同對(duì)象有不同表現(xiàn)”讓Dog和Cat都實(shí)現(xiàn)同一個(gè)叫聲方法你調(diào)用時(shí)不用關(guān)心具體是哪個(gè)動(dòng)物它會(huì)自己叫自己的。入門階段不要陷入設(shè)計(jì)模式的深淵。你只需要做到能寫出一個(gè)類有私有字段、有構(gòu)造方法、有g(shù)etter和setter能用繼承減少重復(fù)代碼能在父子類之間通過方法重寫實(shí)現(xiàn)不同行為這就夠了。設(shè)計(jì)模式是工作一兩年之后再系統(tǒng)學(xué)的東西。3.4 集合框架把數(shù)據(jù)組織方式搞清楚Java的集合框架是后面寫業(yè)務(wù)代碼最常用的工具類庫相當(dāng)于工具箱。你只需要先記四個(gè)核心類型ArrayList有序、可重復(fù)、按下標(biāo)訪問底層是動(dòng)態(tài)數(shù)組。查快、增刪慢。LinkedList有序、可重復(fù)底層是雙向鏈表。增刪快、按下標(biāo)查慢。HashSet無序、不可重復(fù)。用于去重場(chǎng)景。HashMap鍵值對(duì)存儲(chǔ)按鍵取值鍵不可重復(fù)。這是日常使用頻率最高的集合。初學(xué)最容易犯的錯(cuò)誤是遍歷集合時(shí)修改集合。比如在for-each循環(huán)里調(diào)用remove方法會(huì)拋ConcurrentModificationException。正確做法是用迭代器的remove方法或者在遍歷結(jié)束后再統(tǒng)一刪除。這個(gè)坑我見新人踩過太多次了。還有一點(diǎn)泛型從第一天就要養(yǎng)成習(xí)慣。ListString而不是光禿禿的List。泛型能幫你把類型錯(cuò)誤從運(yùn)行時(shí)提前暴露到編譯期還能讓你從“取出后強(qiáng)制轉(zhuǎn)型”的麻煩里解脫出來。3.5 異常處理不是應(yīng)付考試用的異常處理的語法簡(jiǎn)單try塊里放可能出錯(cuò)的代碼catch塊里寫怎么處理finally塊里放不管有沒有異常都要執(zhí)行的代碼。但很多初學(xué)者會(huì)把異常處理當(dāng)成“給編譯通過而寫的補(bǔ)丁”編譯器說這個(gè)方法必須拋異常那就throws一下這段代碼可能出錯(cuò)那就try-catch包起來。這種態(tài)度會(huì)留下隱患因?yàn)檎嬲膯栴}被吞掉了。應(yīng)該這樣理解異常是程序運(yùn)行時(shí)出現(xiàn)的“意外情況”你要考慮的不是“怎么讓它不出現(xiàn)”而是“它出現(xiàn)了之后程序怎么優(yōu)雅地處理”??罩羔槷惓?duì)象是null卻調(diào)用了它的方法。從第一天起掉方法之前多問一句這個(gè)對(duì)象有沒有可能是null數(shù)組越界異常訪問下標(biāo)超過了數(shù)組長(zhǎng)度減一。排查異常的思路是先看異常類型叫什么再看異常消息說了什么最后看棧信息里指向哪個(gè)文件的哪一行。這個(gè)順序可以節(jié)約你大量排查時(shí)間。public static int divide(int a, int b) { if (b 0) { throw new IllegalArgumentException(除數(shù)不能為0); } return a / b; }這個(gè)例子想說明校驗(yàn)參數(shù)是異常處理的重要場(chǎng)景而不是等系統(tǒng)拋個(gè)麻煩的ArithmeticException出來再處理。4. 每個(gè)初學(xué)者都躲不開的報(bào)錯(cuò)場(chǎng)景4.1 環(huán)境與編譯類報(bào)錯(cuò)報(bào)錯(cuò)信息原因解決方法javac 不是內(nèi)部或外部命令PATH沒有配置好檢查JAVA_HOME和PATH配置重開命令行窗口錯(cuò)誤找不到或無法加載主類 HelloWorld類路徑不對(duì)或類名與文件名不一致確認(rèn)當(dāng)前路徑、類名和文件名java命令不要加.class后綴錯(cuò)誤找不到符號(hào)變量或方法名寫錯(cuò)了或未導(dǎo)入需要的類看錯(cuò)誤旁邊的“符號(hào)”提示核對(duì)拼寫、導(dǎo)入語句不兼容的類型類型不匹配比如把double賦給int檢查賦值兩端的類型做強(qiáng)制類型轉(zhuǎn)換或改類型聲明編譯報(bào)錯(cuò)的一大特點(diǎn)你得看第一條錯(cuò)誤而不是被一堆報(bào)錯(cuò)嚇住。有時(shí)候一個(gè)錯(cuò)誤的括號(hào)沒配對(duì)編譯器會(huì)連報(bào)出一大串錯(cuò)誤但真正的病根只有一個(gè)。從第一條開始修往往后面的全都消失了。4.2 運(yùn)行時(shí)異常才是真正要?jiǎng)幽X的編譯通過不代表程序正確這是很多新手第一次寫代碼時(shí)最意外的事??罩羔槷惓W畛R姟W址容^時(shí)常量放前面能避免abc.equals(str)而不是str.equals(abc)前者在str為null時(shí)不會(huì)報(bào)錯(cuò)。數(shù)組越界循環(huán)時(shí)用i arr.length而不是i arr.length這是新手最容易漏掉的1/-1錯(cuò)誤。類型轉(zhuǎn)換異常父類引用轉(zhuǎn)成錯(cuò)誤子類時(shí)拋出。強(qiáng)制轉(zhuǎn)型前用instanceof判斷一下。4.3 邏輯錯(cuò)誤排查打日志不如讀代碼程序沒報(bào)錯(cuò)但結(jié)果不對(duì)這種問題最難搞。我的排查習(xí)慣是輸出關(guān)鍵變量。在幾個(gè)關(guān)鍵節(jié)點(diǎn)打印變量的值看是哪一步和預(yù)期不符。這個(gè)過程叫“插樁”。等后面學(xué)了IDE的調(diào)試功能用斷點(diǎn)逐行跟蹤會(huì)更高效但入門階段先養(yǎng)成“程序是按順序執(zhí)行的”這個(gè)意識(shí)排查邏輯錯(cuò)誤時(shí)一條條走下來問題定位其實(shí)很快。5. 學(xué)習(xí)路徑與常見誤區(qū)5.1 推薦的學(xué)習(xí)順序第一階段語法基礎(chǔ)。變量、類型、運(yùn)算符、if/for/while、數(shù)組、方法預(yù)計(jì)花2到4周。第二階段面向?qū)ο?。類、?duì)象、構(gòu)造方法、封裝、繼承、多態(tài)、接口預(yù)計(jì)花2到3周。第三階段常用API。字符串處理、集合框架、異常、日期時(shí)間、IO流1到2個(gè)月邊用邊學(xué)。第四階段數(shù)據(jù)庫與項(xiàng)目。學(xué)關(guān)系型數(shù)據(jù)庫基礎(chǔ)、SQL、JDBC或MyBatis做一個(gè)簡(jiǎn)單的管理系統(tǒng)類項(xiàng)目這是很多入門書籍的終點(diǎn)也是你從“會(huì)語法”到“能做東西”的轉(zhuǎn)折點(diǎn)。前面怎么看都是概念的真正把知識(shí)串起來的是“做項(xiàng)目”這一步。只學(xué)習(xí)語法不寫完整項(xiàng)目你會(huì)一直有“我好像會(huì)了但什么都不會(huì)”的虛無感。做一個(gè)能跑起來的項(xiàng)目哪怕只是一個(gè)圖書管理系統(tǒng)、一個(gè)待辦事項(xiàng)工具都能把類、對(duì)象、集合、數(shù)據(jù)庫這些知識(shí)點(diǎn)串成一條線你的理解會(huì)立刻不一樣。5.2 入門階段最常見的三個(gè)誤區(qū)第一個(gè)誤區(qū)死磕語法書從第一章刷到最后一章。語法書是字典不是小說不需要從頭到尾讀完。比較高效的做法是先讀完前幾章掌握基本語法然后直接帶著項(xiàng)目去查文檔用到什么學(xué)什么。第二個(gè)誤區(qū)筆記抄得飛起動(dòng)手敲得極少。編程是技能不是知識(shí)。知道和會(huì)做之間隔著大量的練習(xí)。每天至少敲半小時(shí)代碼比一天看五小時(shí)視頻但不動(dòng)手有效得多。第三個(gè)誤區(qū)遇到報(bào)錯(cuò)第一反應(yīng)是“百度一下復(fù)制解決方案”。新手可以這么干但你不能永遠(yuǎn)停留在這個(gè)階段。往上走的關(guān)鍵一步是先自己讀一遍報(bào)錯(cuò)信息猜測(cè)問題出在哪然后再帶著你的猜測(cè)去檢索。這樣你找到的不僅是解決方案還學(xué)到了排查方法。5.3 一些實(shí)在的建議環(huán)境出問題先重啟命令行窗口再檢查配置如果以前能跑現(xiàn)在不能跑想想自己最近改了什么回退一下試試。養(yǎng)成這個(gè)習(xí)慣能解決很多疑難雜癥。代碼規(guī)范要早點(diǎn)養(yǎng)成變量名用有意義的英文單詞方法名用動(dòng)詞開頭邏輯塊之間保持縮進(jìn)。等到寫復(fù)雜項(xiàng)目時(shí)這種方法帶來的好處會(huì)非常明顯。還有常在IDE里看看你寫的代碼是不是有明顯的“黃色警告線”比如聲明了沒用的變量、沒用的import語句。順手清掉你不會(huì)失去什么但你代碼的整潔度會(huì)高出不少。最后說一句我每次帶新人都要囑咐的話入門階段的進(jìn)度慢是正常的。你敲一行代碼就想看結(jié)果報(bào)了錯(cuò)就煩躁這些情緒所有人都經(jīng)歷過。Java內(nèi)容確實(shí)不少環(huán)境門檻也卡人但只要你保持每天寫一點(diǎn)最多兩三個(gè)月就會(huì)度過最痛苦的時(shí)期那時(shí)候你再回頭看第一天對(duì)著JDK配置發(fā)愁的自己會(huì)發(fā)現(xiàn)自己已經(jīng)走了很遠(yuǎn)。