Ubuntu安装Java 17:APT与手动配置详解及多版本管理
1. 为什么在Ubuntu上安装Java 17需要“讲究”方法如果你在Ubuntu上搜索过“如何安装Java”大概率会看到两种主流方案一是直接apt install openjdk-17-jdk二是去Oracle官网下载.tar.gz包手动配置。这两种方法都能装上Java但背后的“讲究”可大不相同直接关系到你后续开发的稳定性和维护成本。我见过太多新手在环境配置上踩坑比如项目编译时突然报“找不到javac”或者系统更新后Java版本被意外替换导致整个构建流程崩溃。所以今天我们不只讲“怎么装”更要拆解清楚“为什么这么装”以及在不同场景下比如个人开发、生产服务器、Docker容器的最佳选择是什么。Java 17作为最新的LTS长期支持版本带来了很多现代语言特性比如密封类、模式匹配的增强是现在新项目起步的推荐选择。在Ubuntu上特别是22.04 LTS及之后的版本系统仓库已经提供了成熟的OpenJDK 17包。但直接apt install安装的和你从Adoptium原AdoptOpenJDK或Oracle官网下载的在细微的打包策略、默认配置甚至性能调优上可能存在差异。对于绝大多数开发场景使用系统包管理器安装的OpenJDK是最省心、最易于维护的方案它能自动处理依赖、更新和与其他系统软件的兼容性。但如果你需要特定供应商的JDK比如为了使用某些商业特性或工具或者需要在同一台机器上管理多个Java版本手动安装并配置就成了必须掌握的技能。2. 方案一使用APT包管理器安装OpenJDK 17推荐给大多数用户这是最快捷、最“Ubuntu”的方式适合绝大多数开发者和服务器环境。它的核心优势在于与系统深度集成更新、卸载都通过apt管理非常干净。2.1 更新软件包索引与安装核心组件在安装任何新软件之前更新本地软件包索引是一个好习惯。这能确保你获取到仓库中最新的软件包信息和版本。sudo apt update这个命令会连接配置的软件源如archive.ubuntu.com获取最新的软件包列表。执行完毕后就可以开始安装Java了。OpenJDK 17提供了两个主要包openjdk-17-jdk和openjdk-17-jre。对于开发工作你必须安装JDKJava Development Kit因为它包含了运行Java程序所必需的JREJava Runtime Environment以及编译器javac、调试器jdb等开发工具。如果只是运行Java应用例如一些服务端软件安装JRE就足够了。sudo apt install openjdk-17-jdk执行这条命令后apt会解析依赖关系。你会发现它不仅会安装openjdk-17-jdk还会自动安装与之关联的openjdk-17-jre-headless等包。-headless版本意味着它不包含图形界面相关的库更适合服务器环境。整个安装过程是全自动的包括设置一些基础的符号链接。2.2 验证安装与理解安装路径安装完成后立即验证是最稳妥的做法。我们通过检查版本号来确认。java -version如果安装成功你会看到类似下面的输出openjdk version 17.0.10 2024-01-16 OpenJDK Runtime Environment (build 17.0.107-Ubuntu-1ubuntu122.04) OpenJDK 64-Bit Server VM (build 17.0.107-Ubuntu-1ubuntu122.04, mixed mode, sharing)同样检查编译器javac -version输出应为javac 17.0.10注意通过apt安装的OpenJDK其可执行文件通常位于标准的系统路径下如/usr/bin/java和/usr/bin/javac。它们实际上是连接到真实安装目录的符号链接。你可以使用readlink -f $(which java)来追踪真实路径通常会指向/usr/lib/jvm/java-17-openjdk-amd64/bin/java这样的目录。这种由系统包管理器维护的链接关系是环境能自动工作的关键。2.3 此方案的优缺点与适用场景优点一键安装无需手动配置环境变量apt在安装时已经帮你把java和javac的链接放到了/usr/bin而这个目录默认就在系统的PATH环境变量中。这是与手动安装最大的便利区别。自动管理依赖所有相关的库文件都会被自动安装。易于更新和卸载未来可以通过sudo apt upgrade统一升级系统所有软件包括Java。卸载也只需sudo apt remove openjdk-17-jdk相对干净。系统一致性高特别适合在干净的服务器或作为系统基础服务运行时使用。缺点版本可能非最新小版本Ubuntu官方仓库的版本更新可能会稍慢于上游供应商。例如上游已经发布了17.0.11但仓库可能还停留在17.0.10。对于需要紧跟所有安全补丁的场景这可能是个考虑因素。供应商锁定你使用的是Ubuntu打包的OpenJDK虽然它源自上游OpenJDK项目但打包配置可能带有发行版的特定调整。适用场景个人开发环境、团队统一使用的开发机、生产服务器尤其是当你希望系统保持统一包管理时、以及任何追求简单稳定、不需要多版本并存的场景。3. 方案二手动下载安装包并配置适用于多版本管理与特定需求当你需要在一台机器上同时维护Java 8、11、17等多个版本或者需要特定构建如Adoptium的HotSpot JVM、Oracle的JDK手动安装是更灵活的选择。这个过程涉及下载、解压、移动和手动配置环境变量。3.1 选择与下载JDK发行版首先你需要决定从哪里下载。常见来源有Adoptium推荐提供高质量的、经过TCK技术兼容性工具包认证的OpenJDK构建社区驱动完全开源。访问 https://adoptium.net/ 。Oracle OpenJDKOracle官方提供的OpenJDK构建从Java 17开始Oracle也提供了免费的GPL许可的OpenJDK构建。访问 https://jdk.java.net/17/ 。其他供应商如Azul Zulu、Amazon Corretto等它们提供额外的支持、性能优化或长期维护保证。这里以从Adoptium下载Linux x64的.tar.gz包为例。你可以在官网选择版本、JVM实现HotSpot或OpenJ9、架构等。通常我们选择最新的LTS版本17、HotSpot JVM、x64架构的压缩包。在终端中我们可以使用wget或curl直接下载。首先找到确切的下载链接建议从官网页面复制。例如wget https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.10%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.10_7.tar.gz实操心得下载链接可能会变。更稳妥的做法是打开Adoptium网站在下载页面右键点击你需要的包选择“复制链接地址”然后粘贴到wget命令后。这样可以避免因手动拼接URL出错导致下载失败。3.2 解压与部署到系统目录下载完成后是一个.tar.gz压缩包。我们需要将其解压并放到一个合适的系统目录。Linux系统有一个约定俗成的目录/usr/lib/jvm/专门用于存放不同版本的Java。# 创建目标目录如果不存在 sudo mkdir -p /usr/lib/jvm # 解压下载的压缩包到 /usr/lib/jvm sudo tar -xzf OpenJDK17U-jdk_x64_linux_hotspot_17.0.10_7.tar.gz -C /usr/lib/jvm/解压后/usr/lib/jvm/目录下会多出一个类似jdk-17.0.107的文件夹。这个文件夹包含了完整的JDK。3.3 关键步骤手动配置环境变量这是手动安装的核心也是容易出错的地方。环境变量告诉系统在哪里可以找到Java的可执行文件。我们需要配置两个主要变量JAVA_HOME和PATH。为什么需要JAVA_HOME很多Java应用、服务器如Tomcat、Gradle以及IDE如IntelliJ IDEA都依赖JAVA_HOME环境变量来定位Java的安装根目录。它指向的是JDK的安装路径即包含bin、lib等子目录的路径。为什么需要修改PATHPATH变量定义了系统查找可执行文件的目录顺序。我们需要将JDK的bin目录$JAVA_HOME/bin添加到PATH中这样在终端里直接输入java、javac时系统才能找到它们。配置环境变量有用户级只对当前用户生效和系统级对所有用户生效两种方式。对于个人开发机配置用户级即可。编辑用户级配置文件以bash为例nano ~/.bashrc或者使用vim ~/.bashrc。在文件的末尾添加以下行export JAVA_HOME/usr/lib/jvm/jdk-17.0.107 export PATH$JAVA_HOME/bin:$PATH重要提示JAVA_HOME的路径必须是你实际解压的JDK文件夹的绝对路径。请务必根据你解压出的文件夹名称进行修改。PATH的修改中$JAVA_HOME/bin:$PATH表示将新的路径添加到原有PATH的最前面这样系统会优先使用我们手动安装的Java。保存并退出编辑器在nano中是CtrlX然后按Y确认再按回车。使配置立即生效source ~/.bashrc这条命令会重新加载.bashrc文件让刚才设置的环境变量在当前终端会话中生效。3.4 验证手动安装结果现在打开一个新的终端窗口或者确保在当前窗口执行了source ~/.bashrc然后进行验证echo $JAVA_HOME应该输出/usr/lib/jvm/jdk-17.0.107。java -version应该显示来自AdoptiumTemurin的版本信息而不是之前APT安装的版本。which java命令应该指向/usr/lib/jvm/jdk-17.0.107/bin/java。如果验证失败请检查JAVA_HOME的路径是否正确末尾没有斜杠。.bashrc文件中的语法是否正确特别是export关键字和等号两边不能有空格。是否执行了source ~/.bashrc或打开了新的终端。3.5 管理多个Java版本与update-alternatives工具手动安装的另一个强大之处是便于多版本共存和切换。Linux提供了update-alternatives工具来管理系统级命令的默认版本。即使你通过APT安装了Java手动安装后也可以用它来切换默认版本。首先为你手动安装的Java 17注册到系统中sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-17.0.107/bin/java 1 sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-17.0.107/bin/javac 1这里的1是优先级数字越大优先级越高。如果同时存在多个选项系统会选择优先级最高的作为默认。然后你可以通过以下命令交互式地选择默认的Java版本sudo update-alternatives --config java系统会列出所有已注册的Java版本并让你输入选择编号。这个工具非常有用特别是在处理一些严格要求特定Java版本的老旧项目时。4. 安装后的关键配置与常见问题排查安装Java只是第一步要让它在开发中顺畅工作还需要理解一些关键配置和知道如何排查常见问题。4.1 理解JAVA_HOME与IDE、构建工具的集成几乎所有现代Java开发工具都依赖JAVA_HOME。例如在IntelliJ IDEA中你可以在File - Project Structure - SDKs中添加新的JDK路径就指向JAVA_HOME。对于Maven或Gradle它们通常会自动检测JAVA_HOME但也可以在各自的配置文件mavenrc或gradle.properties中显式指定。如果你在IDE中编译项目时遇到“JDK not found”或版本错误第一件事就是去检查IDE中配置的JDK路径是否与终端中echo $JAVA_HOME的输出一致。不一致是导致“项目在终端能编译在IDE里报错”的常见原因。4.2 内存设置与JAVA_OPTS环境变量对于需要运行大型应用如Spring Boot微服务、数据处理应用的情况你可能需要调整JVM的堆内存大小。这可以通过设置JAVA_OPTS或应用特定的环境变量来实现。例如在启动应用前设置export JAVA_OPTS-Xms512m -Xmx2G-Xms指定初始堆大小-Xmx指定最大堆大小。然后运行你的Java程序JVM就会应用这些参数。踩坑记录在容器化如Docker环境中JVM不会自动感知容器的内存限制。如果你在Docker中运行Java应用而未设置-XmxJVM可能会根据宿主机内存来分配堆导致容器因内存超限而被杀死。最佳实践在Dockerfile或容器启动命令中总是根据容器内存限制来显式设置JVM堆参数例如使用-XX:MaxRAMPercentage75.0这样的参数让堆大小占容器可用内存的百分比这比固定值更灵活。4.3 常见问题与解决方案排查表即使按照步骤操作也可能遇到问题。下面是一个快速排查指南问题现象可能原因解决方案执行java -version显示“未找到命令”1. Java未安装成功。2.PATH环境变量未正确配置。1. 重新执行安装命令确保无报错。2. 检查echo $PATH看是否包含JDK的bin目录。对于手动安装确认.bashrc已修改并source。显示版本与预期不符例如还是旧版本系统中有多个JavaPATH中旧版本的路径在前。1. 检查which java确定当前使用的是哪个路径下的Java。2. 调整PATH顺序手动安装时把新路径放前面或使用update-alternatives --config java切换。javac命令找不到但java命令可用只安装了JRE运行时环境没有安装JDK开发工具包。通过APT安装openjdk-17-jdk或确保手动下载的是JDK而非JRE包。IDE如IntelliJ找不到JDKIDE中配置的JDK路径错误或未配置。在IDE的设置中手动添加JDK路径指向JAVA_HOME即包含bin、lib等目录的文件夹。运行程序报java.lang.UnsupportedClassVersionError编译用的JDK版本高于运行时的JRE版本。例如用Java 17编译的.class文件在Java 11上运行。确保运行环境的Java版本 编译环境的Java版本。统一开发、构建和运行环境的Java版本。程序启动报java: command not found在脚本中脚本通常在非交互式shell中运行可能未加载用户级的.bashrc或.bash_profile。1. 在脚本中显式设置JAVA_HOME和PATH。2. 或将Java安装到系统级路径如/usr/bin通过APT安装会自动完成。4.4 安全更新与版本维护无论采用哪种安装方式保持Java更新以获取安全补丁都至关重要。对于APT安装定期运行sudo apt update sudo apt upgrade系统会更新所有包包括OpenJDK。对于手动安装你需要关注Adoptium或你所用发行版的官网公告手动下载新版本压缩包解压到新目录例如jdk-17.0.118然后更新JAVA_HOME环境变量指向新路径并重新注册update-alternatives。这是一个相对手动的过程建议将此过程脚本化。5. 进阶场景在Docker容器中安装Java 17在现代开发和部署中Docker容器化非常普遍。在Dockerfile中安装Java追求的是镜像体积小、构建速度快、层次清晰。5.1 基于官方镜像最简单的方式最推荐的方式是直接使用官方提供的JDK镜像作为基础镜像。例如在Dockerfile中FROM eclipse-temurin:17-jdk-jammy # 你的应用复制和启动命令... COPY target/myapp.jar app.jar ENTRYPOINT [java, -jar, /app.jar]eclipse-temurin:17-jdk-jammy是基于Ubuntu Jammy22.04的Temurin JDK 17镜像。这种方式省去了所有安装和配置步骤镜像也经过优化。5.2 在Ubuntu基础镜像中安装如果你需要一个更定制化的基础环境可以选择一个轻量级的Ubuntu镜像如ubuntu:22.04然后在其中安装Java。这里就有讲究了为了减小最终镜像的体积我们通常会在安装后清理APT缓存。FROM ubuntu:22.04 # 避免安装过程中交互式提示如时区选择 ENV DEBIAN_FRONTENDnoninteractive RUN apt-get update \ apt-get install -y openjdk-17-jdk-headless \ # 清理APT缓存这是减小镜像体积的关键步骤 apt-get clean \ rm -rf /var/lib/apt/lists/* # 设置环境变量在Docker中通常也需要 ENV JAVA_HOME/usr/lib/jvm/java-17-openjdk-amd64 ENV PATH$JAVA_HOME/bin:$PATH # ... 后续复制你的应用注意这里安装的是openjdk-17-jdk-headless它比完整的jdk包体积更小因为它去除了图形界面和Web Start等非服务器必需的组件非常适合容器环境。5.3 多阶段构建优化生产镜像对于生产环境我们通常使用多阶段构建。在第一阶段构建阶段使用完整的JDK来编译和打包应用在第二阶段运行阶段使用仅包含JRE的轻量级镜像来运行应用这能极大减少最终镜像的体积和潜在攻击面。# 第一阶段构建 FROM eclipse-temurin:17-jdk-jammy AS builder WORKDIR /app COPY . . # 假设使用Maven这里执行编译打包 RUN ./mvnw clean package -DskipTests # 第二阶段运行 FROM eclipse-temurin:17-jre-jammy AS runner WORKDIR /app # 从构建阶段复制打包好的jar文件 COPY --frombuilder /app/target/myapp.jar app.jar # 以非root用户运行增加安全性 RUN useradd -m myuser USER myuser ENTRYPOINT [java, -jar, /app.jar]这种模式分离了构建依赖和运行依赖是容器化Java应用的最佳实践之一。

相关新闻

(七)gdb调试 及静态库共享库

(七)gdb调试 及静态库共享库

一、gdb 常用调试命令 前提:编译程序必须加 -g 参数,加入调试信息,gcc main.c -g -o main,否则 gdb 看不到源码。 命令作用l / l 行号list,显示源码;l 10直接跳到第 10 行附近显示代码b 行号 / b 函数名bre…

2026/9/28 1:20:04 阅读更多 →
深入解析STM32F429系统架构:从总线矩阵到DMA与中断协同设计

深入解析STM32F429系统架构:从总线矩阵到DMA与中断协同设计

1. 从“单片机”到“微控制器”:STM32F429的定位转变很多工程师朋友一提到STM32,脑子里蹦出来的第一个词可能还是“单片机”。这个称呼没错,但用在STM32F429这类芯片上,就显得有点“委屈”它了。传统的8051、AVR单片机&#xff0c…

2026/10/8 19:25:52 阅读更多 →
八大排序系列(四):归并排序 —— 分治思想的另一条路径,稳定排序的代表

八大排序系列(四):归并排序 —— 分治思想的另一条路径,稳定排序的代表

八大排序系列(四):归并排序 —— 分治思想的另一条路径,稳定排序的代表上一篇我们拆解了快速排序 —— 这个靠「分区 递归」把排序效率拉到 O (nlogn) 的工业级算法,结尾留下了一个核心问题:同样是分治思想…

2026/10/8 11:07:06 阅读更多 →

最新新闻

从直连到聚合:大模型API对接的架构演进与实战避坑指南

从直连到聚合:大模型API对接的架构演进与实战避坑指南

1. 从直连到聚合:API对接思路的转变2026年开年到现在,我陆续参与了三个不同规模的AI应用项目,从智能客服到内容生成工具,再到一个内部知识库问答系统。这三个项目有一个共同点:都需要对接大模型API。而在这三个项目的推…

2026/10/13 16:19:32 阅读更多 →
Linux内核inotify联动端侧模型:实现敏感配置文件变动的毫秒级语义差分告警

Linux内核inotify联动端侧模型:实现敏感配置文件变动的毫秒级语义差分告警

运维堡垒机与核心边缘节点上,/etc/pam.d/、/etc/sudoers 以及各类服务的核心配置文件始终是攻防对抗的最前线。传统的内核审计大多仰仗 auditd 规则或轻量级的 inotify 机制。当文件发生写入关闭事件(IN_CLOSE_WRITE)时,工具通常会…

2026/10/13 16:19:32 阅读更多 →
易语言视频转文本源码解析:从取帧到字幕文本的完整实现

易语言视频转文本源码解析:从取帧到字幕文本的完整实现

简介:面向易语言开发者的视频转文本功能实现源码,定位在图形图像与多媒体处理场景,适合对OCR、语音识别及中文编程感兴趣的初中级学员。压缩包共三个文件,大小约6.3MB,包含易语言主程序源文件(.e)、演示动画(.swf)和HT…

2026/10/13 16:19:32 阅读更多 →
易语言 esqlite3 V1.1 源码升级:多线程事务锁与加密数据库实战

易语言 esqlite3 V1.1 源码升级:多线程事务锁与加密数据库实战

简介:本资源为 sqlite3 易语言支持库与模块的 esqlite3 V1.1 版本,面向使用易语言进行数据库开发的中高级开发者,重点解决多线程环境下 SQLite 操作的稳定性与功能扩展问题。相比 1.0,本次更新新增了 S3 互斥体进入/退出、S3 聚合…

2026/10/13 16:19:32 阅读更多 →
WeTTY 源码安装与开发环境搭建:从 git clone 到热重载开发服务器

WeTTY 源码安装与开发环境搭建:从 git clone 到热重载开发服务器

后端开发工具运维 【免费下载链接】wetty Terminal in browser over http/https. (Ajaxterm/Anyterm alternative, but much better) 项目地址: https://gitcode.com/gh_mirrors/we/wetty 点击查看 免费下载 本文是基于 docs/development.md 展开的源码级开发指南&…

2026/10/13 16:19:32 阅读更多 →
交互式 ER 图 + Schema 差异对比 + 迁移SQL生成:LibreDB Studio 数据库设计工作流完整实操

交互式 ER 图 + Schema 差异对比 + 迁移SQL生成:LibreDB Studio 数据库设计工作流完整实操

【免费下载链接】libredb-studio One browser tab for PostgreSQL, MySQL, Oracle, SQL Server, MongoDB, Redis, SQLite, Couchbase, ClickHouse, Druid, DuckDB, Turso and more. An open-source SQL IDE with SSO, audit trail and AI-assisted queries MIT licensed, with …

2026/10/13 16:18:31 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →