
想学 Java 的人十有八九都会在第一步“装环境”上浪费掉半天时间然后在第一个 Java 程序跑起来之前就已经开始怀疑人生了。标题里的“JavaSE基础02”看着平平无奇其实就是从零到能跑通第一个程序、看懂基本语法的关键一步。这篇文章不讲虚的我把从下载 JDK、配环境变量到写完第一个类的完整过程以及后续学习中最容易卡壳的语法点一次性给你捋清楚。这篇内容适合完全零基础的小白也适合那些之前装过环境但老是出莫名其妙问题的朋友按着步骤走一遍你会发现 Java 入门没那么多玄学。1. 环境准备与JDK选型别再用教学视频里的老古董了1.1 JDK、JRE、JVM 的关系一次搞清楚很多新手第一次搜 Java 环境搭建会看到一堆名词JDK、JRE、JVM、OpenJDK、Oracle JDK……直接劝退。其实拿做饭来类比就很好懂JVMJava Virtual Machine是灶台负责真正把菜煮熟也就是运行 Java 程序JREJava Runtime Environment是厨房除了灶台还配好了锅碗瓢盆和调料也就是 Java 程序运行所需的类库和工具JDKJava Development Kit是整个厨房加上菜谱和刀工教程它是给“做菜的人”也就是程序员用的除了能运行程序更重要的是能用javac命令把源码编译成字节码。所以在开发阶段我们装的是JDK而不是 JRE。JDK 内部自带一套 JRE所以你装完 JDK 就不需要再额外装 JRE 了装了反而容易出现 PATH 指向混乱的问题。我自己见过不少初学者装了 JDK 又把 JRE 单独装一遍结果命令行里java -version显示的版本和javac -version对不上排查了半天才发现是 JRE 的路径在捣乱。1.2 JDK 版本怎么选17 是当前最省心的版本版本选择是很多人纠结的点。现在都出到 Java 21 甚至更新版本了我是不是该装最新的我的建议很明确新手直接从 JDK 17 开始不要碰 8也不要追最新。Java 8 是老一代的主流很多教学视频还在用但它太老了现在新项目新特性基本都围绕 17 及以后的长期支持版本展开而 21 虽然已经很成熟但部分生态工具链、网上的教程资料还没完全跟上来。17 是长期支持版本LTS意味着有稳定的更新和维护Spring Boot 3.x 也要求基于 17现在招聘市场上大部分 Java 岗位的要求也是 17 起步。更重要的是我用 17 实测下来编译速度、内存占用、启动速度都明显优于 8而且新的 switch 表达式、文本块这些语法写起来真的很舒服能让你从一开始就养成良好的现代 Java 编码习惯。注意如果你是为了应付学校考试或看老视频非要用 JDK 8那也不是不行但建议学完基础后尽快切到 17。否则你会在 lambda 表达式、var关键字这些现代特性上感受到深深的代沟。1.3 Oracle JDK 和 OpenJDK 的纠葛与账号问题关于“javase 下载账号”这个热词我多说一句。去 Oracle 官网下载 JDK 的时候确实会遇到“需要登录 Oracle 账号才能下载”的提示很多人卡在这一步。实际上从 JDK 8 之后的某个版本开始Oracle 官方对 JDK 的下载增加了账号验证而且 Oracle JDK 的许可协议也更严格。对于学习和开发来说我强烈建议直接用 OpenJDK它和 Oracle JDK 在核心 API 上几乎没有区别但下载完全免费不需要注册账号不需要登录。如果你的机器上已经有 Oracle 账号那下载也很顺手但没必要为了下载单独注册一个。当然如果你在 Windows 上最简单粗暴的方式其实是去微软的 OpenJDK 页面下载 Microsoft Build of OpenJDK或者用包管理器直接装。在 Windows 上可以用 winget install Microsoft.OpenJDK.17在 macOS 上更简单一条 Homebrew 命令brew install openjdk17省去所有网页下载的麻烦。这对新手来说是最不容易出错的方式。2. 环境变量配置详解Path 和 JAVA_HOME 的底层逻辑2.1 为什么要配环境变量操作系统的“电话簿”下载完 JDK解压或安装到一个目录后你以为就完了还差关键一步——配置环境变量。Windows 系统里当你在命令行敲java的时候操作系统其实并不知道这个命令在哪个文件夹里它只会去Path环境变量列出的目录里挨个找找到了就执行找不到就报“不是内部或外部命令”。所以配环境变量的本质就是告诉操作系统“java 这个程序住在哪”。类比一下环境变量就是操作系统的电话簿Path里存着各个程序的住址。你的新程序JDK 里的 java.exe如果没把自己登记进电话簿别人就找不到它自然也就没法运行。2.2 一步步配置 JAVA_HOME、Path 和 CLASSPATH具体到 Windows 上的操作右键“此电脑”-“属性”-“高级系统设置”-“环境变量”。在系统变量区域我给你一套实测最稳的方案第一步新建一个系统变量变量名写JAVA_HOME变量值填你的 JDK 安装路径比如C:\Program Files\Java\jdk-17.0.8。注意这里不要带bin子目录JAVA_HOME 指向的是 JDK 的根目录。第二步找到Path系统变量双击编辑在列表最前方新增两行%JAVA_HOME%\bin这样写的好处是之后如果你升级 JDK只改 JAVA_HOME 这一个变量就行了不用满世界去改 Path。第三步可选但建议新建系统变量CLASSPATH变量值填.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar。这里面的.代表当前目录意思是让 JVM 能在当前路径下找类文件。不过在 JDK 9 之后CLASSPATH 已经不强制配置了我实测用 JDK 17 跑普通入门程序不配 CLASSPATH 也完全没问题。所以新手可以跳过这步免得配错了反而干扰。2.3 验证环境是否配置成功一行命令见分晓配置完环境变量必须重启命令行窗口新打开一个 cmd然后输入java -version javac -version如果能看到如下输出说明环境已经通了java version 17.0.8 2023-07-18 LTS Java(TM) SE Runtime Environment (build 17.0.89-LTS-211) Java HotSpot(TM) 64-Bit Server VM (build 17.0.89-LTS-211, mixed mode, sharing)这里有个常见坑很多人配好后打开了新终端结果输入java -version显示的还是旧版本比如显示 1.8。这通常是因为原来的老 JDK 路径排在前面你的 Path 里新加的%JAVA_HOME%\bin不在最前面或者旧版本的 Path 项没删掉。解决方案很简单把%JAVA_HOME%\bin移到 Path 列表的顶部然后删掉其他 Java 相关的路径比如C:\Program Files\Java\jdk1.8.0_131\bin这种。提示macOS/Linux 用户别急原理完全相同只是配置文件不同。在~/.bash_profile或~/.zshrc里写三行export JAVA_HOME$(/usr/libexec/java_home -v 17)、export PATH$JAVA_HOME/bin:$PATH然后source ~/.zshrc就行。3. 第一个 Java 程序到程序运行机制亲手打破“只会抄代码”的魔咒3.1 HelloWorld 的三种写法从最规矩到最现代环境通了接下来就是那个每个程序员都逃不掉的 HelloWorld。很多教程直接让你新建一个文本文件改后缀为.java然后抄代码。但我要多讲一层因为理解“编译——运行”这两个阶段比抄代码重要得多。先在任意目录新建一个文件命名为Hello.java注意文件名必须和 public class 的类名完全一致大小写都不能错。这是 Java 的硬性规定因为 JVM 是通过文件名来找对应的类的。用记事本或者 VS Code 写入public class Hello { public static void main(String[] args) { System.out.println(Hello, Java!); } }然后在命令行切换到该文件所在目录执行javac Hello.java你会看到目录里多了一个Hello.class文件这就是字节码文件。接着执行java Hello屏幕上就会打印出Hello, Java!。注意运行命令java Hello后面不带.class后缀很多新手在这里会习惯性地写java Hello.class结果报错错误: 找不到或无法加载主类 Hello.class这个坑我见过了无数次。如果你用的 JDK 11 及以上还能直接运行单文件源码省去显式编译步骤java Hello.java对于写小 demo 来说这个方式特别香但我仍然建议你手动跑一遍javac再java的完整流程因为这样你才能真正理解编译和运行是两个独立的阶段后面学 IDE 里的一键运行时才不会一脸懵。3.2 main 方法为什么签名必须是 public static void main(String[] args)很多初学者对这个签名是死记硬背的看到public static void main(String[] args)就头皮发麻。我来拆开解释一下理解之后你永远都不会忘publicJVM 要从外部调用这个入口方法必须是公开的私有的话 JVM 进不去。staticJVM 在启动时还没有创建类的对象它需要一个不用 new 就能调用的方法所以必须是静态的。void程序入口不需要返回值跑完了就结束返回给操作系统的是退出状态码不是 Java 的返回值。main这是 JVM 约定的入口方法名相当于“电影院的安全出口”标识固定不变。String[] args这是从命令行传给程序的参数数组运行时可以java Hello 参数1 参数2在程序里用args[0]、args[1]取出来。弄懂这个签名后你对 JVM 是怎么启动一个 Java 程序的认知至少超过一半的新手。3.3 编译型还是解释型Java 是两者结合的“混血儿”为什么明明有javac把源码编译成 class 文件java命令又要把 class 文件“解释”执行这不是脱裤子放屁吗其实是 Java 采用了编译和解释混合的模式。javac做的编译是把人类写的.java源码编译成 JVM 认识的字节码.class这一步和平台无关字节码是统一的。到了java运行阶段JVM 再把字节码解释成当前操作系统能执行的机器码。这正是 Java 号称“一次编写到处运行”的根基——只要目标平台有对应的 JVM同一份 class 文件就能跑。而且现在的 HotSpot JVM 里还有 JIT即时编译技术热点代码在运行时还会被进一步编译为本机机器码运行效率并不输给纯编译型语言。所以 Java 的性能其实没有传说中那么差你得从原理上理解它的设计哲学。4. 核心语法基础变量、数据类型的背后逻辑4.1 基本数据类型和引用类型的区别用两次赋值就能想明白Java 是强类型语言变量必须先声明类型再使用。基础的数据类型可以分为四类八种整数类型byte、short、int、long、浮点类型float、double、字符类型char、布尔类型boolean。其中int是默认的整数类型范围在约 -21 亿到 21 亿之间超出这个范围就得用long并加上 L 后缀float和double的默认值是double所以给 float 赋值要加 f 后缀char用单引号包裹String才是双引号这个单双引号的区分是新手最常犯的错误之一。引用类型的概念我用一次赋值来说明。当你写int a 10; int b a; b 20;a 依然是 10因为基本类型存的是值本身赋值就是拷贝一份。但如果你用数组或对象比如int[] arr1 {1, 2, 3}; int[] arr2 arr1; arr2[0] 999; System.out.println(arr1[0]); // 输出 999arr1 的第一个元素也变成了 999因为引用类型变量存的是对象的“住址”arr1 和 arr2 指向同一块内存改一个等于改另一个。理解了这个后面学集合、对象传递的时候会少踩无数坑。4.2 变量作用域和命名规范给你的名字留点“心眼”变量的作用域就是它所在的代码块大括号范围内。出了花括号变量就消失了。很多人会在这个地方翻车在一个 if 里定义了变量出了 if 再访问报“找不到符号”然后怀疑人生。其实这就是作用域规则在起作用不是编译器故意刁难你。命名规范上类名用大驼峰HelloWorld变量名和方法名用小驼峰userName、getUserName常量用全大写下划线分隔MAX_COUNT。这是行业通用的约定不是说编译器强制而是团队协作时的“通用语”。养成好习惯后你在看别人的代码时一眼就能分清哪个是类、哪个是变量、哪个是常量。4.3 String 不是基本类型但它长得太像了初学者容易将String误认为基本类型因为它用起来太像了可以直接String s hello;直接用双引号赋值不需要new。但 String 实际上是一个引用类型是一个不可变的类。不可变意味着你不能“修改”一个字符串的内容只能重新指向一个新的字符串对象。比如String s hello; s s world;表面上 s 变成了 hello world实际上是创建了一个新的字符串对象然后让 s 指向它原来的 hello 字符串还留在内存里等垃圾回收。为什么这么设计因为字符串在 JVM 里用到极其频繁不可变的设计可以保证线程安全、支持字符串常量池复用还能用在 HashMap 的 key 上而不用担心被修改。4.4 键盘输入Scanner终于可以和程序交互了跑通 HelloWorld 之后最枯燥的阶段就算熬过去了接下来就是可以“对话”的环节。用Scanner类能让程序接收键盘输入这是写第一个有交互感的小程序最实用的方式。import java.util.Scanner; public class InputDemo { public static void main(String[] args) { Scanner sc new Scanner(System.in); System.out.print(请输入你的名字); String name sc.nextLine(); System.out.println(你好 name ); sc.close(); } }用nextLine()读取一行字符串用nextInt()读取整数用nextDouble()读取小数。有几个小细节要注意nextInt()之后如果紧跟着nextLine()中间的换行符会被后者吃掉导致读不到内容。这是因为nextInt()只读了数字没读换行符解决方法是再加一个nextLine()把换行符消耗掉。另外用完 Scanner 建议关闭释放底层的输入流尤其是写更复杂的程序时这个习惯很重要。5. 运算符和控制流程写代码逻辑的“乐高积木”5.1 几个容易踩坑的运算符细节运算符是写程序的基础但新手有好多容易在这里翻车的地方。我特别提醒三处一是整数除法。int a 7 / 2;得到的是 3不是 3.5。因为两个整数相除结果会直接截断小数部分。想要得到小数至少得让一边变成浮点数比如7 / 2.0。二是取模运算符%。它得到的是余数可以用来做奇偶判断、环形数组下标切换等。比如i % 2 0判断偶数或者让数组下标循环滚动。三是自增自减的前置后置区别。int a 5; int b a;此时 b 是 5a 变成 6int c a;此时 a 先变成 7c 也是 7。口诀是“ 在前先加再用 在后先用再加”这个没搞清楚面试笔试和日常代码都会埋雷。5.2 if、switch 的新玩法不止是“半斤八两”控制流程里最常用的就是 if-else 和 switch。基础用法我就不重复了只提两个容易忽略的细节。if-else 的串行结构里要注意条件的判断顺序。比如判断成绩等级时if (score 90) { System.out.println(优秀); } else if (score 60) { System.out.println(及格); } else { System.out.println(不及格); }这里条件从大到小排列能保证逻辑正确。如果你写成score 60在前那 90 分以上的也会走到“及格”分支这就是典型的逻辑漏洞。switch 在 JDK 14 之后支持了箭头语法可以不写 break也不会穿透。比如switch (day) { case 1 - System.out.println(周一); case 2 - System.out.println(周二); default - System.out.println(其他); }这种写法比传统的 break 写法简洁很多也省去了漏写 break 导致 fall-through穿透执行的经典 bug。如果你还是 JDK 8老老实实写 break但如果你已经切到 17请用箭头语法。5.3 循环三兄弟for、while、do-while 的选用场合for循环适合知道循环次数的场景比如遍历数组for (int i 0; i arr.length; i)。while循环适合不知道次数、只依赖某个条件的场景比如读取文件直到末尾。do-while的特点是至少执行一次再判断条件适合“先执行一次再问用户是否继续”的场景。实际写代码时还有一个容易忽略的点在循环里用break跳出循环用continue跳过本次循环的剩余代码进入下一次。这两个词在嵌套循环里只作用于最近的一层循环很多人以为 break 能把外层也跳了结果发现循环还在跑这一点在嵌套循环遍历二维数组时尤其要小心。6. 方法把复杂逻辑拆成小积木的艺术6.1 方法的参数传递到底是值传递还是引用传递几乎所有 Java 初学者都会遇到这个问题“Java 到底是值传递还是引用传递”标准答案是Java 只有值传递。基本类型传的是值的拷贝方法里面怎么改都不影响外面的变量引用类型传的是“引用地址”的拷贝方法里改这个对象的属性会影响外部对象但如果在方法里把参数重新赋值为一个新对象外部的引用不会变。太多人把“引用类型作为参数”误认为是引用传递其实不是。这里我用一个生活类比帮你锁死这个概念你把自己家的地址写在一张纸条上递给朋友朋友按地址去你家里搬走了一盆花你会发现自己家的花没了但如果朋友把纸条改成了另一个地址你家的花不会受影响。地址的拷贝是值地址指向的房子里的内容是共享的这就是 Java 传参的本质。6.2 方法重载与可变参数同名不同面参数随心配方法重载Overload是指在同一个类中可以定义多个同名方法但参数列表必须不同个数、类型或顺序不同。调用时编译器会通过实参的类型来决定具体调用哪一个这就是“编译期多态”。写重载方法时有个容易忽略的点如果两个重载方法只差返回类型比如一个返回 int 一个返回 void那是不允许的因为只靠返回类型无法区分调用的是哪个方法。JDK 5 引入的可变参数也能让方法接收不定个数的参数格式是public void printAll(String... names)在方法内部把 names 当数组用。可变参数必须放在参数列表的最后一位这个限制要记住。在写工具类或者日志封装时可变参数能省掉很多写数组创建代码的痛苦。6.3 使用递归时防爆栈是必须课递归就是方法调用自身解决“大问题拆成小问题”的场景比如计算阶乘、斐波那契数列、遍历目录树。但递归有一个致命弱点每调用一次方法JVM 就会往栈里压入一个栈帧深度过大就会抛StackOverflowError。新手学递归最容易犯的错误是忘记写递归出口代码跑起来直接 1 秒闪退然后一脸懵。正确的写法一定要有一个终止条件并且在递归调用时不断逼近这个条件。举个计算阶乘的例子public static int factorial(int n) { if (n 1) { return 1; } return n * factorial(n - 1); }这里的n 1就是递归出口。别看这个例子简单它包含了递归的全部要素边界条件、递归体、参数逼近。7. 常见问题与排查技巧实录新手翻车现场全记录7.1 “找不到或无法加载主类”的三大原因这个错误信息对 Java 初学者来说出现频率极高。我总结下来无非三种原因一是运行命令写错比如java Hello.class多了.class后缀正确写法是java Hello。 二是类文件不在当前目录运行java Hello时 JVM 只在 classpath 指定的路径找类文件如果目录不对就会报这个错。解决办法是cd到 class 文件所在目录或者用java -cp 目录 Hello指定。 三是包名问题类所在包和命令中的类名对不上。如果你的类声明了package com.demo;那 class 文件必须在com/demo目录结构下运行命令也要跑到该目录的上一层写java com.demo.Hello。7.2 “编码错误”和乱码问题记事本背锅现场很多新手习惯用 Windows 记事本写 Java 源码结果编译报错错误: 编码 GBK 的不可映射字符。这是 Windows 简体中文环境下记事本默认用 GBK 编码而 javac 默认以为是平台默认编码然后源文件里的中文注释就出了幺蛾子。解决思路有两个一是换用 VS Code、IDEA 这类现代化编辑器统一使用 UTF-8 编码二是如果非要用记事本在编译时显式指定编码javac -encoding UTF-8 Hello.java。我强烈建议从第一天就用 VS Code 或 IntelliJ IDEA不仅是为了中文注释更是为了后面的代码提示和调试功能。7.3 版本不一致的坑装了两个 JDK 的“精神分裂”这是我在帮读者排查时遇到最多的一个场景症状表现如下java -version显示 17javac -version却显示 1.8。造成这种情况的原因通常是多个 JDK 并存且java.exe和javac.exe来自不同目录。排查方法其实很简单在命令行分别执行where java where javac可以看到两个文件各自所在的路径。正常情况下两者应该指向同一个bin目录。如果不一样要么删掉多余的 JDK要么调整Path环境变量的顺序把目标 JDK 的%JAVA_HOME%\bin提到最前面。7.4 常见问题速查表症状最常见原因解决思路不是内部或外部命令Path 未配置或新终端没重启检查 JAVA_HOME 和 Path重开终端找不到或无法加载主类类名写错 / 目录不对 / 多了 .class确认文件名和类名一致运行不带后缀编码 GBK 不可映射字符记事本默认 GBK 导致中文注释乱码换 UTF-8 编辑器或编译时加 -encoding UTF-8java 和 javac 版本不一致多个 JDK 共存PATH 顺序混乱用 where 命令定位统一指向一个版本数组越界异常循环条件写错访问了下标外元素检查循环边界用i arr.length代替i arr.length空指针异常引用变量还没指向对象就调用方法在使用引用前先判断是否为 null8. 小工具推荐学习阶段让效率翻倍的组合方案8.1 用 jshell 快速验证代码别每次都开新工程JDK 9 开始自带一个交互式工具 jshell简直是小白的福利。以前你想验证一个语法得建文件、写类、写 main、编译、运行五步操作才能看到结果。现在只要在终端里敲jshell就能直接输入语句看输出jshell int a 10; jshell a * 2 $2 20这个工具特别适合我在上文谈到的语法点验证比如想确认自增前置后置的区别或者试试字符串拼接的效果直接在 jshell 里试一下比一遍遍开 IDEA 建类高效得多。8.2 学习阶段用 VS Code 还是 IDEA这个问题的答案分两种。如果你只是初学语法建议用一个轻量编辑器 终端编译的组合比如 VS Code 装上 Extension Pack for Java它自带代码补全、语法高亮、调试支持启动速度快内存占用小不至于让新手的注意力被工具本身带走。如果你已经能流畅写一些简单程序想走 Java 开发路线那就直接上手 IntelliJ IDEA Community Edition社区版免费它对新手的感知很好代码提示非常主动重构功能强大。我第一次用 IDEA 的时候感觉就像是学车从手动挡换到了自动挡很多琐事都不用管了。8.3 关于“javase下载账号”学习资源获取的替代路径既然刮到了这个热词我就顺带聊一下。很多人在某搜索引擎里搜 javase 下载出来的都是各种需要注册登录门槛的站点。我的建议是学习阶段完全没必要在下载上纠结OpenJDK 免费无门槛微软和 Adoptium 都有现成的编译版本下载速度也很快。所谓“javase 下载账号”其实是用某种搜索引擎找资源时遇到的注册门槛换一个渠道这个问题就不存在了。学习资料方面Oracle 官方有免费的 Java Tutorials内容非常体系化只是纯英文。中文环境的话找一些口碑好的 Java 入门书籍比到处找的所谓“内部资源”靠谱得多。技术这行最珍贵的从来不是找到一个下载链接而是系统化的学习和足够的练习量。这里我必须多说一句真心话。JavaSE 是整个 Java 生态的地基地基牢不牢直接决定你后面学 Spring、微服务能走多快。网上很多人鼓吹“Java 基础不重要直接上手框架”我见过不少被这种说法坑了的人框架照着抄能运行但一遇到报错就完全摸不着头脑因为底层 Java 基础太薄弱了。反过来基础扎实的人框架对他们来说就是一堆封装好的工具几天就能上手。我自己的经历是当年学 JavaSE 的时候对着 main 方法的签名抠了很久不放过任何一个“为什么”虽然当时感觉进度比别人慢但现在回过头看那些慢功夫全成了遮风挡雨的本钱。最后再给你一个掏心窝的建议学完这些知识点后别急着看下一个视频亲手写一个小工具比如一个计算器、一个猜数字游戏把这些语法点都用一遍比你光看两小时视频有用得多。Java 这行没有捷径但脚踏实地的每一步都会在之后的日子里加倍回馈你。