Java开发环境搭建的核心从来都不是IDE。JDK提供了完整的编译、运行、打包工具链,只要正确安装JDK并配置好环境变量,纯命令行完全可以完成从源码到可执行JAR的完整流程。IDE的本质是在这些命令之上封装了图形界面、代码补全和调试器,它并不是必需的。下面通过几个层次的示例来验证命令行开发的可行性。

先厘清JDK与IDE的职责边界
经常有人把JDK和IDE混为一谈,认为装了IntelliJ IDEA就等于装好了Java环境。实际上IDEA只是调用了JDK里现成的工具,真正的编译动作由javac完成,运行由java完成,打包由jar完成。即使卸载掉所有IDE,只要JDK还在,就可以用命令行完成开发。Windows下安装JDK后需要设置两个环境变量:JAVA_HOME指向JDK安装目录,例如C:\Program Files\Java\jdk-17;另外把%JAVA_HOME%\bin追加到Path变量中。Linux或macOS可以在shell配置文件里加入export JAVA_HOME=/usr/lib/jvm/java-17-openjdk和export PATH=$JAVA_HOME/bin:$PATH。配置完成后打开终端输入javac -version和java -version,能正常显示版本就说明命令行工具链已经就绪。
javac的职责非常单一:读取.java源文件,检查语法和类型,生成与平台无关的.class字节码文件。它不负责运行,也不负责依赖管理。java命令则负责启动JVM,加载指定的主类并执行main方法。理解这两个命令的分工,后续所有关于类路径、包结构、JAR打包的问题都会变得清晰。IDE把这些步骤藏在了按钮后面,开发者点击运行,IDE内部就是拼接了类似的命令。
从单文件到多包项目的命令行编译
先在任意目录创建一个HelloWorld.java文件,用记事本或者Vim写入以下内容。注意文件名必须与公共类名完全一致,否则编译会直接报错。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello from command line");
}
}
在当前目录打开终端,执行javac HelloWorld.java,目录下会生成HelloWorld.class文件。接着运行java HelloWorld,终端输出字符串。这里不需要任何项目结构,也不需要IDE。如果源文件里有中文注释,建议在编译时加上-encoding UTF-8参数,避免Windows默认编码不一致导致乱码。
实际项目几乎不可能只有一个无包名的类。Java的包名必须与目录层级对应,假设源文件路径为src/com/example/Main.java,包声明为com.example,编译时如果直接运行javac src/com/example/Main.java,.class文件会生成在源码同一目录,运行时必须切换到src目录再执行java com.example.Main,很不方便。更好的做法是用-d参数指定输出目录,把源码和字节码分离。
javac -d out src/com/example/Main.java java -cp out com.example.Main
上面的命令会把com/example/Main.class按照包结构输出到out目录,运行时通过-cp指定类路径为out,JVM就能找到com.example.Main。如果项目存在多个包下的多个源文件,可以一次性传入所有文件:javac -d out src/com/example/Main.java src/com/example/util/StringUtil.java,或者使用通配符javac -d out $(find src -name "*.java")。这也是大部分构建工具底层干的事情。
第三方依赖、JAR打包与模块化
命令行开发最大的挑战来自第三方依赖。IDE里点一下Maven刷新就能引入的库,在纯命令行下需要手动下载对应的.jar文件,放到项目目录,然后编译和运行时都通过-cp把依赖加进去。Windows下多个路径用分号分隔,Linux和macOS用冒号分隔。假设依赖是当前目录下的libs/gson-2.10.1.jar,编译命令可以写成:
# Linux / macOS javac -cp libs/gson-2.10.1.jar -d out $(find src -name "*.java") java -cp out:libs/gson-2.10.1.jar com.example.Main # Windows javac -cp libs/gson-2.10.1.jar -d out src/com/example/Main.java java -cp out;libs/gson-2.10.1.jar com.example.Main
可以看到依赖一旦增多,手工维护-cp很快会变得极其繁琐,这也是IDE和构建工具存在的核心价值。不过命令行下同样可以使用Maven或Gradle,它们本身也是命令行工具,并不要求安装IDE。比如在项目根目录放一个pom.xml,执行mvn compile、mvn package,依赖解析和打包都会自动完成。严格来说这仍属于命令行开发,只是用构建脚本替代了手工敲javac。
如果只依赖JDK自带的类,可以把编译好的字节码打包成可执行JAR。下面的命令在out目录下生成app.jar,并通过--main-class指定入口类。
jar --create --file app.jar --main-class com.example.Main -C out . java -jar app.jar
Java 9引入的模块化系统也可以完全通过命令行走通。javac增加--module-source-path参数,java增加--module-path和-m参数,加上module-info.java文件,就可以编译和运行模块化项目。命令行操作反而比某些IDE设置模块路径更直观,因为所有参数都显式写出来,不会藏在某个配置窗口里。
纯命令行开发的适用边界与混合方案
纯命令行开发最明显的优势是环境轻量、启动快,尤其适合服务器运维、脚本化构建、CI/CD流水线中的编译步骤、编写小工具或验证某个语法特性。服务器通常没有图形界面,SSH过去只能用命令行,此时不可能打开IDE。学习阶段用命令行也有好处,能强迫你理解类路径、包结构和模块边界,避免变成只会点绿色三角形却不知道底层发生什么的开发者。
但纯命令行的缺陷同样突出。缺少实时语法检查,错误要等到编译阶段才暴露;没有自动补全和重构,大型项目里写代码效率极低;调试只能靠jdb命令行调试器,操作门槛远高于图形化调试。当项目代码量超过数千行、模块数量多、第三方依赖复杂时,纯手工维护命令参数已经不具备工程可行性。
更务实的做法是采用混合方案:使用VS Code、Sublime Text、Vim等轻量编辑器负责代码编写,终端交给命令行完成编译、测试和打包。VS Code安装Java扩展包后同样提供代码补全和跳转,但它比完整IDE更轻,启动快,而且扩展只是调用JDK和构建工具,不会绑架你的开发流程。日常小项目或脚本工具用这种组合足够;大型企业级项目再根据需要引入IDEA或Eclipse,本质上都是用同一个JDK工具链,切换起来没有想象中那么割裂。