
Java基础这四个字在编程招聘JD里出现频率极高但真正能把它讲清楚、能拿着它去解决实际问题的人其实并不算多。很多人一上来就盯着Spring Boot、微服务、高并发这些热词结果面完试才发现挂掉的反而全是基础题。我见过不少转行做Java的同事上手框架很快但问一句“HashMap为什么扩容”、或者“重写equals为什么要重写hashCode”立马卡壳。这篇内容就是写给刚入门、准备面试或者在自学路上有点迷茫的朋友帮你把Java基础的东西重新理一遍把那些面试官爱问的、平时写代码容易踩的坑一次性说透。1. Java基础到底包含什么先搞清学习边界1.1 别急着冲框架基础决定你能走多远经常有人在群里问“Java基础多久能学完”“能不能跳过基础直接学Spring”我的回答一般是框架可以速成基础不能。Java基础不只是语法它的核心是让你理解这门语言的设计逻辑——为什么类是引用类型、为什么字符串不可变、为什么集合有那么多接口和实现。你后面写多线程、写JVM调优、看框架源码全都要用到这些底层认知。之前带过一个新人能很快写CRUD但遇到线上内存溢出完全不知道从哪查就是因为对JVM和集合本质没有概念。基础学扎实了很多问题其实不用靠猜。1.2 基础学习的优先级排序如果你的目标是找工作建议按下面这个顺序推进这个顺序也是大多数面试题考察的密度排序语法基础变量、数据类型、运算符、流程控制、数组、方法重载面向对象类与对象、封装继承多态、接口与抽象类、static和final常用APIString、StringBuilder、包装类、日期时间、Math集合框架List、Set、Map的体系结构、底层原理、遍历方式异常处理与IO运行时异常和受检异常、文件读写泛型与反射泛型擦除、Class对象、动态代理进阶基础很多人容易犯的毛病是贪多今天学一点集合明天看一点线程结果全是碎片。我自己更推荐用“串讲”的方式学比如你学完集合体系之后直接把ArrayList的扩容源码打开结合内存模型去理解再手动写一个简单的HashMap推导过程。这样一来知识点就是连成线的而不是散落一地的珠子。1.3 面试里的“八股文”到底在考察什么网上有一堆“Java基础面试题大全”被很多人叫做八股文。但我观察下来真正经典的题目不是靠背的比如“String和StringBuilder的区别”如果你能讲到字符串常量池、不可变性、拼接性能损耗甚至可以讲出JDK9之后的Compact Strings优化面试官基本就能判断出你的深度。反过来只会说“String不可变StringBuilder可变”那和没学过差不多。所以面对基础题千万别试图用死记硬背代替理解你理解的深度面试官多追问两次就能试出来。2. 语法细节里最容易忽略的硬知识2.1 字符串拼接为什么循环里别用String加号先看一个最常见的反例String s ; for (int i 0; i 10000; i) { s i; }这段代码每执行一次s i都会创建一个新的String对象因为String对象是不可变的原来的字符串和拼接结果会被一起废弃。虽然JVM有字符串常量池和编译器优化但循环里的拼接很难被优化成StringBuilder于是产生大量临时对象性能和内存占用都会变差。有经验的开发者会这样写StringBuilder sb new StringBuilder(); for (int i 0; i 10000; i) { sb.append(i); } String result sb.toString();StringBuilder本质是一个可变的字符序列append操作就是在内部数组上追加内容不产生额外对象。再补充一个细节StringBuilder不是线程安全的单线程场景下用它的性能反而比StringBuffer高因为StringBuffer每个方法都加了synchronized有锁开销。如果只在单线程里拼接字符串优先用StringBuilder多线程环境里要安全拼接才考虑StringBuffer或者直接用线程安全的容器。2.2 equals和hashCode为什么总是成对出现这是Java基础面试里出现频率最高的一对组合。Object类默认的equals是地址比较也就是两个引用是否指向同一个对象。但业务上我们通常需要内容比较比如两个User对象只要id相同就认为相等。于是你会重写equals但如果你只重写equals不重写hashCode就会踩坑。举个例子HashMap的put和get都依赖key的hashCode定位桶位置。如果你把两个逻辑等价的User对象放进HashMap一个放入时计算的hash值是A另一个查找时计算的hash值是B它们在桶数组里可能落到了不同位置结果自然查不到。反过来如果只重写hashCode、不重写equalsHashMap里的散列值一致但equals又因地址不同返回false导致同一个逻辑对象被当成两个不同对象存进去内容重复。正确的约定是如果两个对象equals返回true它们的hashCode必须相等如果hashCode不等equals一定为false。所以重写equals几乎必须同时重写hashCode否则就是给自己埋雷。实际的开发里很多IDE都支持一键生成这两个方法但建议你至少手动写过一次明白里面是怎么把类的关键字段组合成散列值的。2.3 包装类的缓存范围一个容易翻车的细节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缓存机制导致的。JVM在启动时会缓存-128到127之间的Integer对象所以Integer a 127其实拿到的是缓存里的同一个对象用比较当然为true。但128超出了默认缓存范围两个引用指向的是不同的对象比较地址就变成false了。在项目里如果你一定要比较两个Integer数值别用用a.intValue() b.intValue()或者a.equals(b)。更稳妥的做法是如果你定义实体类的id类型优先选择long或Long但当使用Long时同样要注意它也有缓存只不过范围也是-128到127。这个细节很容易被忽略但面试时能准确讲出来会让对方觉得你确实踩过坑、读过源码。2.4 数组真的是对象吗Java里数组的运行时类型是一个特殊类数组变量实际上存储的是引用所以你可以写int[] arr new int[10]。在JVM里数组对象有长度字段但你无法直接通过反射修改它的长度。新手经常混淆的地方是把数组当成了基本类型实际上arr.length是属性不是方法。另外多维数组本质上还是数组的数组比如int[][] matrix new int[3][4]第一个维度必须指定第二维可以动态赋值。理解数组也是对象对理解后续的集合源码、JVM内存布局都有帮助。3. 面向对象和容器基础里的核心硬骨头3.1 面向对象三大特性的落地场景学面向对象别只看概念定义。封装是什么就是把字段用private藏起来通过public方法对外暴露行为不让外部直接改数据。继承是什么子类复用父类代码但继承关系容易造成高耦合所以设计模式里有“组合优于继承”的说法。多态是什么父类引用指向子类对象调用同一个方法时表现出不同行为这是框架和接口设计的基石。举个例子你写一个支付系统如果只用一个PayService里面用if判断支付类型后面每加一种渠道就得改这个类。但如果你定义一个Pay接口让AliPay、WeChatPay、UnionPay各自实现再配合工厂模式新接入渠道只需要新增实现类完全不用改已有代码。这个思路的基础就是对多态和接口本质的理解。你可以用这种场景来检验自己是否真的懂面向对象。3.2 ArrayList和LinkedList的选择逻辑ArrayList底层是动态数组LinkedList底层是双向链表。很多人背结论说“查询ArrayList快增删LinkedList快”但这个说法太粗糙。实际上ArrayList通过索引随机访问能达到O(1)而LinkedList只能从头或尾遍历所以随机读性能天差地别。往中间插入元素时LinkedList确实只需改指针但如果你指定了插入下标LinkedList也得先遍历找到那个位置整体复杂度还是O(n)。真正常见的业务场景大部分情况下在尾部追加数据、按索引读取ArrayList反而更合适。ArrayList扩容也是高频考点。初始容量是10当元素超过容量时会创建一个新数组容量大约是原来的1.5倍再把旧数据拷贝过去。如果你能提前预估数据量可以在构造时指定初始容量减少扩容次数。之前测试过一个批量导出的场景目标List没有指定初始大小插入百万级数据时gc明显变长改成预分配容量后性能提升不少。这个优化背后就是源码细节的功劳。3.3 HashMap基础进阶的分水岭HashMap相关的面试问题从基础到进阶少说能问出十来个。我先挑最核心的讲。JDK8之后的HashMap底层是数组加链表加红黑树。put一个键值对时先按key的hashCode做扰动运算再用结果和数组长度减一做位与算出桶下标。如果这个位置没有元素直接放进去如果有就要分情况。hash值不同但落到了同一个桶里哈希冲突就在这个桶上挂链表链表长度超过8并且数组长度达到64链表会转成红黑树降低查找复杂度数组长度不够64时先触发扩容而不是直接转树这里的“扩容”是老生常谈默认初始容量16负载因子0.75也就是当HashMap存到12个元素时就会扩容成32接着是64、128。为什么负载因子选0.75而不是1因为0.75在时间成本和空间成本之间做了一个平衡太低了浪费空间太高了容易频繁冲突查找性能下降。理解这个平衡点面试时如果被问到“为什么不是0.8”你就不会只干瞪眼了。还有一个老生常谈的坑HashMap不是线程安全的。多线程put时可能造成数据丢失JDK7时代的并发扩容还可能导致死循环JDK8已经修复了这个问题但并发场景仍然应该用ConcurrentHashMap而不是对HashMap加锁。3.4 排序从冒泡排序到Comparator排序是Java基础里最常被拿来练手的主题。冒泡排序的思路很简单每一轮把相邻元素中较大的往后移就像气泡往上浮。代码可以这么写public static void bubbleSort(int[] arr) { int n arr.length; for (int i 0; i n - 1; i) { boolean swapped false; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; swapped true; } } if (!swapped) { break; } } }这里加了一个swapped标记如果某一轮没有发生交换说明数组已经有序可以提前结束这就是对经典冒泡的优化。你还可以尝试手写选择排序、插入排序重点理解时间复杂度怎么算出来的理解为什么冒泡是O(n²)。实际工程里排序基本用Collections.sort或者List.sort给自定义对象排序时要么实现Comparable接口要么传一个ComparatorListUser users ...; users.sort(Comparator.comparing(User::getAge).reversed());这种写法看起来很现代但基础不扎实的人可能看不懂方法引用。建议先从匿名内部类写Comparator再到Lambda再到方法引用每一步都搞清楚它编译后是什么样子这样面试讲排序相关的问题时你才不会被一句“你对Comparator了解多少”问住。4. 环境配置和自学路上的高频坑4.1 JDK安装与版本选择很多人零基础自学Java第一关就卡在环境变量上。这里分享下我现在Windows和Mac上都会用的方式。先到官网下载对应操作系统的JDK安装包注意选LTS版本例如JDK17或者JDK21不要盲目装最新版因为很多教程和老项目还是基于JDK8或11的环境版本不一致会产生一些莫名其妙的奇怪问题。Windows装完JDK后需要配置JAVA_HOME和Path。在系统环境变量里新增JAVA_HOMEC:\Program Files\Java\jdk-17然后在Path变量里加上%JAVA_HOME%\bin配置完打开命令行输入java -version能打印出版本信息就说明安装成功。如果提示找不到命令多半是Path没配好或者命令行窗口没重启。Mac上其实很多场景不强制配JAVA_HOME但用Homebrew安装openjdk后最好也 export 一下。4.2 很多新手看不懂的错误提示初学的时候编译报错经常是一大段英文人直接懵掉。其实不用怕常见的无非几类找不到符号变量或方法名拼错或者作用域不对类型不兼容比如把字符串直接赋给int变量空指针异常对一个null对象调用了方法遇到空指针先看报错堆栈里哪一行再检查那个对象是不是没有初始化。如果不知道怎么定位就打印日志或者DEBUG。IDE里的调试工具很有用在行号旁边打一个断点然后以Debug方式运行鼠标悬停在变量上可以看到当前值。这套调试思路前后端通用学会了之后排查问题效率翻倍。4.3 知识学了就忘怎么办人的记忆有遗忘曲线建议采用“三天一复习七天一总结”的节奏。我自己通常是把某个知识点先写成一篇很短的笔记然后在三天后不看资料的情况下试着自己复述一遍笔记内容。如果复述不出来就说明还没真正内化。另外学了面向对象再学集合时可以做个“组合练习”用List存一批对象再用Comparator进行多条件排序再用HashMap按某个字段分组一个综合小案例把几个知识点穿起来。这种组合练习比你单独刷几百道基础题更有用。5. 零基础学习路线和实操建议5.1 推荐的阶段式路线我给零基础自学的朋友推荐下面这条路线按周来规划会比较稳第1周安装JDK、IDEA学变量、数据类型、运算符、流程控制第2周方法、数组、面向对象基础类、对象、构造器第3周封装、继承、多态、接口、抽象类第4周String、StringBuilder、包装类、日期类第5周集合框架List、Set、Map、泛型初步第6周异常、IO、常用工具类第7周综合复习用基础语法做一个小命令行项目比如学生成绩管理系统第8周进入数据库和JDBC然后逐步过渡到Web开发别小看最后用基础语法做项目这一步很多人学完基础就急着上框架结果连一个用ArrayList存储、用循环菜单交互的CRUD都能卡住。这个项目虽然简单但它能逼你把面向对象设计、集合操作、异常处理全部串起来比单纯看视频有意义得多。5.2 刷题和面试准备如果想练思维可以尝试在力扣上做简单题比如两数之和、反转链表、有效的括号。这些题看着像是算法题其实能帮助理解哈希表、栈这些Java里的常用结构。注意别一上来就死磕困难题基础阶段先刷50道简单题就够了。面试前快速回顾基础我建议用思维导图自己过一遍。把“Java集合”拆成List、Set、Map三个分支然后每个集合列出底层数据结构、是否线程安全、扩容机制、适用场景。再比如“String相关问题”不可变性、常量池、拼接优化、intern方法。当你看着思维导图能流畅说出五分钟基础就算过关了。5.3 从基础到进阶之间可以尝试一个小项目如果想在基础上再往实战走一步可以试试用命令行写一个待办事项管理器要求包含以下功能用类封装任务对象字段包括id、标题、优先级、是否完成用ArrayList保存任务用HashMap实现按优先级分组用Comparator实现按优先级排序用异常处理捕获非法输入用IO把任务保存到本地文件程序启动时自动读取这个小项目能覆盖Java基础里大半的内容而且完全依赖框架来做做完后你能清楚知道哪些知识点掌握扎实、哪些还得补。我在线下带过几个新人他们做完这个项目之后就掌握得相当牢固后面学Spring的时候也更加从容。6. 学基础时务必要有的人工感知学习Java基础的过程中一定要培养一种“错题复盘”的习惯。我自己早期学的时候觉得看懂了视频就等于会了结果自己上手写一个类加载顺序的题输出完全不对。后来每次遇到一个让人困惑的点就会把代码跑到IDE里加断点一步步看执行顺序比如static初始化和构造方法的先后顺序、继承时父类和子类的初始化顺序。这些东西看十遍不如跑一遍。另一个建议是试着给别人讲题。不管是写博客还是在群里回答别人问题只要你尝试把“为什么”说清楚你就会被问到各种自己没想过的地方。经过几次这种追问模糊的认知就会变得非常清晰。比如你给别人讲String不可变别人问“那String的值到底存在哪”你就得去翻常量池和堆的关系。这种时候学到的知识才是自己的。如果你正在准备面试愿你多花时间在基础逻辑上而不是背一堆自己都解释不清的术语。基础是一个磨刀的过程刀磨快了后面的框架、项目、性能优化才会好用。只要把时间花在理解上这条路真的越走越宽。