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层面展开
脚本示例
# 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; doCLASSPATH="${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
核心流程

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模式下需满足以下约束:
-
-cp/-classpath参数将被IGNORED(强制忽略) -
CLASSPATH环境变量将被IGNORED (强制忽略) -
唯一classpath来源等于jar包本身 + MANIFEST.MF的Class-Path声明的路径 -
如果MANIFEST.MF缺少Main-Class属性则抛出错误
脚本示例
# 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}" ]; thenecho "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()来解析和加载相关类的。
微信赞赏
支付宝扫码领红包
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。侵权投诉:375170667@qq.com
