
簡介JDK 1.8.0_231 的 64 位免安裝壓縮包面向需要在本地或服務器上快速搭建 Java 8 開發(fā)環(huán)境的開發(fā)者與教學場景。它省去安裝向?qū)Ы鈮汉笈渲?JAVA_HOME、PATH 等變量即可編譯并運行程序適合多環(huán)境切換、離線部署與舊項目維護。壓縮包共 1473 個文件約 172.9MB以 705 個 jar核心類庫與工具組件、118 個 dll 與 74 個 exe本地庫與可執(zhí)行程序為主另含 228 個 xml、50 個 properties 配置文件和若干 HTML、圖片等文檔資源目錄結(jié)構(gòu)基本保留了官方發(fā)行版的完整形態(tài)。該版本包含 Java 8 的 Lambda 表達式、Stream API 與 java.time 日期時間 API 等特性并附帶 Java Mission Control 等診斷工具可用于排查內(nèi)存與性能問題。目前已有 2073 人學習下載適合需要穩(wěn)定 64 位 JDK 環(huán)境、希望獲得完整工具鏈的初、中級 Java 開發(fā)者。1. 裝 JDK 1.8 前先把 8u231 這個版本號認清楚構(gòu)建機上前腳剛把 JDK 升到 11后腳項目一編譯就拋出Unsupported class file major version 55想退回去裝 jdk1.8 安裝包又卡在 JAVA_HOME 上反復折騰。這種來回折騰在還跑著老項目的團隊里幾乎每周都會發(fā)生。大量老中間件、老構(gòu)建腳本、老 IDE 工程仍然鎖定在 JDK 1.8 上而 2019 年那批更新里的 jdk1.8.0_231 64 位版本是這幾年被復用最多的一份——zip 是免安裝的解壓后配好環(huán)境變量就能用。也正因為它不寫注冊表、不自動配環(huán)境jdk1.8 64位下載之后最常出現(xiàn)的狀況是java 能跑、javac 找不到或者兩個命令的版本對不上。這篇記錄的是我完整走一遍的過程從校驗壓縮包到跑通冒煙測試新手照著做能走通熟手重點看版本取舍和后面那五條排查。2. 解壓 jdk1.8.0_231.zip包結(jié)構(gòu)、校驗和目錄落位拿到壓縮包之后先不要急著雙擊。綠色版 JDK 的好處是干凈壞處是它把“配置”這部分責任全部交回給你所以第一步必須弄清楚你手里這個包到底是什么結(jié)構(gòu)、該放到哪里。2.1 zip 里到底有什么跟 exe 安裝包差在哪把jdk1.8.0_231.zip解開你會看到一個頂層目錄jdk1.8.0_231/里面是bin、jre、lib、include、db、man這幾樣。bin下面同時有java.exe和javac.exe這一點很關(guān)鍵——后面排查“java 有 javac 沒有”的時候靠的就是確認 JAVA_HOME 有沒有指到這一層。jre目錄在 JDK 8 里是包內(nèi)自帶的一份和 JDK 本體放在一起。這是 1.8 的結(jié)構(gòu)特點從 9 之后模塊化改造這種結(jié)構(gòu)就不再有。所以老教程寫%JAVA_HOME%\jre\bin是合理的但在 1.8 里其實沒有必要往 Path 里加bin目錄已經(jīng)覆蓋了運行和編譯所需的可執(zhí)行文件。和 exe 安裝包最大的差別在注冊表和轉(zhuǎn)發(fā)器。exe 裝完之后會在C:\Program Files\Common Files\Oracle\Java\javapath放一組java.exe、javaw.exe、javaws.exe并把該目錄寫進系統(tǒng) Path 最前面同時也會往C:\Windows\System32塞幾個。它做這些是為了讓用戶在任意目錄都能直接用但副作用是你后來把 JAVA_HOME 指向 APath 里排在前面的 javapath 還指著 B于是java -version正常、javac -version報錯或者反過來。zip 綠色版沒有這個問題代價就是環(huán)境變量得自己一項一項配。我一般會先確認壓縮包有沒有被截斷。正常 Windows 64 位版的 jdk1.8.0_231 壓縮包在一百多兆到兩百兆之間如果看到只有幾十 KB 或者幾 MB基本可以判斷是下載中斷、下成了 HTML 錯誤頁或者被某些下載工具“優(yōu)化”過。這類包解壓時可能不報錯但bin里會缺文件后面編譯時才會出現(xiàn)各種莫名奇異的錯誤最難排查。2.2 校驗與解壓路徑里別留空格和中文下載完成后先做一次哈希校驗。Windows 上自帶 certutil:: Windows 下校驗 zip 完整性把輸出和你手上的 sha256 對一下 certutil -hashfile D:\download\jdk1.8.0_231.zip SHA256Linux 下我更習慣用 sha256sum# 校驗通過再進行解壓避免解到一半才發(fā)現(xiàn)包壞了 sha256sum jdk1.8.0_231.zip校驗這一步很多人會跳過但它是唯一能在解壓之前確認“這個包是完整的”的手段。遇到過一次壓縮包只下到 90%解壓工具不報錯lib\tools.jar是 0 字節(jié)結(jié)果所有用到 tools.jar 的老腳本全掛排查了大半天才回到源頭上。解壓工具的選擇也有講究。Windows 資源管理器對 zip 支持沒問題但它不處理超長路徑如果解壓目標路徑本身就長會出現(xiàn)“解壓成功但文件少了”。我一般用 7-Zip 右鍵“解壓到 jdk1.8.0_231\”解壓后統(tǒng)計一下文件數(shù)再和壓縮包內(nèi)置的目錄結(jié)構(gòu)對一眼。Linux 上# 沒裝 unzip 先裝apt install unzip / yum install unzip # -d 指定目標目錄-q 靜默-o 覆蓋已存在文件 unzip -q -o jdk1.8.0_231.zip -d /usr/local/路徑規(guī)則只有一條不要有空格、中文、、(、)。D:\Program Files\jdk1.8.0_231這種是典型反例很多老構(gòu)建腳本、老批處理在拼接路徑時不做引號轉(zhuǎn)義空格會直接把參數(shù)截斷報出來的錯誤跟你實際做的事毫無關(guān)系。同理D:\工具\jdk也不行某些控制臺程序讀中文路徑會亂碼。我統(tǒng)一用D:\env和/usr/local路徑短、純 ASCII、權(quán)限模型清晰。2.3 目錄落位我一般用 /usr/local 和 D:\envWindows 上我把它放在D:\env\jdk1.8.0_231和 Maven、Node、Python 這些放一起方便統(tǒng)一備份和遷移。放 C 盤的問題不在權(quán)限而在于重裝系統(tǒng)或者做鏡像時容易丟而且 C 盤空間經(jīng)常告急JDK 幾萬個文件堆在那里不合適。Linux 上用的是/usr/local/jdk1.8.0_231然后建一個軟鏈# 建軟鏈好處是升級時只改指向不用動所有引用它的腳本 ln -sfn /usr/local/jdk1.8.0_231 /usr/local/java ls -l /usr/local/java-s建軟鏈-f強制覆蓋已有文件-n是關(guān)鍵——如果/usr/local/java已經(jīng)是個指向目錄的軟鏈不加-n會在那個目錄里面再建一個java軟鏈變成/usr/local/java/jdk1.8.0_231/...這種嵌套非常隱蔽。我第一次踩到的時候看了半天ls -l才反應過來。權(quán)限方面JDK 目錄應該屬于 rootbin下的可執(zhí)行文件保留執(zhí)行位chown -R root:root /usr/local/jdk1.8.0_231 chmod -R 755 /usr/local/jdk1.8.0_231/bin不要把 JDK 目錄設成 777。有人圖省事這么干后來發(fā)現(xiàn)bin下的文件權(quán)限被改過之后java啟動直接報 permission denied還查不到原因。3. Windows 配環(huán)境變量JAVA_HOME、Path 和那條 where 命令綠色版在 Windows 上的全部工作量就是三處環(huán)境變量但順序和寫法錯一個字符就會出現(xiàn)“明明配了卻用不上”的現(xiàn)象。3.1 JAVA_HOME 放在系統(tǒng)變量里值不要帶 \bin右鍵“此電腦” → 屬性 → 高級系統(tǒng)設置 → 環(huán)境變量。新建變量建在系統(tǒng)變量里不是用戶變量。原因是很多場景——Windows 服務、計劃任務、以 SYSTEM 賬號運行的 CI agent——讀的是系統(tǒng)變量用戶變量在它們眼里根本不存在。用戶變量里配一份、系統(tǒng)變量里配另一份并且不一致是最容易讓人崩潰的組合。變量名JAVA_HOME值D:\env\jdk1.8.0_231。三個細節(jié)不要帶\bin。帶了的后果是 Path 里%JAVA_HOME%\bin展開成D:\env\jdk1.8.0_231\bin\bin然后java找不到報“不是內(nèi)部或外部命令”。不要帶末尾分號。分號在 Windows 上是路徑分隔符寫在值末尾會讓展開后多出一個空路徑段某些程序解析時行為不確定。不要在值里加引號。環(huán)境變量的值原樣保存加了引號引號會變成路徑的一部分。然后編輯系統(tǒng)變量Path新增兩條并且用上移按鈕把它們挪到最前面%JAVA_HOME%\bin %JAVA_HOME%\jre\bin第二條在 1.8 里嚴格來說不是必需的但加了不會有副作用。真正決定成敗的是先后順序Windows 查找可執(zhí)行文件是按 Path 變量從前往后掃描取第一個命中的。如果你的機器上曾經(jīng)裝過 exe 版 JDK 或者 JREPath 里通常已經(jīng)有C:\Program Files\Common Files\Oracle\Java\javapath和C:\Windows\System32這兩個目錄里都有java.exe。不把它們挪后或者刪掉java -version顯示的永遠是它們你在 JAVA_HOME 里改什么都沒用。3.2 CLASSPATH 到底要不要配這是老教程遺留最多的一處。十年前的標準寫法是.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar在 JDK 8 下這個配置能工作但基本是冗余的。dt.jar是 IDE 用來做 BeanInfo 顯示的tools.jar主要供編譯器和工具使用應用運行時并不依賴它們。而最前面的那個點表示當前目錄在現(xiàn)代 JDK 上也已經(jīng)是-cp缺省時的默認行為。我的做法是如果項目或構(gòu)建腳本明確要求讀 CLASSPATH就配否則不配讓它空著??罩臅r候應用的類路徑完全由命令行-cp或者 manifest 決定出了問題看啟動腳本就行不用再考慮環(huán)境變量這一層干擾。一定要配的話有兩個坑。第一開頭那個點別丟丟了之后連當前目錄下的HelloWorld.class都找不到報NoClassDefFoundError第二一定用分號不能用逗號或者中文分號某些輸入法下很容易打錯控制臺不會提示只會在運行時告訴你找不到類。3.3 用 where 確認 java 和 javac 指向同一個目錄配完之后必須關(guān)掉所有已經(jīng)打開的 cmd 窗口再重新開一個。環(huán)境變量是在進程啟動時讀取的已經(jīng)運行的窗口不會熱更新。這一條是“我明明配了啊怎么沒用”的頭號原因我自己也在這上面浪費過時間。新窗口里跑:: 檢查三個最常見的落點輸出按 Path 順序排列第一條就是實際生效的 where java where javac :: java -version 輸出到 stderr重定向一下才能和后面的內(nèi)容一起看 java -version 21 javac -version :: 看 JAVA_HOME 是不是真的被改成了預期值 echo %JAVA_HOME%判斷依據(jù)很直接where java和where javac的第一行都應該落在D:\env\jdk1.8.0_231\bin下面而且必須是同一個目錄。如果where java第一條是 javapath 而where javac是 JDK 目錄那就說明 Path 順序沒調(diào)對回到上一步把%JAVA_HOME%\bin上移。java -version的輸出形如java version 1.8.0_231javac -version輸出javac 1.8.0_231。兩個版本號必須完全一致。不一致就說明有兩個 JDK 在同時生效通常是因為 JAVA_HOME 指向 A、Path 里排在最前面的卻是 B 的 bin 目錄。還有個更徹底的辦法直接從 JVM 自己嘴里問出它在哪:: 打印 JVM 視角下的所有關(guān)鍵屬性java.home 就是它自己認為的家目錄 java -XshowSettings:properties -version 21 | findstr /C:java.home /C:java.versionfindstr是 Windows 版的 grep/C:表示整串匹配。這條命令看到的是運行時真實值比看環(huán)境變量可靠——有些啟動器會自己改寫JAVA_HOME再拉起 JVM只有這一招能識破。在 Linux 上對應的是grep后面還會再用一次。4. Linux 裝 jdk1.8.0_231profile.d、軟鏈和 alternatives 的取舍Linux 這邊的復雜度不在于配什么而在于配在哪一層。同一臺機器上有登錄 shell、非登錄 shell、cron、systemd 四種運行環(huán)境它們讀的初始化文件不一樣配錯層就會出現(xiàn)“我登錄能跑服務跑不了”。4.1 解壓與軟鏈別把版本號寫死在業(yè)務腳本里解壓和軟鏈在第 2 章已經(jīng)寫過這里補一個權(quán)限細節(jié)如果 JDK 放在 NFS 或者某些做了 noexec 掛載的分區(qū)上java會報Permission denied文件權(quán)限看起來卻是對的。遇到這種先看掛載參數(shù)# 確認 /usr/local 所在分區(qū)沒有 noexec 之類影響執(zhí)行的掛載選項 mount | grep -E /usr| /usr/local軟鏈的意義在于解耦。所有腳本、服務的JAVA_HOME統(tǒng)一寫/usr/local/java將來從 8u231 換到別的 1.8 更新版本或者臨時切到 11 做兼容性驗證只需要重新指向軟鏈不用去翻十幾份啟動腳本改路徑。4.2 /etc/profile.d/jdk.sh 比 ~/.bashrc 靠譜在哪推薦寫法# /etc/profile.d/jdk.sh —— 全局生效登錄 shell 通過 /etc/profile 自動 source export JAVA_HOME/usr/local/java export JRE_HOME$JAVA_HOME/jre export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar export PATH$JAVA_HOME/bin:$PATH文件建好后chmod 644 /etc/profile.d/jdk.sh屬主 root。放到profile.d而不是直接改/etc/profile好處是升級或者卸載時刪一個文件就行不會污染主配置也更方便被自動化腳本管理。為什么不寫進~/.bashrc因為 bashrc 只對交互式 shell 生效。cron 任務、ssh host java -version這種單條命令、CI agent 拉起的構(gòu)建進程大多是非交互、非登錄的它們不讀 bashrcJAVA_HOME是空的。只寫 bashrc 的典型癥狀就是人登錄上去一切正常服務跑起來報JAVA_HOME not found或者直接用到了系統(tǒng)自帶的 OpenJDK。/etc/profile.d也有邊界——它由/etc/profile調(diào)用而/etc/profile只在登錄 shell 里執(zhí)行。所以 systemd 托管的服務仍然讀不到。這時候有兩個辦法# 方式一在 unit 文件里顯式聲明最可靠 [Service] EnvironmentJAVA_HOME/usr/local/java EnvironmentPATH/usr/local/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin# 方式二讓啟動腳本自己 source 一遍適合不能在 unit 里改的場景 #!/bin/bash source /etc/profile.d/jdk.sh exec java -jar /opt/app/app.jar我傾向方式一。環(huán)境應該跟著服務的定義走而不是依賴 shell 的隱式初始化這樣systemctl show service能直接看到它拿到了什么環(huán)境排查時不用猜。4.3 alternatives 和 PATH 只能有一個說了算機器上如果已經(jīng)存在系統(tǒng)自帶的 OpenJDK/usr/bin/java通常是個軟鏈或者 alternatives 管理的轉(zhuǎn)發(fā)器。想讓新裝的 JDK 接管這個入口# 注冊到 alternatives最后的數(shù)字是優(yōu)先級數(shù)字越大越優(yōu)先 alternatives --install /usr/bin/java java /usr/local/jdk1.8.0_231/bin/java 2000 \ --slave /usr/bin/javac javac /usr/local/jdk1.8.0_231/bin/javac \ --slave /usr/bin/jar jar /usr/local/jdk1.8.0_231/bin/jar # 交互式選擇當前生效的版本 alternatives --config javaDebian/Ubuntu 上命令是update-alternatives參數(shù)位置略有差異但概念一致。RHEL/CentOS 用alternatives。這里有一個必須理解的沖突alternatives 改的是/usr/bin/java這個入口而/etc/profile.d/jdk.sh改的是PATH讓/usr/local/java/bin排在/usr/bin前面。兩者同時用PATH 優(yōu)先級高的那條路贏alternatives 配了也白配。選一個當主機制我一般用 PATH 軟鏈管主要場景alternatives 只在需要給系統(tǒng)里其他不讀 profile 的程序提供統(tǒng)一入口時補一下并且確保改完的readlink -f $(which java)指向的是我預期的路徑。4.4 分別驗證登錄 shell 和非登錄 shell配完之后分兩次驗證這一步不能省# 登錄 shell會讀 /etc/profile進而 source profile.d bash -lc echo $JAVA_HOME; which java; java -version # 非登錄 shell不讀 /etc/profile用來模擬 cron 和 CI agent 的視角 bash -c echo $JAVA_HOME; which java; java -version 21如果第一條正確、第二條JAVA_HOME為空但java -version也對說明系統(tǒng)里本來就有 OpenJDK 在/usr/bin下兜底配置只是“看起來沒報錯”。這種情況在部署服務時最容易出問題——它用的其實是版本完全不同的另一個 JDK。要消除就得把 alternatives 也指到 1.8或者在業(yè)務腳本里顯式 source。5. 裝完跑不起來五條踩坑記錄按現(xiàn)象倒著查下面這五條是我在不同機器上真實遇到過多次的。排查順序建議按“先看它到底是哪個 JDK再看它缺什么”來走不要一上來就重裝。5.1 環(huán)境變量類故障現(xiàn)象一javac 不是內(nèi)部或外部命令但java -version正常。原因通常是JAVA_HOME指到了jre子目錄或者 Path 里只加了%JAVA_HOME%\jre\bin。JDK 8 包里jre\bin有java.exe沒有javac.exe所以 java 能跑、編譯不能。 解決echo %JAVA_HOME%確認指向的是D:\env\jdk1.8.0_231這一層不是帶\jre的那層然后用where javac確認 Path 里確實能找到。現(xiàn)象二java -version和javac -version的版本號對不上或者編譯報Unsupported class file major version 55。原因是機器上存在兩套以上 JDKPath 順序或者啟動腳本里寫死了另一個。IDE、Tomcat 的setenv.sh、Maven 的JAVA_HOME都是常見藏身點。 解決先用java -XshowSettings:properties -version 21 | findstr java.home拿到 JVM 自己認為的家目錄再回頭找是誰在把它拉起來。IDE 里要看項目 SDK 和運行配置兩處只改一處不起作用?,F(xiàn)象三Linux 上登錄能跑systemctl起的服務報JAVA_HOME not found。原因就是第 4 章說的systemd 不走 shell 初始化流程/etc/profile.d對它無效。 解決在 unit 文件里寫Environment或者讓啟動腳本顯式source。不要圖省事去改/etc/environment那個文件對 systemd 的傳遞也有限不是所有發(fā)行版行為都一致。5.2 運行類故障現(xiàn)象四啟動直接報Error occurred during initialization of VM或Could not reserve enough space for object heap。第一種可能是拿到了 32 位版本——java -version輸出里會寫Client VM或者不帶64-Bit正常 64 位會顯示Java HotSpot(TM) 64-Bit Server VM。第二種可能是-Xmx設得超過可用內(nèi)存或容器限額。 解決先確認是 64 位再把-Xmx降下來。容器里要額外注意JDK 8 在 8u191 之前不識別 cgroup 內(nèi)存限制會按宿主機物理內(nèi)存來算默認堆容器被限制在 2G 卻按 32G 機器去申請必然失敗。8u231 已經(jīng)支持UseContainerSupport但默認值仍然偏保守部署時顯式寫-Xmx更穩(wěn)妥?,F(xiàn)象五日志里全是中文亂碼或者讀文件報編碼異常。原因是 Java 8 的file.encoding跟隨操作系統(tǒng) locale。Windows 中文系統(tǒng)默認 GBKLinux 上多為 UTF-8同一份代碼在兩邊表現(xiàn)完全不同。 解決啟動參數(shù)里統(tǒng)一加-Dfile.encodingUTF-8。注意這個參數(shù)對已經(jīng)初始化的流不生效必須在 JVM 啟動時傳不能靠代碼里System.setProperty補救。另外javac編譯時也要加-encoding UTF-8否則源碼里的中文注釋可能導致編譯告警甚至報錯。還有一個容易被誤判的PKIX path building failed。它看起來像證書問題但很多情況下是系統(tǒng)時間不對——證書校驗依賴準確的時間窗口機器時間偏了幾小時或者幾年握手就會失敗。遇到先date看一眼再去查證書鏈。6. 裝完怎么驗證跑通三步冒煙每次部署前都過一遍版本號對上了不代表能用。我最后一道工序是跑一個最小冒煙覆蓋三件事版本自洽、運行時路徑正確、堆參數(shù)真的生效。第一步兩條命令的版本號必須一致java -version 21 | head -n 1 javac -version第二步編譯并運行一個檢查類讓 JVM 自己把關(guān)鍵屬性打印出來// EnvCheck.java —— 打印運行時真正生效的 JDK 路徑和關(guān)鍵屬性 public class EnvCheck { public static void main(String[] args) { System.out.println(java.home System.getProperty(java.home)); System.out.println(java.version System.getProperty(java.version)); System.out.println(file.encoding System.getProperty(file.encoding)); System.out.println(java.class.path System.getProperty(java.class.path)); // 換算成 MB看 -Xmx 有沒有被真正接受 System.out.println(maxMemory(MB) Runtime.getRuntime().maxMemory() / 1024 / 1024); } }# 編譯時指定編碼避免源碼里的中文影響結(jié)果 javac -encoding UTF-8 EnvCheck.java # 顯式給一個堆上限驗證參數(shù)確實被 JVM 接收 java -Xmx512m EnvCheckjava.home應該指向你解壓的那個目錄Linux 上經(jīng)過軟鏈后會顯示/usr/local/java/jre這是正常的JVM 把家目錄報在 jre 層。maxMemory應該接近但略小于 512如果它顯示的是幾個 G說明-Xmx沒生效——要么被啟動腳本里的后置參數(shù)覆蓋要么被_JAVA_OPTIONS這類環(huán)境變量改了那個變量會覆蓋命令行參數(shù)優(yōu)先級比你想的高。第三步打一個 jar 跑一下確認 manifest 和 classpath 都通# 打包時指定入口類模擬真實部署形態(tài) jar -cvfe EnvCheck.jar EnvCheck EnvCheck.class java -jar EnvCheck.jar三步都過基本可以判定這套 jdk1.8 安裝是通的。我整理了一張對照表出問題時按“該看到什么”反推檢查項該看到的結(jié)果不符合說明什么java -version含1.8.0_231且含64-Bit用錯版本或拿到 32 位包javac -version1.8.0_231Path 順序錯或 JAVA_HOME 指向 jrejava.home你解壓的那個目錄環(huán)境變量沒生效被其他 JDK 搶先file.encodingUTF-8沒傳-Dfile.encoding跨平臺會亂碼maxMemory接近你設的-Xmx參數(shù)被覆蓋或被容器限額截斷從那以后我每裝一次 JDK不管多急都強制把這三步走完再往機器上交尤其是java.home那一行——它比任何環(huán)境變量截圖都誠實。希望這套流程能幫到你少在版本號上繞兩圈。本文還有配套的精品資源點擊獲取