
做Java开发这些年带过不少新人也当过多轮技术面试官我越来越发现一个现象很多人简历上写着“熟练掌握JavaSE”但一写代码或者一被追问就露馅。JavaSE虽然听起来基础却是整个Java技术栈的底座——无论是后来的JavaEE、Spring生态、Android开发还是大数据方向底层跑的都是JavaSE这套东西。对象、集合、IO、多线程、异常处理这些基础不扎实后面学什么框架都像是在沙地上盖楼越盖越心虚。这篇“JavaSE大总结礼包”就是给想系统过一遍JavaSE的人准备的。不管你是刚入门想理清学习主线的学生还是要面试了想快速梳理考点的新人或者是工作中发现基础不牢准备回炉的初级工程师这篇内容都适合你。我会按我平时带人用的知识体系把核心知识点、底层原理、高频考点一次性串起来顺便把多年实践里踩过的坑和好用的技巧一并放进来。建议收藏起来当作一份复习索引用别看完一遍就丢。1. JavaSE的完整知识版图先搞懂学什么再动手1.1 JavaSE在整个技术栈里到底扮演什么角色很多初学者有个困惑JavaSE、JavaEE、Spring Boot、微服务到底先学哪个、它们是什么关系我打个比方JavaSE相当于一门语言的语法和词汇是你表达逻辑的基本工具JavaEE和Spring这些框架是别人写好的“文章模板”帮助你更快地组织段落而微服务、分布式这些都是“长篇小说的写作技法”。你可以硬背模板但不懂语法的人一旦模板不匹配场景立刻就没法改写了。这也是为什么所有正规的Java学习路线都把JavaSE放在第一步。Spring的核心是IoC和AOP底层用到了反射和动态代理MyBatis和Hibernate要把数据库记录映射成Java对象底层是集合框架和反射Netty这类高性能通信框架直接构建在Java NIO之上。可以这么说JavaSE的功底决定了你后续学框架是真正“看懂”了还是只是“看个表面”。在实际工作中这种“地基”效应体现得更直接。我见过不少开发同学框架用得很溜但遇到线上OOM或者接口响应慢的问题时完全不知道从哪入手。真正能定位问题的人往往是从JVM内存模型、集合底层、GC机制这些JavaSE层面的知识开始分析的。可以说JavaSE决定了你解决问题的天花板。1.2 六个板块构建你的JavaSE知识大盘要给JavaSE画一张地图我建议把它拆成六个板块对应六个递进的学习阶段板块核心内容学完能做什么语法基础数据类型、运算符、流程控制、数组能写简单可运行的代码面向对象类与对象、封装继承多态、接口、内部类能用对象思维建模核心APIString、包装类、日期时间、Lambda与Stream能熟练使用JDK自带工具集合框架List、Set、Map、Queue及底层原理能正确选型并优化性能IO与网络字节流字符流、NIO、Socket编程能处理文件读写与网络通信多线程与JVM线程、锁、并发工具、内存区域、GC能编写高性能并发代码这六个板块相互关联、层层递进。语法和面向对象阶段是“能写”核心API和集合阶段是“会用对的工具”IO和多线程阶段则是“写出扛得住业务压力的代码”。学习时按这个顺序推进不要跳级。我见过太多人先学Spring再说“回头补基础”结果回头补的时候已经没耐心了基础就一直缺在那儿。有人可能会问JVM不是JavaSE之外的内容吗严格来说JVM属于Java平台的一部分但学习JavaSE时无论如何都绕不开内存区域、类加载、GC这些基本概念。所以我把“多线程与JVM基础”放在同一个板块里先建立整体认知后面再单独深入也不迟。2. 基础语法与面向对象看似简单却最见功底2.1 语法细节里的三个高频大坑基础语法这部分我不讲“什么是变量”这种最基础的内容直接讲几个我这些年见人踩过最多的坑。第一个是整数除法。int a 5 / 2得到的是2不是2.5。新手做金额计算时特别容易翻车而且这个错误非常隐蔽编译器不报警运行时不报错结果就是不对劲。解决办法很简单把其中一个操作数转成double比如5 / (double)2或者用5 / 2.0这种写法。第二个是String的与equals之争。这是Java面试出场率最高的问题。比较的是引用地址equals比较的是内容。但由于String常量池的存在String a hello和String b hello时a b会返回true。很多人在这个现象面前直接懵了以为可以比较字符串结果换成new String(hello)再比较又变成false。我的建议很朴素比较字符串内容永远用equals不要有任何侥幸心理。第三个是switch的穿透。case后面不写break代码会继续执行下一个case。我以前就因此漏写break线上业务数据错乱排查了大半天才定位到。现在Java 14以后引入的新式switch表达式用箭头语法天然不会穿透我建议新代码直接用新写法老代码里则强制给每个case写break并且加注释提醒。2.2 数组与引用类型在内存中的存储逻辑数组是新手很容易忽略的知识点。int[] arr new int[5]这行代码背后发生了两件事在堆内存中创建了一个长度为5的数组对象在栈内存中的变量arr保存了这个数组对象的引用。很多人以为arr就是数组本身其实不是。二维数组也一样int[][] matrix new int[3][4]在内存中是这样的结构matrix指向一个长度为3的数组这个数组的每个元素又分别指向一个长度为4的int数组。所以严格来说Java的二维数组是“数组的数组”不是连续的内存块。这也解释了为什么Java可以定义“不规则二维数组”比如第一行3个元素、第二行5个元素这在一些语言里是做不到的。理解数组在内存中的存储方式对后面理解集合、理解参数传递很有帮助。Java的参数传递只有一种——按值传递。基本类型传的是值的副本引用类型传的是引用的副本。所以方法里修改引用指向的对象属性方法外能看到变化但方法里让引用指向新对象方法外不受影响。这个区分也是面试常考的。2.3 面向对象三大特性在实战中的正确理解封装、继承、多态教科书里讲了很多我更愿意从工程角度讲。封装的本质是“隐藏内部细节、暴露稳定接口”。设计类的时候先问自己外部使用者真正需要什么不需要的字段和方法全部收起来。业务逻辑复杂以后封装好不好直接决定后续维护成本。我常用一个判断标准谁的代码里出现大量getter/setter调用、变量手动拼接业务规则谁的封装大概率没做好。继承是代码复用的手段但一定要慎用。我刚工作时喜欢建一个超级基类塞公共方法结果后期需求一改基类变更影响所有子类一次改动要全量回归苦不堪言。现在我坚持一个原则优先组合慎用继承。只有子类和父类存在明确的“is-a”关系时才用继承否则把一个类作为成员变量组合进来更灵活、更安全。多态的核心是“面向接口编程而不是面向实现编程”。调用方只依赖接口定义的方法具体行为由运行时传入的实现类决定。这样后续要替换实现或新增实现调用方代码一行都不用改。这就是开闭原则最直观的落地。还有一个高频考点是内部类。匿名内部类配合Lambda在处理事件、回调逻辑时很常用。这里有一个必考的规则匿名内部类或Lambda表达式访问外部局部变量时这个变量必须是final或者effectively final赋值后不再修改否则编译不过。这个点经常出现在笔试题里遇到时多留个心眼。3. 核心API与Java 8新特性日常开发的主力工具3.1 String、StringBuilder和包装类的细节String这个类Java程序员每天都在用但很多细节值得深挖。先说不可变性。String被final修饰底层的char数组也是final的任何看起来“修改String”的操作实际都是创建新对象。这样做的好处是字符串可以安全地被多线程共享也能支持常量池复用。坏处是循环里拼接字符串会产生大量中间对象所以Java提供了StringBuilder和StringBuffer。单线程环境用StringBuilder速度最快多线程环境一般也不用StringBuffer它每个方法都加synchronized性能太差而是自己加锁或用其他方案。再说字符串常量池。代码里直接写的字面量“hello”会进入常量池遇到相同内容时直接复用而new String(hello)在堆里额外创建新对象。字符串拼接时“a” “b”如果两边都是编译期常量编译器会直接优化成“ab”如果其中一个是变量运行期就会走StringBuilder拼接。这个细节经常出现在笔试题里。包装类同样有坑。Integer a 127; Integer b 127a b返回true因为-128到127之间有缓存。一旦写成128同样的代码就返回false了。很多人在这个坑里栽过。比较包装类数值永远用equals或者先拆箱成基本类型再用。3.2 Lambda与Stream从会用到底层理解Java 8的Lambda和Stream彻底改变了写集合处理的姿势。比如要筛选出年龄大于20的人并收集成新列表传统写法要for循环加if判断写好几行用Stream一行搞定ListPerson adults persons.stream() .filter(p - p.getAge() 20) .collect(Collectors.toList());Stream的核心是“流水线思维”先用stream()开启流水线接着用filter、map、sorted这类中间操作处理最后用collect、forEach、count这类终止操作收尾。中间操作是惰性的只有遇到终止操作时才真正执行整条流水线。理解这一点才能明白为什么中间操作不写时毫无效果、也不会报错。使用Stream还有几个很容易被忽略的坑。Stream是一次性的一个流只能有一个终止操作执行完就关闭了。并行流parallelStream虽然能利用多核加速但数据量小时线程切换成本可能超过收益存在共享可变状态时还容易引入线程安全问题。我的建议是先用串行流把逻辑写对再考虑要不要并行而且一定要有压测数据支撑。Lambda表达式也不能只停在“会用”层面。它依赖函数式接口——只包含一个抽象方法的接口比如Runnable、Comparator以及java.util.function包里的Function、Predicate、Consumer。理解函数式接口后再看框架里那些“自动帮你生成实现类”的写法底层逻辑就清楚了。3.3 日期时间API别再被SimpleDateFormat坑了Java 8之前处理日期时间主要用java.util.Date和SimpleDateFormat这两个类说实话设计得挺糟心的。Date的很多方法都已经废弃SimpleDateFormat不是线程安全的在多线程环境下做日期格式化经常出诡异问题。Java 8引入了全新的java.time包设计上非常人性化。LocalDate表示日期LocalTime表示时间LocalDateTime表示日期时间这三个类都是不可变且线程安全的。配合DateTimeFormatter做格式化代码写起来也舒服很多LocalDateTime now LocalDateTime.now(); DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss); String text now.format(formatter);日常开发里我建议新代码一律使用java.time包别再走Date和SimpleDateFormat的老路。尤其是做时间运算的时候plusDays、minusHours这些方法语义清楚基本不会出错。4. 集合框架面试高频与日常开发的主战场4.1 集合框架的整体架构与选型表Java集合框架几乎是每个方法里都会出现的工具但很多人停留在“会用”层面面试官一问HashMap源码就卡壳。想真正理解集合先记住顶层关系所有集合类都实现了Collection或Map这两个根接口之一。Collection下面又派生List、Set、Queue三个子接口分别对应有序列表、不可重复集合、队列三种语义。这里分享一张我用了很多年的选型对照表接口常见实现底层结构特点典型场景ListArrayList动态数组查询快、增删慢读多写少的列表ListLinkedList双向链表增删快、查询慢频繁头尾插入删除SetHashSetHashMap无序、不可重复去重SetTreeSetTreeMap有序需要排序去重MapHashMap数组链表红黑树高效的键值存储绝大多数映射场景MapTreeMap红黑树键有序范围查询选集合的核心逻辑就一句话先想清楚核心操作是“读”还是“写”以及要不要保持顺序。想明白了选型就不会跑偏。4.2 HashMap从底层结构到扩容机制一次讲透HashMap是JavaSE面试的“必考题”很多人背了又忘。我把它拆成四个核心要点。第一底层结构。JDK 1.8之后HashMap底层是“数组链表红黑树”。默认初始容量16负载因子0.75。当某个桶的链表长度超过8且数组长度不小于64时链表转红黑树把最坏情况查询从O(n)降为O(logn)。第二存储流程。put一个键值对时先计算key的hashCode再用扰动函数高16位与低16位异或让哈希分布更均匀最后通过(n-1)hash计算桶的位置。如果桶里已经有元素用equals判断key是否相同相同就覆盖不同则追加到链表或树中。第三扩容机制。当元素个数超过“容量×负载因子”即默认超过16×0.7512个时触发扩容容量翻倍。扩容后所有元素要重新计算位置开销不小。如果预估数据量创建时直接指定初始容量可以减少扩容次数。我的经验值是设置成“预估数量/0.751”再向上取整。第四线程安全。HashMap是线程不安全的多线程put甚至可能导致CPU飙升或数据错乱。并发场景请直接用ConcurrentHashMap。JDK 1.8之后的ConcurrentHashMap用CASsynchronized锁桶并发性能比老版本好很多使用起来也不复杂。4.3 ArrayList扩容细节与LinkedList选型误区ArrayList与LinkedList的对比是经典八股题。标准答案是“ArrayList查询快、增删慢LinkedList增删快、查询慢”但这个说法有个隐藏前提LinkedList的增删快是建立在已经定位到指定位置的前提下。如果你需要先遍历查找再删除前面还有O(n)的查找成本整体不一定比ArrayList快。更何况LinkedList每个节点要额外维护前后指针内存占用更大。所以日常开发里ArrayList的使用频率远高于LinkedList没有特别充分的理由就优先ArrayList。ArrayList底层是动态数组最核心的机制是扩容。每次add如果容量不够会创建新数组容量扩为原来的1.5倍再把旧数据复制过去。频繁扩容很伤性能所以能预估大小时建议直接new ArrayList(容量)把扩容次数降到最低。这也是我在代码审查时经常提的一条优化建议。5. 异常处理与IO流工程化能力的分水岭5.1 异常处理最容易犯的三个错误以及正确姿势异常处理是很多写了两三年代码的人依然处理不好的地方。我总结了三个最常见的错误。第一个是吞异常。典型写法是catch(Exception e) { e.printStackTrace(); }打印完堆栈就没事了。这样等于把错误信息吃掉了出问题时日志里只有一片堆栈没有业务场景排查极其痛苦。第二个是把打印异常当成处理。logger.error(e)本质上是“记录并忽略”。正确做法是能恢复的场景就针对性地恢复不能恢复的转换成上层能理解的业务异常并抛出。第三个是没有区分checked和unchecked异常。checked异常如IOException编译器强制处理unchecked异常如NullPointerException、IllegalArgumentException是RuntimeException子类编译器不管。业内约定是应用内部因参数或状态不对产生的异常用unchecked异常调用外部系统产生的异常用checked异常或包装后上抛。我在团队里推行的规范是Service层统一抛出带错误码的业务异常BusinessExceptionController层统一捕获并转成前端能识别的结构底层异常在边界统一包装。这样日志里能清楚看到哪个链路、哪个环节、为什么失败排查效率翻倍。另外别忘了try-with-resources。凡是实现了AutoCloseable的资源比如文件流、数据库连接直接写在try后面的括号里JVM自动关闭代码更简洁也彻底避免忘记关连接的隐患。5.2 字节流、字符流、NIO与Socket的取舍IO流基础分类不复杂字节流以InputStream/OutputStream为基类处理二进制数据字符流以Reader/Writer为基类处理文本数据自带编码转换。选型原则就一条图片、音频、视频用字节流文本用字符流可以有效避开中文乱码。实际开发里读文件最常用Files.readAllLines一次把整个文件按行读成List。虽然大文件时内存紧张但日常配置和中小数据量场景完全够用。我贴一段常用的写法ListString lines Files.readAllLines( Paths.get(/path/to/file.txt), StandardCharsets.UTF_8 );至于NIO它的核心思想是把传统“一个线程阻塞在一个流上”的模式改成“一个线程通过Selector同时监听多个Channel的事件”。这样少量线程就能服务大量连接是高性能网络编程的基础。但对大多数业务开发者来说平时真正接触NIO的机会不多中间件已经封装好了。我的建议是基础IO必须熟练NIO理解非阻塞和Buffer、Channel、Selector三大组件的分工即可不必死磕源码。网络编程这块JavaSE里的基础就是Socket和ServerSocket。一个简单的TCP服务端大体是创建ServerSocket、绑定端口、accept等待客户端连接、然后用IO流读写数据。虽然现在几乎没有业务系统直接裸写Socket了但理解这个模型对于之后学习Netty、WebSocket以及各种RPC框架都很有帮助。我给初学者的建议是能手写一个“客户端-服务端”的简单聊天程序就够了不需要深入高并发。6. 多线程与并发真正拉开差距的硬骨头6.1 创建线程的四种方式以及为什么工程里只用线程池多线程是JavaSE里最绕也最值钱的部分面试几乎必考。先看线程创建的四种方式继承Thread类重写run方法实现Runnable接口重写run方法实现Callable接口配合FutureTask获取返回结果通过ExecutorService线程池提交任务前三种是“手工造线程”工程里几乎不推荐。因为线程创建和销毁开销大无节制地创建线程会导致CPU上下文切换频繁甚至把内存耗尽。线程池的核心价值是线程复用和总量控制把线程当作宝贵资源来管理。我一直和新人说学多线程不只是学语法更要学“资源管理”的思维。线程的生命周期有六种状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED这六个名称要背下来。面试最爱考的是状态转换条件比如sleep和wait的区别sleep不释放锁wait会释放锁sleep是Thread的静态方法wait是Object的方法。这两个区别也是实际编码中最容易踩坑的地方。6.2 synchronized与Lock锁的选择与避坑线程安全的核心矛盾是多个线程同时操作共享的可变数据解决思路不外乎三种加锁、用不可变对象、用ThreadLocal做线程隔离。其中加锁最通用也最容易出问题。synchronized是JVM内置锁最大优点是不用手动释放锁代码再乱也不会锁泄漏。它可以修饰方法、代码块、静态方法锁定的对象分别是当前实例、括号内指定对象、当前类的Class对象。我遵循的设计原则是尽量锁代码块而不是整个方法锁的粒度越细并发度越高。Lock是JDK层面的接口ReentrantLock是典型实现。它比synchronized多了几项能力支持公平锁、支持超时尝试获取锁tryLock、支持多个Condition条件变量。但代价是必须手动加锁和释放锁通常配合try-finally模式否则异常跳出就会锁泄漏。我的经验是简单同步场景优先synchronized需要超时控制、公平性保证时再上ReentrantLock不要为了用高级工具而用。6.3 并发工具类与线程池参数速览java.util.concurrent包里有一批非常实用的并发工具我按使用频率排序ConcurrentHashMap线程安全的HashMap并发场景首选BlockingQueue阻塞队列生产者-消费者模型最佳搭档CountDownLatch让一个或多个线程等待其他线程完成Semaphore信号量控制同时访问某个资源的线程数CyclicBarrier让多个线程互相等待齐了再出发线程池这块最核心的就是理解ThreadPoolExecutor的七个参数参数含义经验取值corePoolSize核心线程数CPU密集≈CPU核数1IO密集≈CPU核数×2maximumPoolSize最大线程数通常为核心线程数的2倍左右需压测keepAliveTime非核心线程空闲存活时间常用60秒workQueue任务队列有界队列优先避免OOMthreadFactory线程工厂建议自定义命名方便排查handler拒绝策略常用CallerRunsPolicy或自定义笔试里的“标准答案”只是一个起点生产环境参数一定要结合业务压测调整。我给团队定的底线是队列必须有界拒绝策略必须明确线程名必须带上业务标识。不然出问题时线程dump打开一看全是pool-1-thread-1根本分不清是哪个业务线的线程。7. 高频考点与学习路线用这份清单完成最后的查漏补缺7.1 JavaSE高频面试题速查表下面是这些年我当面试官时最常问的JavaSE考题做成速查表建议你用它做一轮自查分类高频问题核心考点语法String、StringBuilder、StringBuffer三者区别不可变、线程安全、性能语法和equals的区别引用 vs 内容语法重载和重写的区别编译期多态 vs 运行期多态面向对象抽象类和接口的区别设计语义、多继承集合HashMap底层原理数组链表红黑树、扩容集合ArrayList扩容机制1.5倍扩容、数组复制集合ConcurrentHashMap如何保证线程安全CASsynchronized锁桶异常checked与unchecked异常的区别受检与不受检、设计意图多线程线程六种状态及转换状态机、wait/sleep区别多线程synchronized和Lock的区别内置锁 vs 显式锁多线程ThreadPoolExecutor七个参数核心数、队列、拒绝策略JVMJVM内存区域划分堆、栈、方法区、程序计数器这些问题能不看资料说清楚并且能写几行代码演示的JavaSE面试关基本就过了。如果有些问题答不清楚也不用慌按下面的路线回炉补一遍。7.2 一套亲测有效的JavaSE学习路线最后给想系统学JavaSE的朋友一个建议路线。这是我在团队里带过很多新人后总结的亲测比漫无目的地刷视频高效得多。第一步快速过语法。找一套你能接受的教学资源用一周半到两周把语法基础过一遍期间配合小练习别拖延。第二步写一个综合小项目。学生管理系统、图书借阅系统都行重点是把集合、异常、IO、面向对象这些知识点串起来用。这个阶段最忌讳只看不写代码量不够知识记不牢。第三步啃集合源码。专门花时间看HashMap、ArrayList、LinkedList的源码不用逐行读透但核心流程要能讲明白。第四步死磕多线程。多写几个Demo比如多线程下载器、生产者消费者模型、简单线程池。多线程是JavaSE里唯一必须靠“多写多调”才能理解的部分。第五步刷题加复盘。把高频面试题整理成自己的笔记每个题逼自己讲一遍讲不好就回炉重看。这一步对面试帮助巨大。补充一下我用下来觉得不错的学习资料入门阶段推荐《Head First Java》或者口碑好的入门视频适合建立整体概念进阶阶段强烈推荐《Java核心技术卷I》这本书虽然是砖头但胜在全面且准确遇到问题可以当工具书查源码研究阶段直接看JDK源码和《Effective Java》配合起来能少走很多弯路。视频适合前期跟学书和源码才是长期陪伴你的东西。JavaSE学习最大的坑就是“眼高手低”。看视频觉得全会一动手写就废。代码一定要自己敲、自己调试报错了自己排查这个过程比任何教程都值钱。我记得自己刚入行时为了搞懂线程池硬是把Demo写烂了十几个版本但正是那段时间打下的底子让后面学并发框架都轻松了很多。希望你也能沉住气把这份总结当作索引一个一个知识点啃扎实。