java -cp

语法

java -cp <classpath> <main-class> [args...]

classpath 的格式:

  • Windows:英文分号;分隔
  • Linux/macOS:冒号:分隔
  • 支持*通配符:通过lib/*匹配所有jar包

核心流程

第一步:通过sun.misc.Launcher读取-cp参数

第二步:将路径字符串按分隔符拆分,生成File[]并转为URL[]

第三步:创建URLClassLoader,设置URLClassPath

第四步:AppClassLoader.loadClass("com.example.App")在URLClassPath中搜索.class文件

第五步:通过defineClass()验证→准备→解析→初始化类实例

第六步:反射调用main(String[])方法

图片

特性

  • 能够完全控制classpath路径,精确指定哪些目录/jar参与类加载
  • 忽略jar包内的MANIFEST.MF声明
  • 通配符*在JVM层面展开

脚本示例

#!/bin/bash# Linux启动脚本start.sh# 适用场景:依赖多个外部jar,需要灵活指定classpath
# 配置APP_NAME="my-service"BASE_DIR="/opt/app/${APP_NAME}"MAIN_CLASS="com.example.Bootstrap"JAVA_OPTS="-Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
# 构建classpath(含lib目录下所有jar和config目录)CLASSPATH="${BASE_DIR}/config"for jar in "${BASE_DIR}/lib"/*.jar; do    CLASSPATH="${CLASSPATH}:${jar}"done
# 启动exec java ${JAVA_OPTS} \    -cp "${CLASSPATH}" \    -Dapp.name="${APP_NAME}" \    -Dlogging.config="${BASE_DIR}/config/logback.xml" \    ${MAIN_CLASS} \    "$@"

java -jar

语法

java -jar app.jar [args...]

核心流程

图片

Manifest文件声明示例

Manifest-Version: 1.0Main-Class: com.example.BootstrapClass-Path: lib/spring-core.jar lib/spring-context.jar lib/logback.jar

JVM规范约束

根据JLS§12.1.1和JVM规范§5.2中规定,在java -jar模式下需满足以下约束:

  1. -cp/-classpath参数将被IGNORED(强制忽略)
  2. CLASSPATH环境变量将被IGNORED (强制忽略)
  3. 唯一classpath来源等于jar包本身 + MANIFEST.MF的Class-Path声明的路径
  4. 如果MANIFEST.MF缺少Main-Class属性则抛出错误

脚本示例

#!/bin/bash# Linux启动脚本start.sh# 适用场景:SpringBoot fat jar部署
APP_NAME="my-boot-app"JAR_PATH="/opt/app/${APP_NAME}/${APP_NAME}.jar"JAVA_OPTS="    -Xms2g -Xmx2g    -XX:+UseG1GC    -XX:MaxGCPauseMillis=200    -XX:+HeapDumpOnOutOfMemoryError    -XX:HeapDumpPath=/opt/app/${APP_NAME}/dump/    -Dspring.profiles.active=prod    -Dcom.sun.management.jmxremote"
# 检查 jar 是否存在if [ ! -f "${JAR_PATH}" ]; then    echo "ERROR: ${JAR_PATH} not found!"    exit 1fi
# 启动exec java ${JAVA_OPTS} \    -jar "${JAR_PATH}" \    "$@"

适用场景

在实际应用场景中java -cp和java -jar该如何选择?它们分别使用哪些场景中呢?

-cp的适用场景

  • 微服务多模块:各模块独立jar包,启动时按需组合
  • 开发/调试:快速切换主类,不需要重新打包
  • 工具类/CLI:依赖外部jar包的动态变化
  • 自定义ClassLoader:需要精准控制类加载顺序

-jar的适用场景

  • Spring Boot部署:fat jar约定优于配置
  • 容器化(Docker):单一交付物,镜像简洁
  • CI/CD发布:版本管理简单,一个jar包一个版本
  • 运维友好:非技术人员只需一个命令

注意事项

  • -jar命令中-cp命令将会被忽略
# ❌错误:-cp被忽略,Main-Class不会在my-utils.jar中搜索java -jar app.jar -cp my-utils.jar
# ✅正确:Class-Path写在MANIFEST.MF声明中# 或在jar外层用-jar依赖其他jar时,使用-Xbootclasspath/a或扩展类路径
  • -cp命令中MANIFEST.MF文件声明不生效
# -cp不会读取MANIFEST.MF中的Main-Classjava -cp app.jar  # 会报错 "找不到或无法加载主类"
# 必须显式指定java -cp app.jar com.example.Main
  • 通配符使用
# ✅正确使用方式java -cp "lib/*:config" com.example.App
# ❌错误:引号缺失,shell展开*可能会有问题java -cp lib/*:config com.example.App

总结

一句话总结:java -cp是我告诉你主类在哪,也告诉你依赖在哪,而java -jar则是约定优于配置。java -jar方式是从MANIFEST.MF声明中获取启动信息,java -cp方式则从命令行取相关信息,但两条两种方式最终都是通过URLClassLoader.loadClass()来解析和加载相关类的。

扫码领红包

微信赞赏支付宝扫码领红包

发表回复

后才能评论