1. 第二天到底该学什么:先别急着写代码,把地基打牢
今天是我自学 Java 的第二天,说实话,第一天我差点被环境配置劝退。装 JDK、配环境变量、跑第一个 HelloWorld,每一步都像在踩雷。但今天回头看,这几个小时花得非常值。第二天我没有急着去写什么"管理系统"或"爬虫脚本",而是老老实实把 Java 的基础语法和背后的运行逻辑捋了一遍。这个决定看起来很慢,但对后面学面向对象、集合、并发这些硬核内容,作用巨大。
我给自己定的 Day2 目标很明确:搞定 JDK/JRE/JVM 的区别,彻底弄懂八种基本数据类型和 String,把流程控制、循环、方法这些"肌肉记忆"练出来,最后用冒泡排序和 switch-case 做一次实战演练。如果你也跟着网上的学习路线走,大概率会发现 Day2 的内容就是"数据类型、运算符、流程控制"这一坨。别嫌枯燥,这一坨是后面所有代码的地基。
1.1 我的Day2路线:环境配置、数据类型、流程控制
先说环境。很多新手第一天装环境是直接下载最新版 JDK,结果配完发现java -version显示的不是自己装的版本,或者 IDE 里编译报错"源发行版 17 需要目标发行版 17"——这些坑我后面会专门讲。我的建议是:如果你不是为了研究新特性,别一上来就装最新的 JDK 17 或 21,装一个企业里用的最多的 JDK 8 或者 JDK 11,配合 IDEA 或者 Eclipse,先跑通再说。因为很多教程、面试题、开源项目都是基于 JDK 8 的,等你把基础语法吃透了,再升级版本也不迟。
1.2 为什么Day2要卡在基础上,而不是直接怼项目
我知道很多人学 Java 的终极目标是做项目、进大厂,所以恨不得第一天就写一个"学生管理系统"。但你回想一下,你看那些 Java 面试八股文,什么 HashMap 原理、AQS 锁、动态代理、数据一致性……这些东西,哪一个不是建立在扎实的基础语法之上的?如果你连int和Integer的区别都说不清楚,就算背会了八股文,面试官一追问就露馅。
而且 Java 和 Python 这类动态语言不太一样,它是强类型语言,每个变量、每个方法都要声明类型。这就意味着,你对类型的理解直接决定了代码能不能跑起来。Day2 把数据类型搞明白,后面写 CRUD、写算法题才不会频繁被编译错误打断。
1.3 一个关键点:搞懂JDK、JRE、JVM的区别
这是 Java 新人最容易混淆的知识点,也是面试高频题。我用一句话总结:JVM 是虚拟机,负责把字节码翻译成机器码运行;JRE 是运行环境,包含 JVM 和核心类库;JDK 是开发工具包,包含 JRE 和编译器、调试器等。你写代码需要 JDK,别人运行你的程序只需要 JRE。这个区别搞不清楚,你连环境变量配置都会一脸懵。
我记得自己第一次配置 JAVA_HOME 时,网上教程说"配置到 JDK 的安装目录",我直接配到了C:\Program Files\Java\jdk-17下面,然后把%JAVA_HOME%\bin加进 PATH。结果命令行里javac能用,java也能用,但 IDE 里运行总是报"找不到主类"。后来发现是 IDEA 里 Project Structure 没有把 JDK 版本指向正确路径,或者模块编译级别设错了。这种问题在 Day2 遇见是好事,越早踩坑越早长记性。
2. 数据类型与变量:Java的"盒子"逻辑
2.1 八种基本数据类型与默认值
Java 的基本数据类型一共八种,我先背口诀:整型有 byte、short、int、long,浮点型有 float、double,字符型是 char,布尔型是 boolean。每种类型占的字节数不一样,取值范围也不一样。面试里常考的是long类型赋值时要在数字后面加 L,float要加 F,不然会默认当 int 或 double 处理,编译直接报错。
我建议你把每个类型的默认值记一下,因为类的成员变量如果没赋值,它会有一个默认值。比如int默认 0,boolean默认 false,char默认'\u0000',引用类型默认 null。这个在面试题里经常以"下列代码输出什么"的形式出现。我做了个小表格,方便你直接背:
| 类型 | 字节数 | 默认值 | 取值范围举例 |
|---|---|---|---|
| byte | 1 | 0 | -128 ~ 127 |
| short | 2 | 0 | -32768 ~ 32767 |
| int | 4 | 0 | 约 -21亿 ~ 21亿 |
| long | 8 | 0L | 很大,记得加 L |
| float | 4 | 0.0f | 记得加 f |
| double | 8 | 0.0d | 默认浮点类型 |
| char | 2 | '\u0000' | 0 ~ 65535 |
| boolean | 1位(理论) | false | true / false |
2.2 引用类型与String的不变性
基本类型之外都是引用类型,包括类、接口、数组、枚举。引用类型本质上存的是"地址",指向堆内存里的对象。这里最经典的就是String。String不是基本类型,它是一个类,而且它是不可变的。每次对 String 的修改,实际上都是新建一个对象,然后把引用指过去。
比如你写:
String s = "Hello"; s = s + " World";表面上 s 变了,实际上原来的 "Hello" 字符串对象还在内存里,只是 s 指向了新的 "Hello World" 对象。所以如果频繁拼接字符串,不要用+,要用StringBuilder或StringBuffer。这是热词里很多人搜"java stringbuilder"的原因,也是面试必问的 String 性能问题。
2.3 包装类与自动装箱拆箱的坑
基本类型都有对应的包装类:Integer、Long、Double、Boolean等。Java 会自动装箱和拆箱,但这里有很多隐藏的坑。最经典的例子是:
Integer a = 127; Integer b = 127; System.out.println(a == b); // true Integer c = 128; Integer d = 128; System.out.println(c == d); // false为什么?因为Integer内部有一个缓存池,范围是 -128 到 127。在这个范围内,valueOf会直接返回缓存对象,所以==比较的是地址,返回 true;超过范围就会 new 新对象,地址不同,返回 false。所以以后比较两个Integer的数值,一定要用equals()或者拆箱后比较,千万别用==。
类似的坑还有Integer参与算术运算时自动拆箱,如果它是 null,就会抛出空指针异常。比如:
Integer num = null; int result = num + 1; // NullPointerException这个错误在开发里太常见了。你的代码里如果从数据库查出来一个 null 的 Integer,没判空就直接参与计算,直接崩。Day2 就把这个坑记下来,能帮你省下很多 debug 的时间。
3. 运算符和流程控制:代码的"红绿灯"
3.1 短路与、短路或的实际坑
Java 的&&和||是短路运算符,也就是说,左边能决定结果时,右边就不会再执行了。举个例子:
if (name != null && name.length() > 0) { System.out.println("name 非空"); }如果 name 是 null,短路与会让左边条件为 false,右边name.length()不会执行,因此不会抛空指针。如果你不小心用了&,那就完蛋了,两边都会执行,null 直接炸。同理,||和|也有这个区别。我在面试题里经常看到这个考点,但真正写代码时,很多人会习惯性地用&&,却说不清为什么安全。其实这就是短路带来的保护。
还有一个小细节:&和|在整数位运算中也有用,比如按位与、按位或,那个跟逻辑短路没关系。新手别搞混。
3.2 switch-case的空数据问题
热词里有个"java switch 空数据",我猜是有人写 switch 时遇到空指针或者 case 穿透了。Java 的 switch 支持 int、char、String、枚举等类型,但从 Java 某个版本开始支持 String。这里有一个常见坑:如果 switch 的判断变量是 String,而这个变量是 null,直接调用hashCode()会空指针吗?其实不会,switch 内部会先判空,如果为 null 会抛 NullPointerException。对,你没看错,switch 里 String 为 null 会抛 NPE,所以使用前必须判空。
再说 case 穿透。如果你在 case 块末尾不加break,程序会继续执行下一个 case 的代码。有时候这是故意设计的,比如多个 case 走同一个逻辑;但大多数时候这是 bug。我建议 Day2 就养成习惯:每个 case 后面要么 break,要么 return,要么注释说明你故意不 break。
另外一个关于 switch 的冷知识:JDK 14 开始引入了 switch 表达式,可以省略 break,用->替代。写法更简洁:
switch (day) { case "MON", "FRI" -> System.out.println("工作日"); case "SAT", "SUN" -> System.out.println("周末"); default -> System.out.println("其他"); }不过很多公司还在用 JDK 8,所以传统写法也得会。
3.3 for循环与冒泡排序手写
流程控制里最实用的就是 for 循环,而 for 循环的经典练习就是排序。冒泡排序是面试里最爱考的基础算法,也是 Day2 最好的练手题目。原理很简单:每轮从头到尾比较相邻元素,如果顺序不对就交换,最大的数会像气泡一样浮到末尾。重复 n-1 轮。
我手写了一个标准实现:
public class BubbleSort { public static void main(String[] args) { int[] arr = {5, 2, 9, 1, 7}; for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } for (int num : arr) { System.out.print(num + " "); } } }注意内层循环的判断条件arr.length - 1 - i,很多新手会写错。每轮排序后,最大的元素已经归位,所以内层比较的次数要减去已经排好的轮数 i。如果不减,代码也没错,就是多做无用功。面试时能写出加这个减法的版本,会显得你理解更到位。
冒泡排序的时间复杂度是 O(n²),空间复杂度 O(1)。Day2 不要求你优化成快排,但起码要能手写并解释清楚过程。我在学习的时候,还会在纸上画出每轮排序后的数组变化,这样印象更深。
4. 方法与面向对象初探:从"做事情"到"对象协作"
4.1 方法重载与方法重写的区别
Day2 如果只讲语法,不讲面向对象,会有点亏。因为 Java 是面向对象语言,方法的设计和类的设计是连在一起的。先搞清楚两个容易混淆的概念:重载(Overload)和重写(Override)。
重载是同一个类里,方法名相同、参数列表不同(类型、个数、顺序),和返回值无关。比如一个add方法,可以接收两个 int,也可以接收三个 int,还可以接收两个 double。编译时通过参数的静态类型来决定调用哪个版本。
重写是子类继承父类后,对父类方法的重新实现。要求方法名、参数列表、返回值类型必须完全一致(返回值可以是子类),不能缩小访问权限。Day2 你只要知道概念就行,真正写重写是后面学继承的时候。
有一个面试高频题是:"重载和重写的区别是什么?"标准回答里要提到编译时和运行时的区别,重载是编译期多态,重写是运行期多态。不过现在你先记住定义,后面我会专门写一篇深入讲多态。
4.2 类与对象的关系:用"模具和零件"来理解
面向对象最核心的概念就是类(Class)和对象(Object)。我打个比方:类就是模具,对象就是模具造出来的零件。模具定义了零件有哪些属性(比如螺丝有直径、长度)和功能(比如拧紧),同一个模具可以造出无数个零件,每个零件的属性值可以不同。Java 里,new关键字就是开模的动作。
代码表示:
public class Student { String name; int age; public void study() { System.out.println(name + "正在学习Java"); } }然后创建对象:
Student s1 = new Student(); s1.name = "张三"; s1.age = 20; s1.study();你可能会想,这不就是一个结构体加函数吗?但类和对象的意义在于:它把数据和操作数据的方法封装在一起,这为后面的封装、继承、多态打下了基础。当你以后理解"对象间协作"时,整个程序就不再是一堆函数堆在一起,而是多个对象互相发消息。
4.3 构造器与this关键字
构造器(构造方法)是创建对象时调用的特殊方法,名字必须和类名相同,没有返回值。如果你不写,Java 编译器会自动生成一个无参构造器;如果你写了任何构造器,编译器就不会再自动生成无参构造器了。这个细节是很多 bug 的来源。比如你定义了一个有参构造器Student(String name),却忘了写无参构造器,然后在别人的代码里调用了new Student(),直接编译报错。
this关键字是用来区分成员变量和局部变量的。最常见的使用场景是构造器中参数名和成员变量名相同:
public Student(String name, int age) { this.name = name; this.age = age; }这里的this.name表示当前对象的成员变量,等号右边的name是参数。搞清楚这个,你以后读 Spring 源码、看 MyBatis 的时候,就不会因为this遍地都是而看不懂了。
5. 常见问题与排查技巧实录(避坑)
5.1 环境变量配置后java -version还是老版本
这个问题很经典,我在 Day1 就遇到了。明明装了 JDK 17,配置了 JAVA_HOME 和 PATH,但执行java -version显示的是 JDK 8。大概率是你的 PATH 里有一个更靠前的 Java 路径。Windows 的 PATH 变量是从前到后匹配的,如果前面有一个别的 Java 目录,它就会被优先找到。
解决办法:检查echo %PATH%,看看 java 可执行文件的路径到底来自哪里。比如如果答案来自C:\Program Files\Common Files\Oracle\Java\javapath,那这里通常是安装 Oracle JDK 时自动加入的,优先级很高。你需要在 PATH 里把%JAVA_HOME%\bin移到最前面,或者直接把javapath删掉。我建议是删掉javapath,因为它指向的可能是旧版本,而且你用 IDE 时 IDE 不依赖这个 PATH。
5.2 "源发行版 17 需要目标发行版 17"怎么办
这个报错几乎每个用 IDEA 的新手都会遇到。本质是项目的编译级别和 JDK 版本不匹配。比如你当前模块的 Java 编译器版本设成了 17,但项目构建版本是 1.8,就会提示"源发行版 17 需要目标发行版 17"。
解决方法有几种:
- 在 IDEA 中按
Ctrl + Alt + Shift + S打开 Project Structure,确认 Project SDK 是你要用的 JDK 版本,Project language level 和 Modules 的 language level 保持一致。 - 如果是 Maven 项目,去
pom.xml里检查<properties>中的maven.compiler.source和maven.compiler.target,统一改成1.8或17。 - 也可以直接检查
File -> Settings -> Build, Execution, Deployment -> Compiler -> Java Compiler,勾选 "Use --release" 并设置版本。
这个问题比环境变量更隐蔽,因为 IDEA 里 IntelliJ 的项目没设置好,命令行编译可能又没问题。所以排查时一定四个地方都看:Project SDK、Module language level、Maven compiler 配置、IDE 的 Java Compiler。
5.3 卸载Java提示程序包有问题
热词里有人搜"java卸载时提示程序包有问题",我猜是 Windows 控制面板卸载时弹出错误。这个通常是因为旧版 Java 的安装注册表损坏,或者杀毒软件干扰。如果你也遇到,别硬刚,直接用工具清注册表,或者去 Oracle 官网下载对应版本的卸载程序。更省事的方法是:先重启电脑,再卸载;还不行就以管理员身份运行卸载命令。
还有一个常见情况:你装了多个 JDK 版本,有的软件还在用,卸载时报"此程序包已被使用"。这时候要记住:不要去C:\Program Files\Java里手动删除文件夹,因为注册表里还有记录,后面重装时很容易出问题。最好用专业的卸载工具(比如 Geek Uninstaller)清理。
5.4 StringBuilder与String的拼接性能
我前面说过 String 是不可变的,所以大量拼接时性能很差。现在很多人在学了 StringBuilder 后,又听说 StringBuilder 是线程不安全的,StringBuffer 是线程安全的,于是纠结怎么选。其实在单线程环境下,比如临时拼接 SQL、拼接日志,直接用 StringBuilder 就够了。只有在多线程共享同一个可变字符串时,才需要用 StringBuffer 或者加锁。Day2 先把 StringBuilder 会用就行了。
一个常见的面试题:"用+拼接字符串和用 StringBuilder 拼接有什么区别?"其实在 JDK 8 以上,编译器会对简单情况下的+做优化,自动转成 StringBuilder。但是如果你在循环里拼接,编译器可能每次循环都 new 一个 StringBuilder,性能反而更差。所以大循环拼接一定要手动用 StringBuilder。
5.5 学习Java常见的心态问题
学 Java 到第二天最容易出现的错觉是:"我今天学了八种数据类型、写了个冒泡排序,感觉已经入门了。"但第二天也是弃坑高发期,因为你会发现后面还有一大堆东西:集合、IO、多线程、网络编程、JVM……好像永远学不完。我的经验是:不要试图一口吃成一个胖子,把 Day2 之前的内容吃透,做点小练习,比刷完一整本书更重要。
我给自己定的规矩是:每学一个知识点,都要在 IDE 里写一遍。就算写完了报错也没关系,报错越多,说明你越早发现了自己的理解漏洞。像空指针异常、数组越界异常,这些 Day2 就能碰到,碰到一次记一辈子。
6. Day2的作业与下一步计划
6.1 我留的作业:手写一个简易学生管理系统(控制台版)
很多教程会让你做学生管理系统,因为它是经典的 CRUD 练习。Day2 我已经学了变量、数据类型、流程控制、数组、方法,还抽空看了类与对象。所以我的作业是:用数组存储学生姓名和成绩,支持添加、查询、显示所有学生,暂时用控制台交互,不涉及数据库。
这个作业的难点有三个:
- 数组长度固定,如何实现动态扩容?如果你用了 ArrayList,其实就已经提前接触到集合了。这也说明,你很快会从"数组"走向"集合"。
- 如何将学生信息封装成对象?明天学完面向对象后,你可能会把二维数组改成
Student[],这就是从面向过程向面向对象跨越的一步。 - 循环退出和用户输入的边界情况怎么处理?比如输入非法选项时,程序不能直接崩溃。
我建议你今天就做这个作业,哪怕只做出一个用二维数组保存姓名和成绩的版本。等你明天学了封装之后,再回来重构,对面向对象的好处体会会非常深。
6.2 下一步:面向对象三大特性与集合框架
按照我给自己规划的路线,Day3 要集中学封装、继承、多态,并了解this、super、final关键字;Day4 学集合框架,重点弄懂 ArrayList、LinkedList、HashMap 三者的底层结构和区别;Day5 开始接触异常处理和 IO 流。这条路线是我结合自己踩过的坑和网上很多人分享的 Java 学习路线图总结的。
如果你对"面向对象编程 java"这个热搜词感兴趣,其实它对应的就是封装继承多态。学的时候别光看概念,要动手写:写一个父类 Animal,一个子类 Dog,重写父类的eat()方法,然后利用向上转型调用。等你亲手试过一遍,才能体会"多态"的威力。
另外,热词里还有不少看起来很高大上的东西,比如"aqs java"、"java动态代理"、"java对象深度拷贝"、"java怎么保证数据一致性"。这些内容说实话,Day2 阶段去啃完全是浪费时间。它们属于并发、Spring AOP、分布式系统这些领域,你现在只需要知道这些名词意味着 Java 有很深的生态,等基础扎实后再循序渐进地学。如果你现在就好奇,可以先去搜一搜它们的入门解释,但别指望看懂源码——源码里的恐怖细节,等你学了 JVM 和并发再看才会不虚。
7. 写在最后:我的个人体会
说实话,第二天的学习效率并不高,我花了差不多三个小时才把数据类型的坑、switch 的穿透、冒泡排序的细节彻底想明白。但我觉得这是正常的。学编程不是看谁学得快,而是看谁基础扎得实。那些在 Java 面试八股文里游刃有余的人,绝对不是靠死记硬背,而是像这样一天一天把每个基础点过了一遍。
我给自己留了一个小技巧:把今天遇到的所有报错信息截图,存到手机备忘录里,晚上统一整理。今天我就记录了五个报错:"非法的表达式开始"、"无法从静态上下文中引用非静态方法"、"'}' 应为"、"源发行版17需要目标发行版17"、空指针异常。这些问题看着是小事,但把它们搞懂,就等于给未来的自己排掉了一堆雷。
如果你也在学 Java,欢迎按照我分享的路线走一遍。不用赶进度,把每个代码都敲一遍,把每个坑都记下来。Day2 的学习心得到这里,下一天我会分享面向对象的实战心得,到时候咱们再聊。