新机器到手第一件事往往不是写代码而是把 Linux 上的 JDK 和 Maven 装利索。这件事看起来简单——解压、配环境变量、验证三步走完似乎就结束了——但我见过太多人在这三步里栽跟头装完java -version能跑mvn -v却报 JAVA_HOME 不对本地跑得好好的一提交到构建机上就找不到命令想换个 JDK 版本结果把系统里其他依赖 Java 的服务全弄挂了。这篇就把我在 Linux 上安装 JDK 和 Maven 的完整流程摊开讲从版本选择、目录规划、环境变量写法到多版本共存、非交互式环境失效这些真正让人头疼的问题一条条过一遍。内容偏实操命令和配置都能直接抄适合刚接触 Linux 服务端环境的新手也适合已经装过几次但总在细节上翻车的朋友对照排查。1. 开工之前先搞清楚你要装哪个 JDK、装到哪1.1 系统源里的 JDK 为什么经常不能用很多人第一反应是打开包管理器一条安装命令搞定。这条路在桌面环境上问题不大放到服务端就未必合适。主流发行版的软件源为了兼容性收录的 JDK 版本往往偏保守尤其是那些生命周期长的发行版源里的默认版本可能落后好几个大版本。你按官方文档要求用 JDK 17 编译的项目在源里只能找到 JDK 11编译直接报不支持的类文件版本。更麻烦的是包管理器安装的 JDK 会往系统里塞一堆东西/usr/lib/jvm下多个目录、/etc/alternatives里一堆软链接、还有各种openjdk-xx-jre-headless之类的依赖包。这些在正常使用时不碍事一旦你要清理重装就得花时间搞清楚哪些能删、哪些删了会影响别的软件。我遇到过一台机器上同时存在三个不同来源的 JDKwhich java指向的路径和JAVA_HOME指向的路径根本不是同一个排查了半天才发现是之前装某个软件时被顺带装上的。所以我的习惯是服务端的 JDK 一律手动下载官方压缩包解压到独立目录用软链接管理版本。好处是来源清晰、版本可控、卸载就是删目录不会污染系统包管理器的状态。1.2 手动安装与包管理器安装的取舍对照不是说包管理器一无是处。小规模试用、临时跑个脚本、容器基础镜像里需要个 JRE用包管理器最省事。判断标准很简单这个东西要不要长期维护、要不要精确控制版本。要就手动装。对比维度包管理器安装官方压缩包手动安装版本选择受限于软件源收录范围任意版本官方发布即可用升级方式一条升级命令但可能连带升级依赖换软链接指向秒级切换、秒级回滚卸载干净度需处理依赖与残留配置删目录即可无残留多版本共存容易互相干扰天然支持各自独立目录系统污染会写入系统级配置与 alternatives只在你指定的目录里活动适合场景临时验证、容器内的最小运行时开发机、构建机、长期运行的服务表格里最后一行是我最看重的。构建机这种东西最怕的就是环境漂移——今天装的是这个版本明天运维重装系统变成了另一个版本同一个 commit 编译出来的东西都不一样了。手动装 固定目录 写进配置管理脚本才能保证环境可复现。1.3 安装目录规划别把东西丢在 /tmp选目录这件事新手最容易随手扔。下载到/tmp解压到/tmp配完环境变量跑通了重启机器一看全没了——因为很多发行版会把/tmp配置成重启清空甚至挂载时带noexec参数解压出来的二进制根本没法执行。我一般用这几个位置按优先级排/opt第三方独立软件的惯例位置JDK、Maven 这类自解压的绿色包放这里最合适目录名带版本号比如/opt/jdk-17.0.9、/opt/apache-maven-3.9.6。/usr/local如果你所在团队习惯把自编译、自安装的东西统一放这里那跟着团队走别自成一套。用户主目录下的~/tools或~/.local只有当前用户需要、没有 root 权限时的选择缺点是换用户就失效。我倾向于/opt配合一个不带版本号的软链接比如/opt/java指向/opt/jdk-17.0.9/opt/maven指向/opt/apache-maven-3.9.6。所有环境变量、脚本、服务配置一律引用软链接路径将来升级只需要改软链接指向其他配置文件一个字都不用动。这套做法我在几台机器上用了好几年升级 JDK 从 11 到 17 的时候改一条ln -sfn命令就完事业务配置零改动。注意解压前先看一眼磁盘余量。JDK 解压后通常三五百兆Maven 一百多兆加上后续依赖仓库的占用/opt所在分区至少留出几个 G。用df -h /opt确认一下别装到一半空间不足留下半拉子目录。2. JDK 落地实操从确认环境到验证通过2.1 第一步摸清系统底细与残留 Java动手之前先做体检这一步能省掉后面大量返工。确认架构决定下载哪个包uname -m输出x86_64就是常见的 64 位 Intel/AMD 架构输出aarch64就是 ARM 架构。这两种的 JDK 包完全不同下错了解压出来执行会报“无法执行二进制文件”而且这个报错信息很不直观新手容易误以为是权限问题。顺手看一眼系统版本cat /etc/os-release然后查一下机器上是不是已经有 Java 了which java java -version readlink -f $(which java)三条命令分别看命令在哪、版本是多少、真实文件在哪。重点看第三条因为系统里常见的做法是/usr/bin/java是个软链接链到/etc/alternatives/java再链到真正的 JDK 目录。三级跳下来才能看到真身。如果确认机器上装过 JDK 且你打算替换别急着删。先记录下来原来指向哪万一某个老服务依赖它你还得还原回去。我的做法是保留旧版本目录只改软链接和JAVA_HOME旧目录留着占几百兆空间但换来的是随时可以切回去的安全感。2.2 下载包与完整性校验从 JDK 发行方官网的下载页拿到 Linux 64 位压缩包文件名通常形如jdk-17.0.9_linux-x64_bin.tar.gz。这里有个容易忽略的点要下 JDK不是 JRE。JRE 只有运行时没有javac装完你会发现 Maven 编译直接失败。下载页上这两个包挨得很近下错的人不在少数。下完先校验别嫌麻烦sha256sum jdk-17.0.9_linux-x64_bin.tar.gz拿输出的哈希值和官网页面上公布的校验值比对一致再往下走。大文件下载中断、被中间设备改写、镜像同步延迟都会导致包损坏。不校验直接解压运气不好就是解压报错或者解压出来但跑不起来那时候再回头查原因成本高得多。顺手看一眼文件大小ls -lh如果只有几百 K那基本是下到了一个错误页面而不是安装包。2.3 解压、目录规范化与软链接把包放到/opt下解压sudo tar -xzf jdk-17.0.9_linux-x64_bin.tar.gz -C /opt解压出来的目录名一般就是jdk-17.0.9正好符合我们带版本号的命名习惯。如果名字里带了平台后缀改一下更清爽sudo mv /opt/jdk-17.0.9_linux-x64 /opt/jdk-17.0.9如果是用普通用户解压而不是 root可能会遇到属主警告。加--no-same-owner参数可以避免tar --no-same-owner -xzf jdk-17.0.9_linux-x64_bin.tar.gz -C /opt建立软链接sudo ln -sfn /opt/jdk-17.0.9 /opt/java-sfn三个参数的组合值得说一下s建软链接f强制覆盖已存在的同名链接n表示如果目标是软链接就当成普通文件处理而不是跟进去。少了n当/opt/java已经是个指向目录的软链接时命令会试图在目标目录里再建一个链接结果变成一个套娃非常隐蔽。权限也要顺手处理。如果 JDK 只给特定用户用sudo chown -R deploy:deploy /opt/jdk-17.0.9如果是给整机所有用户用保持 root 属主、目录权限 755 即可别人只能读和执行不能改。2.4 JAVA_HOME 与 PATH 的写法这一步是全场最容易出事的地方。我推荐写一个独立的环境脚本文件放在/etc/profile.d/下sudo tee /etc/profile.d/java.sh /dev/null EOF export JAVA_HOME/opt/java export PATH$JAVA_HOME/bin:$PATH EOF为什么不用直接往/etc/profile里追加因为/etc/profile是系统文件很多发行版的升级流程会检查它的改动你可能在升级时被提示合并冲突。而/etc/profile.d/下的独立脚本是官方预留的扩展点登录时会被自动 source互不干扰将来卸载直接删文件就行。写法的两个关键点第一PATH里$JAVA_HOME/bin必须放在前面。如果你写成export PATH$PATH:$JAVA_HOME/bin那么当系统里已经存在/usr/bin/java时shell 会优先命中系统的那个你配的 JDK 压根不生效。表现出来就是JAVA_HOME看着是对的java -version却是另一个版本。这个坑我被坑过不止一次。第二不要用JAVA_HOME/opt/java/带尾斜杠的写法。有些脚本会自己拼$JAVA_HOME/bin/java多一个斜杠变成双斜杠绝大多数情况下能用但少数工具的路径解析逻辑会出问题。统一不带尾斜杠省心。写完立刻生效source /etc/profile.d/java.sh2.5 验证清单与几个容易翻车的细节别只看java -version一行输出就收工按这份清单过一遍echo $JAVA_HOME which java java -version javac -version四项都对上才算真的配好了。javac那行尤其重要它能验证你装的是 JDK 而不是 JRE同时也能验证PATH里确实包含$JAVA_HOME/bin。再补一个更彻底的验证直接让 Java 自己报告它的家在哪java -XshowSettings:properties -version 21 | grep java.home输出的java.home应该等于你配置的JAVA_HOME加上运行时目录。如果两者对不上说明环境变量和实际执行的 Java 不是同一套赶紧回头查。这里说一个我踩过的坑某些发行版的登录脚本会在~/.bashrc里再设一次JAVA_HOME指向另一个版本。因为~/.bashrc在/etc/profile.d/之后加载会把你刚配的覆盖掉。表现就是 root 用户下一切正常切成部署用户就变了版本非常迷惑。排查方法是grep -rn JAVA_HOME ~/.bashrc ~/.bash_profile ~/.profile /etc/profile /etc/profile.d/ 2/dev/null把结果从头看一遍记住后加载的覆盖先加载的这个顺序问题就一目了然。3. Maven 安装不是解压完就完事3.1 Maven 与 JDK 的版本对应关系Maven 本身是 Java 写的它需要先有一个 JDK 才能跑起来。所以顺序不能颠倒先把 JDK 装好并验证通过再来装 Maven。版本匹配上近几年的 Maven 3.8、3.9 系列要求 JDK 8 及以上。但这只是“能跑起来”的底线实际项目里还得看编译插件的目标级别。比如你用 JDK 17 跑 Maven但项目的maven.compiler.target还写着 1.8那编译出来的是 Java 8 字节码能跑但用不上新特性反过来项目写的是 17 而你的 Maven 跑在 JDK 11 上就会看到那句经典的“不支持发行版本 17”。我一般建议让 Maven 运行所用的 JDK 和项目编译的目标 JDK 保持一致或者至少运行环境不低于目标版本。省掉很多莫名其妙的编译问题。3.2 安装流程与权限处理流程和 JDK 几乎一样sudo tar -xzf apache-maven-3.9.6-bin.tar.gz -C /opt sudo mv /opt/apache-maven-3.9.6 /opt/apache-maven-3.9.6 sudo ln -sfn /opt/apache-maven-3.9.6 /opt/maven注意下载的包名里要有-bin不要下成-src或者-bin.zip。-src是源码包需要自己构建纯属给自己找麻烦.zip在 Linux 上当然也能解但多一层东西没必要。环境变量同样丢到profile.dsudo tee /etc/profile.d/maven.sh /dev/null EOF export MAVEN_HOME/opt/maven export PATH$MAVEN_HOME/bin:$PATH EOF source /etc/profile.d/maven.shM2_HOME这个变量你可能在一些老文档里见过那是 Maven 2 时代的叫法现在用MAVEN_HOME就行。两个都写上也没坏处但我更推荐只写一个避免将来排查时多一层干扰。验证mvn -v输出里要能同时看到三行关键信息Apache Maven 的版本、Maven home 路径、Java version。如果 Java version 显示的是你没预期的那个版本说明JAVA_HOME有问题回到上一节重查。3.3 settings.xml 三段式配置本地仓库、镜像、编译级别到这一步 Maven 能跑了但直接拿来用会很痛苦。默认配置下所有依赖都会往中央仓库拉网络状况不好时一次构建能耗掉几十分钟还经常卡在某个依赖上超时。所以必须改配置。配置文件位置有两个优先级不同$MAVEN_HOME/conf/settings.xml全局配置对所有用户生效。~/.m2/settings.xml用户级配置优先级更高会覆盖全局配置中的同名项。我习惯在用户级配置里改因为构建机上常常有多个账号跑不同项目各自的需求不同。目录不存在的话手动建mkdir -p ~/.m2第一段本地仓库位置。默认在~/.m2/repository随用户主目录走。如果主目录所在分区很小或者多个用户想共享一份依赖缓存就指到别处localRepository/data/maven-repo/localRepository共享缓存这事要谨慎。多个进程同时往同一个目录写理论上 Maven 有并发控制但被强杀进程打断留下半截文件的情况我遇到过表现是某个依赖一直报校验失败只能手工删掉重下。真要多用户共享我会配只读的共享仓库 各自的私有仓库而不是简单粗暴共用一个目录。第二段仓库镜像。把中央仓库指向一个访问更快的镜像站点这是提速最明显的一招mirrors mirror idcentral-mirror/id nameinternal-central-mirror/name urlhttps://mirror.example.internal/repository/maven-public//url mirrorOfcentral/mirrorOf /mirror /mirrorsmirrorOf写central表示只镜像中央仓库写*表示镜像一切仓库包括项目自己声明的私有仓库这个要慎用——它会把你项目里特意配置的仓库地址也一并劫持过去导致某些内部依赖拉不到。写成external:*是另一个常见选择意思是镜像所有外部仓库但保留本地文件仓库。配完一定要测。删掉本地仓库里某个依赖的目录重新跑一次构建观察日志里下载地址是不是变成了你配的镜像域名。如果日志里还是原来的地址说明settings.xml没被读到——最常见的原因是文件放错了位置或者 XML 语法错误导致整段配置被忽略可以用mvn help:effective-settings查看实际生效的配置一目了然。第三段编译级别。让 Maven 明确知道用哪个 Java 版本编译避免不同机器上默认值不一致profiles profile idjdk-17/id activation activeByDefaulttrue/activeByDefault /activation properties maven.compiler.release17/maven.compiler.release project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties /profile /profiles这里用maven.compiler.release而不是老的source/target组合。release参数会让编译器同时检查 API 使用是否符合目标版本防止你不小心用了高版本才有的接口在低版本运行时抛NoSuchMethodError。这个问题在老项目里特别隐蔽——编译时看着好好的一上生产就炸。project.build.sourceEncoding也建议显式写上。Windows 上默认编码和 Linux 不一样同一个项目在两边编译中文资源文件的表现会不同加上这行就统一了。3.4 首次构建验证与依赖拉取的观察方法用一个最小项目验证整套环境mvn archetype:generate -DgroupIdcom.example.demo \ -DartifactIdhello-maven -Dversion1.0.0-SNAPSHOT \ -DarchetypeArtifactIdmaven-archetype-quickstart \ -DinteractiveModefalse cd hello-maven mvn package跑到BUILD SUCCESS就算通了。如果卡在依赖下载看日志里的Downloading from那一行地址对不对如果是编译报错看报的版本号和你预期是否一致。我习惯在第一次构建后执行mvn help:system这个命令会打印一堆环境信息其中java.home、java.version、user.home、maven.home四项是排查问题的关键。以后遇到“为什么构建机上的结果和本地不一样”第一反应就该是把这个输出和本地对比一下。再补一个观察依赖拉取速度的技巧构建时加-X会输出 debug 日志太吵加-Dorg.slf4j.simpleLogger.showDateTimetrue可以在下载日志里带上时间戳用来判断是哪个依赖拖慢的。这个参数不像-X那样淹没有用信息实际排查效率更高。4. 一台机器多个 JDK共存与切换4.1 update-alternatives 的工作机制真实场景里一台机器上跑三个不同 JDK 版本的项目是常事。老项目锁在 JDK 8新项目要 JDK 17中间还有个要 JDK 11 的。这时候怎么办update-alternatives是 Debian 系发行版提供的命令级多版本管理机制。它的原理不复杂维护一个记录表把每个“命令名”比如java对应的若干候选路径和优先级存起来然后在/etc/alternatives/下建一个指向当前选中项的软链接/usr/bin/java再指向那个链接。切换时只改一层链接全局生效。但要注意它的局限性update-alternatives只管命令不管JAVA_HOME。你把java切到了 17JAVA_HOME还停在 11那么java -version显示 17Maven 却用 11 去编译。这就是很多人切换后依然报版本错的根因。所以正确的做法是命令切换用update-alternativesJAVA_HOME单独管理两者保持同步。4.2 注册与切换实操把现有的 JDK 加进去sudo update-alternatives --install /usr/bin/java java /opt/jdk-17.0.9/bin/java 1700 sudo update-alternatives --install /usr/bin/java java /opt/jdk-11.0.20/bin/java 1100 sudo update-alternatives --install /usr/bin/javac javac /opt/jdk-17.0.9/bin/javac 1700 sudo update-alternatives --install /usr/bin/javac javac /opt/jdk-11.0.20/bin/javac 1100最后的数字是优先级数字大的在自动模式下被选中。我习惯用版本号本身当优先级1700 代表 171100 代表 11一眼能看出来谁高谁低。手动切换sudo update-alternatives --config java会列出所有候选让你输入编号选择。javac同样要切一次两个命令分开管理是这套机制的粗糙之处但也只能这样。切换后立刻同步JAVA_HOMEsudo ln -sfn /opt/jdk-17.0.9 /opt/java因为/etc/profile.d/java.sh里写的是/opt/java这个软链接路径改软链接就等于改了JAVA_HOME。新开的终端会自动生效当前终端需要重新source一次。一个细节要提醒图形界面或某些桌面环境下系统可能还会维护一个/etc/environment文件里面有静态的JAVA_HOME。这个文件在登录流程中的加载时机和 shell 脚本不一样有时候会出现改了脚本不生效的情况。检查一下有没有有就一并改掉。4.3 项目级锁定让构建不随全局变化如果只是某个项目要用特定版本根本没必要动全局配置。Maven 支持项目级的 JVM 参数和运行参数配置在项目根目录建一个.mvn目录mkdir -p .mvn echo -Dfile.encodingUTF-8 .mvn/jvm.configjvm.config里的内容是直接传给 JVM 的启动参数。maven.config则是传给 Maven 本身的默认命令行参数比如固定某个 profile、设置内存上限-T 1C -DskipTests这套机制的好处是配置跟着代码走谁拉下来都是同样的行为不依赖他本机怎么装的。团队协作里这比在 README 里写一段“请确保你装了 JDK 17”要可靠得多。要做到真正的版本锁定还有一招是在settings.xml里用toolchains配置多个 JDK 路径然后项目里声明用哪个。这个配置稍微复杂适合那种“构建机上有五个 JDK、每个项目指定用哪个”的大规模场景。小团队用目录软链接 环境变量已经足够。5. 问题速查我在装环境时踩过的那些坑5.1 故障速查表把常见问题按“现象 → 大概率原因 → 处理动作”整理成一张表出问题先扫一遍能跳过大部分无效排查现象大概率原因处理动作java -version报 command not foundPATH 未生效或路径写错echo $PATH看是否含$JAVA_HOME/bin确认脚本被 source版本显示和预期不符PATH 中系统路径优先或~/.bashrc覆盖查which java和readlink -f用 grep 排查重复设置javac: command not found装的是 JRE 不是 JDKls $JAVA_HOME/bin确认有没有 javac解压报“无法执行二进制文件”架构包下错x64 / ARM 混淆uname -m对比下载页的架构标识Maven 报 JAVA_HOME 未设置环境变量只在交互 shell 生效见第 6 节改 profile.d 或显式传参依赖下载极慢或超时未配镜像或镜像不可达mvn help:effective-settings确认配置生效编译报“不支持发行版本 X”运行 Maven 的 JDK 低于项目目标版本mvn -v看 Java version调整JAVA_HOME构建提示找不到某个内部依赖mirrorOf写成*劫持了私有仓库改为central或external:*首次构建极慢、之后正常依赖缓存未建立属正常现象确认本地仓库位置后再观察中文资源文件编译后乱码未指定源码编码配置project.build.sourceEncodingUTF-8表格里的“首次构建慢”这一条很多人第一次遇到会以为是配置错了反复折腾配置其实只是依赖在下载。判断方法很简单看日志有没有持续的Downloading from输出有就是在正常拉取。5.2 三个值得单独说的案例案例一解压到/tmp后重启消失。某次帮人看一台构建机的环境脚本里写死了/tmp/jdk/bin/java。机器不重启时一切正常一重启就全挂。原因是那个发行版把/tmp配置成 tmpfs 临时文件系统另外还带了noexec挂载参数。教训是任何要长期使用的软件永远不要放在/tmp。排查这类问题可以用mount | grep /tmp看一眼挂载参数一眼就能确认。案例二JAVA_HOME正确但 Maven 用的是别的 JDK。现象是echo $JAVA_HOME显示 17java -version也显示 17但mvn -v里 Java version 显示的是 11。查了半天发现mvn这个可执行文件本身是个 shell 脚本它启动时会优先读JAVA_HOME但如果JAVA_HOME没被正确导出比如只在当前 shell 的局部变量里设置就会回退到PATH里找java。解决办法是确保export确实生效或者干脆在mvn命令前临时指定JAVA_HOME/opt/jdk-17.0.9 mvn package。这个临时指定的写法特别适合验证阶段的对比测试。案例三软链接套娃导致升级失败。用ln -s而不是ln -sfn更新软链接当目标已经存在时命令会在已有链接指向的目录内部再创建一个同名链接。表面上看/opt/java还在实际结构已经乱掉了指向的可能是/opt/jdk-17.0.9/jdk-17.0.9这种不存在的路径。检查方法ls -l /opt/java看箭头ls -l $(readlink -f /opt/java)逐层确认。修复就是把整个链接删掉重建别试图在套娃结构上修补。心得这套环境安装完我会把整个流程写成一份带版本号的脚本放在仓库里包括下载地址、校验值、解压路径、软链接、环境变量、配置文件的完整内容。下次换机器直接跑省掉所有回忆成本。环境这东西第二次装比第一次装容易出错因为你不会像第一次那样小心翼翼。6. 非交互式环境为什么脚本里找不到 java6.1 交互 shell 与非交互 shell 的加载差异这一节的坑我踩过两次第二次才彻底搞明白。现象SSH 登录上去mvn -v正常。写个 shell 脚本用sh script.sh执行脚本里调mvn报 command not found。或者放到定时任务里跑同样报错。原因在 shell 的启动类型上。你登录时的 shell 是登录交互式 shell它会依次读取/etc/profile、/etc/profile.d/*.sh、~/.bash_profile等文件。而sh script.sh启动的是非交互式 shell它只读$BASH_ENV指向的文件一般不读/etc/profile.d/。定时任务的执行环境更是精简到了极致连 HOME 都可能不是你以为的那个。所以/etc/profile.d/java.sh里的配置对于非交互式进程来说根本不存在。验证方法很简单sh -c echo $JAVA_HOME输出空行就说明没继承。这个命令是排查此类问题最快的手段比直接去跑整个脚本快得多。6.2 让环境变量在 CI 与服务里都生效的做法针对不同场景处理方式不一样脚本里最直接的办法是在脚本开头显式 source 一次#!/usr/bin/env bash source /etc/profile.d/java.sh source /etc/profile.d/maven.sh缺点是每个脚本都要写两行容易被忘。更彻底的做法是把环境变量声明放在脚本头部不依赖外部文件export JAVA_HOME/opt/java export MAVEN_HOME/opt/maven export PATH$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATHsystemd 服务里不要指望它能读到profile.d。正确写法是在 unit 文件里用Environment指令[Service] EnvironmentJAVA_HOME/opt/java EnvironmentPATH/opt/java/bin:/opt/maven/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin ExecStart/opt/maven/bin/mvn -f /srv/app/pom.xml packageExecStart里用命令的绝对路径比依赖 PATH 查找更稳妥。systemd 的 PATH 默认值本来就比你登录后的短这是很多人服务启动失败的原因。容器镜像里用ENV指令设置并且要注意基础镜像里有没有预装 JDK。如果有你的路径可能被覆盖或者PATH顺序不对导致构建时用的是基础镜像自带的那个。构建完在容器里跑一次java -version确认别等到运行时报错。持续集成环境里一般都有专门的 JDK 版本声明配置项优先用平台提供的机制而不是自己在脚本里塞 export。平台机制的好处是它会帮你处理缓存、切换、多版本并存这些事自己塞 export 反而容易和平台逻辑打架。最后分享一个我自己在用的小技巧在机器的登录提示里加一行版本信息。把下面这段放进/etc/profile.d/java.sh末尾echo [env] JAVA_HOME$JAVA_HOME ($(java -version 21 | head -n1))每次登录就能看到当前生效的 JDK多台机器来回切的时候不用每次手动查少犯好几次“在错误的机器上跑构建”的迷糊。这个输出只在交互登录时出现不影响脚本执行代价几乎为零。
企业数字化 ERP 产品动态
相关推荐
贝叶斯定理:从数学原理到AI与金融实战应用 1. 贝叶斯定理:从直觉到数学的认知升级第一次听说贝叶斯定理是在研究生时期的机器学习课上,教授用"假阳性检测"的例子彻底颠覆了我对概率的认知。当时那个关于"某种疾病检测准确率99%,但检测呈阳性的人实际患病的概率可能不足… · 2026/11/2 19:41:12
Vue+WebUploader实现医疗大文件分片上传方案 1. 医疗系统中的病历图片上传挑战在医疗信息化系统中,病历图片上传是一个高频且关键的操作场景。一张普通的CT影像通常在20-50MB之间,而一套完整的检查资料包可能达到500MB以上。传统单文件上传方式在面对这种体量的数据时,往往会遇到以下典型… · 2026/11/2 19:40:11
Qt数据库开发:核心组件与实战技巧详解 1. Qt数据库开发核心组件解析在桌面和嵌入式应用开发中,数据库操作是刚需场景。作为跨平台框架的Qt,提供了一套完整的数据库访问方案,主要由三个核心类构成技术栈:QSqlDatabase:数据库连接管理器QSqlQuery:… · 2026/11/2 19:40:11
逆向六款开源RAG:从架构拆解到可复用自研蓝图 1. 为什么我要花两周时间逆向六款开源RAG过去大半年,我一直在带团队做企业级的知识问答系统。说实话,RAG这个词从2023年火到现在,网上的教程一抓一大把,但真正落到生产环境里,问题就全冒出来了:召回率上不去… · 2026/11/2 20:26:45
自动字幕生成全链路:ASR、VAD与强制对齐原理及SRT导出实战 1. 自动字幕到底在解决什么问题做视频的人都有一个共同的痛:素材剪完了,导出前发现还得加字幕。手动敲时间轴这件事,十分钟的视频能敲掉你两个小时,而且敲完还得反复对轴,嘴型对不上、字幕提前跑、延迟半秒,… · 2026/11/2 20:26:45
MATLAB工程级SMOTE:解决小样本分类失衡的鲁棒实现 简介:本资源是面向机器学习初学者与MATLAB实践者的SMOTE算法轻量级实现包,专为解决分类任务中少数类样本不足导致的模型偏差问题而设计,适用于科研实验、课程设计及不平衡数据预处理场景。压缩包共5个文件,含2个核心MATLAB函数&am… · 2026/11/2 20:26:45
护景贴技术解析:第四代屏幕保护膜的创新与应用 1. 产品定位与核心价值解析"护景贴"这个命名本身就蕴含着产品设计的深层思考。"护"代表保护功能,"景"暗指屏幕显示内容,"贴"则体现了产品的物理形态和使用方式。这种命名方式既保留了传统屏幕保护贴的基本属性&… · 2026/11/2 20:26:45
机器人叉车在智慧仓储中的核心技术与应用实践 1. 项目概述:当机器人叉车遇见智慧仓储去年参观某大型物流园区时,我第一次看到数十台无人叉车在5米高的立体库位间自如穿梭的场景——它们以毫米级精度将托盘送入指定货位,全程无需人工干预。这让我意识到,机器人叉车已从实验室概… · 2026/11/2 20:26:45
云沙箱文件通道设计:从Workspace边界到Agent安全隔离 很多第一次接触云沙箱的人都会有个误解:以为 Agent 在云端跑的是一台完整虚拟机,能直接操作任意文件、任意进程。实际上,云沙箱里的 Agent 真正能摸到的只有一块被精心划定出来的 Workspace 目录。文件通道这个概念,就是把这块目录… · 2026/11/2 20:25:45
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18
雷达信号调制技术解析:从LFM到伪线性调频原理与应用 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15
基于MAX14431与TM4C123的工业SPI隔离电路设计全攻略 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15
PLFM_RADAR:相位线性调频雷达信号处理框架 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18