如何吃透一个Java项目?(附学习实践)

Posted king哥Java架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何吃透一个Java项目?(附学习实践)相关的知识,希望对你有一定的参考价值。

先说一下自己的情况:就是对着视频敲Java项目,其中遇到的BUG还能解决,但就是每次敲完一个项目,就感觉很空虚,项目里面的知识点感觉懂了但又好像没懂,我应该怎样才能掌握一个项目所用的知识点呢?至少不至于过了一头半个月就想不起来这个项目是什么东西了。

写博客记录?,画思维导图?还是怎么样呢?有没有过来人能给点经验呢?

首先,尝试分析下题主感到空虚、似懂非懂的原因,从问题描述来看原因可能有以下几方面:

目标不清晰

在项目学习之前,是否有认真梳理和思考过,希望通过项目学习到哪些技术、重点需掌握哪些知识点?这些知识点又属于自己技术体系中哪个环节,是需要必须熟练掌握还是了解原理即可?相信只有明确目标之后才有学习侧重点和方向。

▐ 学习方法

项目学习过程中,是否有带着问题和思考?比如项目核心需要解决的问题场景、使用了哪些技术方案,为什么需要这些技术,方案选择考虑主要有哪些?系统模块这样分层和实现的好处是?这个方法的实现,性能是否可以进一步优化等等。

如果只是纯粹跟着视频将项目代码机械敲一遍,我认为跟练习打字没任何区别,写出来的代码也是没有灵魂如行尸走肉。我相信只有结合自己的思考和理解,才可能赋予新的灵魂,做到知其然知其所以然,相关知识点也才能真正转化为自己的技术。

复习与应用

纸上得来终觉浅,绝知此事要躬行,相信对编程而言更是如此,唯有实践才能出真知。对项目中学到的相关技术、知识点需要在不同场景反复练习和应用,并对过程中遇到的问题不断总结和反思。

其次,回到题主问题,如何吃透一个Java项目?从个人经验来看,大致可以从以下几方面入手:

项目背景了解

学习之前,先对项目业务背景和技术体系做大致的了解,这点非常重要,一是为了解项目核心要解决问题域,二是知道系统涉及哪些技术体系,这样在学习之前可以有相关技术知识准备,以便更轻松高效学习。另外,学习完之后也可以清楚知道,什么样问题可以使用什么技术、什么方案来解决、如何解决的。

系统设计文档学习

对项目和系统大概了解之后,可以开始对系统设计文档熟悉,建议按照架构文档、概要设计、详细设计方式递进。通过设计文档的学习,可以快速对各系统模块有个框架性认识,知道各系统职责、边界、如何交互、系统核心模型等等。

对于设计文档的学习,切不可走马观花,一定要带着问题和思考。比如项目背景中的核心业务问题,架构师是如何转化成技术落地,方案为什么要这样设计,模型为什么要这样抽象,这样做的好处是什么等等?同时,对不理解的问题做需好笔记,以便后续向老师或其他同事请教或讨论等等。

系统熟悉和代码阅读

通过设计文档的学习,对系统设计有整体了解之后,接下来就可以结合业务场景、相关问题去看代码如何实现了。不过代码阅读,也需要注意方式方法,切不可陷入代码细节,应该自顶向下、分层分模块的阅读,以先整体、后模块、单功能点的方式层层递进。先快速走读整个代码模块逻辑,然后再精读某个类、方法的实现。

代码阅读过程中,建议一边阅读一边整理相关代码模块、流程分支、交互时序,以及类图等,以便更好理解,有些IDE工具也可根据代码自动生成,比如IntelliJ IDEA。

代码阅读除了关注具体功能的实现之外,更重要的是需要关心代码设计上的思路和原理、性能考究、设计模式、以及设计原则的应用等。同样,阅读代码注释也非常重要,在研究一个API或方法实现时,先认真阅读代码注释会让你事半功倍,尽可能不要做从代码中反推逻辑和功能的事情。

最后,对于核心功能代码建议分模块精读,不明白部分可借助代码调试。

然后,对于技术学习这块我给几点个人建议,以供题主参考:

制定学习规划

梳理一份适合自己的技术规划,并制定明确的学习路线和计划,让学习更有方向和重点。同样在视频课程的选择上也会更清晰,知道什么样视频该学、什么不该学,也不容易感到迷茫和空虚。如今网上各种学习资料、视频汗牛充栋,学会如何筛选有效、适合自己的信息非常重要。

思考与练习

对于技术编程,无捷径可言,思考和练习都非常重要,需要不断学习、思考、实践反复操练。从了解、会用、知原理、优化不断演进。结合学习计划,可以给自己制定不同挑战,比如学习spring可以尝试自己实现一个ioc容器等等。另外,工作或学习过程中遇到的问题,也是你快速提升技术能力的一个好方法,也请珍惜你遇到的每个问题的机会。时间允许的话,也请尽可能去帮助别人解答问题,像stackoverflow就是个非常不错的选择,帮助别人的同时提升自己。

如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
[Java架构群]
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

分享与交流

保持思考总结的习惯,将学到的技术多与人分享交流,教学相长。多与优秀的程序员一起、多参与优秀的开源项目等。

最后,我再以我们团队Dubbo核心开发@哲良 大神的另一开源框架TransmittableThreadLocal(TTL)为例,来讲解下我们该如何学习和快速掌握一个项目。

  • TransmittableThreadLocal(TTL):(https://github.com/alibaba/transmittable-thread-local)

结合上文所述,首先我会将TTL项目相关文档、issues列表认真阅读一遍,让自己对项目能有个大体的认识,并梳理出项目一些关键信息,比如:

  • 文档(https://github.com/alibaba/transmittable-thread-local#-%E5%8A%9F%E8%83%BD)

  • issues列表(https://github.com/alibaba/transmittable-thread-local/issues)

核心要解决的问题

用于解决「在线程池或线程会被复用情况下,如何解决线程ThreadLocal传值问题」

有哪些典型业务场景

  • 分布式跟踪系统或全链路压测(即链路打标)

  • 日志收集记录系统上下文

  • Session级Cache

  • 应用容器或上层框架跨应用代码给下层SDK传递信息

使用到的技术

有线程、线程池、ThreadLocal、InheritableThreadLocal、并发、线程安全等。

然后,再结合使用文档编写几个测试demo,通过程序代码练习和框架使用,一步步加深对框架的理解。比如我这里首先会拿TTL与原生JDK InheritableThreadLocal进行不同比较,体验两者的核心区别。

`public class ThreadLocalTest {` `private static final AtomicInteger ID_SEQ = new AtomicInteger();` `private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(1, r -> new Thread(r, "TTL-TEST-" + ID_SEQ.getAndIncrement()));` `//` `private static ThreadLocal<String> THREAD_LOCAL = new InheritableThreadLocal<>();` `//⑴ 声明TransmittableThreadLocal类型的ThreadLocal` `//private static ThreadLocal<String> THREAD_LOCAL = new TransmittableThreadLocal<>();` `public static void testThreadLocal() throws InterruptedException {` `try {` `//doSomething()...` `THREAD_LOCAL.set("set-task-init-value");` `//` `Runnable task1 = () -> {` `try {` `String manTaskCtx = THREAD_LOCAL.get();` `System.out.println("task1:" + Thread.currentThread() + ", get ctx:" + manTaskCtx);` `THREAD_LOCAL.set("task1-set-value");` `} finally {` `THREAD_LOCAL.remove();` `}` `};` `EXECUTOR.submit(task1);` `//doSomething....` `TimeUnit.SECONDS.sleep(3);` `//⑵ 设置期望task2可获取的上下文` `THREAD_LOCAL.set("main-task-value");` `//⑶ task2的异步任务逻辑中期望获取⑵中的上下文` `Runnable task2 = () -> {` `String manTaskCtx = THREAD_LOCAL.get();` `System.out.println("task2:" + Thread.currentThread() + ", get ctx :" + manTaskCtx);` `};` `//⑷ 转换为TransmittableThreadLocal 增强的Runnable` `//task2 = TtlRunnable.get(task2);` `EXECUTOR.submit(task2);` `}finally {` `THREAD_LOCAL.remove();` `}` `}` `public static void main(String[] args) throws InterruptedException {` `testThreadLocal();` `}``}``//InheritableThreadLocal 运行结果:``task1:Thread[TTL-TEST-0,5,main], get ctx:set-task-init-value``task2:Thread[TTL-TEST-0,5,main], get ctx :null` `//TransmittableThreadLocal 运行结果``task1:Thread[TTL-TEST-0,5,main], get ctx:set-task-init-value``task2:Thread[TTL-TEST-0,5,main], get ctx :main-task-value `

通过代码运行结果,我们可以直观看到使用JDK原生InheritableThreadLocal,在task2异步任务中是无法正确获取代码⑵处所设置的上下文参数,只有改用TransmittableThreadLocal之后,程序才如我们预期正常获取。

不难发现,由JDK原生ThreadLocal切换到TransmittableThreadLocal,只需要做极少量的代码适配即可。

`//private static ThreadLocal<String> THREAD_LOCAL = new InheritableThreadLocal<>();``//⑴ 声明TransmittableThreadLocal类型的ThreadLocal``private static ThreadLocal<String> THREAD_LOCAL = new TransmittableThreadLocal<>();``...``//⑷ 转换为TransmittableThreadLocal 增强的Runnable``task2 = TtlRunnable.get(task2);`

相信看到这里我们都会不禁想问,为什么只需要简单的更改两行代码,就可以平滑实现上下文透传?TTL框架背后具体都做了哪些工作,到底是怎么实现的呢?相信你和我一样都会比较好奇,也一定有想立马阅读源码一探究竟的冲动。

不过,通常这个时候,我并不会一头扎进源码,一般都会先做几项准备工作,一是回到设计文档再仔细的阅读下相关实现方案,把关键流程和原理了解清楚;二是把涉及到的技术体相关的基础知识再复习或学习一遍,以避免由于一些基础知识原理的不了解,导致源码无法深入研究或花费大量精力。像这里如果我对Thread、ThreadLocal、InheritableThreadLocal、线程池等相关知识不熟悉的话,一定会把相关知识先学习一遍,比如ThreadLocal基本原理、底层数据结构、InheritableThreadLocal如何实现父子线程传递等等。

假设这里你对这些知识都已掌握,如果不熟悉,网上相关介绍文章也早已是汗牛充栋,你搜索学习下即可。这里我们先带着到底如何实现的这个疑问,一起来探究下核心源码实现。

首先把源码clone下来导入IDE,然后结合文档把系统工程结构和各功能模块职责快速熟悉一遍,然后结合文档和Demo找到关键接口和实现类,利用IDE把相关类图结构生成出来,以便快速理解类之间关系。非常不错,TTL整体代码非常精练、命名和包信息描述也都非常规范和清晰,我们可以快速圈出来。

从类图中我们可以清晰看到核心关键类TransmittableThreadLocal是从ThreadLocal继承而来,这样的好处是不破坏ThreadLocal原生能力的同时还可增强和扩展自有能力,也可保证业务代码原有互操作性和最小改动。

然后结合Demo代码,我们不难发现使用TTL主要有三个步骤,TransmittableThreadLocal声明、set、remove方法的调用。根据整个使用流程和方法调用栈,我们也可以很方便梳理出整个代码处理初始化、调用时序。

(这里借用官方原图)

通过流程图,我们可以清晰看到TTL核心流程和原理是通过TransmittableThreadLocal.Transmitter 抓取当前线程的所有TTL值并在其他线程进行回放,然后在回放线程执行完业务操作后,再恢复为回放线程原来的TTL值。

TransmittableThreadLocal.Transmitter提供了所有TTL值的抓取、回放和恢复方法(即CRR操作):

capture方法:抓取线程(线程A)的所有TTL值。

replay方法:在另一个线程(线程B)中,回放在capture方法中抓取的TTL值,并返回 回放前TTL值的备份

restore方法:恢复线程B执行replay方法之前的TTL值(即备份)

弄明白核心流程和原理后,我们现在来分析下相关核心代码,在声明TransmittableThreadLocal变量时,我们会发现框架初始化了一个类级别的变量holder用于存储用户设置的所有ttl上下文,也是为了后续执行capture抓取时使用。

 `// Note about the holder:` `// 1. holder self is a InheritableThreadLocal(a *ThreadLocal*).` `// 2. The type of value in the holder is WeakHashMap<TransmittableThreadLocal<Object>, ?>.` `//    2.1 but the WeakHashMap is used as a *Set*:` `//        the value of WeakHashMap is *always* null, and never used.` `//    2.2 WeakHashMap support *null* value.` `private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =` `new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {` `@Override` `protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {` `return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();` `}` `@Override` `protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {` `return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(parentValue);` `}` `};` `/**` `* see {@link InheritableThreadLocal#set}` `*/` `@Override` `public final void set(T value) {` `if (!disableIgnoreNullValueSemantics && null == value) {` `// may set null to remove value` `remove();` `} else {` `super.set(value);` `addThisToHolder();` `}` `}` `private void addThisToHolder() {` `if (!holder.get().containsKey(this)) {` `holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.` `}` `}`

结合set方法实现来看,我们会发现holder变量设计的非常巧妙,业务设置的上下文value部分继续复用ThreadLocal原有数据结构ThreadLocalMap来存储( super.set(value));capture的数据源利用holder进行引用存储(addThisToHolder put this)。这样的好处是既可保持ThreadLocal数据存储原有的封装性,又很好实现扩展。除此之外,holder还有其他设计考究,这里抛出来大家可以思考下:

  1. 为什么holder需要设计成static final类级别变量?

  2. ttl变量的存储为什么需要使用WeakHashMap,而不是hashmap或其他?

然后我们再来看异步task转换 TtlRunnable.get(task2) 核心代码实现,代码整体实现相对比较简单,get方法是一个静态工厂方法,主要作用是将业务传入的普通Runnable task装饰成TtlRunable类,并在TtlRunable构造方法中进行线程capture动作(具体实现我们后面再分析),然后将结果存储到对象属性capturedRef中。

 `@Nullable` `public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {` `if (null == runnable) return null;` `if (runnable instanceof TtlEnhanced) {` `// avoid redundant decoration, and ensure idempotency` `if (idempotent) return (TtlRunnable) runnable;` `else throw new IllegalStateException("Already TtlRunnable!");` `}` `//将入参runnable进行了装饰` `return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);` `}` `//......``public final class TtlRunnable implements Runnable, TtlWrapper<Runnable>, TtlEnhanced, TtlAttachments {` `private final AtomicReference<Object> capturedRef;` `private final Runnable runnable;` `private final boolean releaseTtlValueReferenceAfterRun;` `private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {` `this.capturedRef = new AtomicReference<Object>(capture());` `this.runnable = runnable;` `this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;` `}` `/**` `* wrap method {@link Runnable#run()}.` `*/` `@Override` `public void run() {` `final Object captured = capturedRef.get();` `if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {` `throw new IllegalStateException("TTL value reference is released after run!");` `}` `final Object backup = replay(captured);` `try {` `runnable.run();` `} finally {` `restore(backup);` `}` `}`  `//........`` }`

然后是run方法,这也是核心关键的CRR操作了。这里通过模板方法将CRR操作编排在业务逻辑执行的前后了,也即业务逻辑执行前会将capturer的值进行replay恢复,执行后进行复原restore操作。同样这里也有几个问题很值我们思考:

  1. capture操作为什么需要放到TtlRunnable构造方法中,而不能在run方法中?

  2. 代码中使用了哪两个设计模式,使用设计模式的好处是什么?

  3. 业务执行完之后为什么还需要restore操作?

接下来,我们再分别对capture、replay、restore方法实现做个一一分析。首先是capture方法,我们可以看到capture操作整体比较简单,主要是将set操作保存到holder变量中的值进行遍历并以Snapshot结构进行存储返回。

 `/**` `* Capture all {@link TransmittableThreadLocal} and registered {@link ThreadLocal} values in the current thread.` `*` `* @return the captured {@link TransmittableThreadLocal} values` `* @since 2.3.0` `*/` `@NonNull` `public static Object capture() {` `return new Snapshot(captureTtlValues(), captureThreadLocalValues());` `}` `private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {` `HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();` `for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {` `ttl2Value.put(threadLocal, threadLocal.copyValue());` `}` `return ttl2Value;` `}` `private static HashMap<ThreadLocal<Object>, Object> captureThreadLocalValues() {` `final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<ThreadLocal<Object>, Object>();` `for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {` `final ThreadLocal<Object> threadLocal = entry.getKey();` `final TtlCopier<Object> copier = entry.getValue();` `threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));` `}` `return threadLocal2Value;` `}`

另一个captureThreadLocalValues,主要是用于将一些已有ThreadLocal中的上下文一起复制,已有ThreadLocal需要通过registerThreadLocal方法来单独注册。相关代码如下:

`public static class Transmitter {` `//....` ` private static volatile WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> threadLocalHolder = new WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>>();` `private static final Object threadLocalHolderUpdateLock = new Object();` `//......` `public static <T> boolean registerThreadLocal(@NonNull ThreadLocal<T> threadLocal, @NonNull TtlCopier<T> copier, boolean force) {` `if (threadLocal instanceof TransmittableThreadLocal) {` `logger.warning("register a TransmittableThreadLocal instance, this is unnecessary!");` `return true;` `}` `synchronized (threadLocalHolderUpdateLock) {` `if (!force && threadLocalHolder.containsKey(threadLocal)) return false;` `WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> newHolder = new WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>>(threadLocalHolder);` `newHolder.put((ThreadLocal<Object>) threadLocal, (TtlCopier<Object>) copier);` `threadLocalHolder = newHolder;` `return true;` `}` `}` `//......``}`

这里代码有个非常关键的处理,由于WeakHashMap非线程安全,为了避免并发问题安全加上了synchronized锁操作。这里有可以思考下除了synchronized关键字还有什么保障线程安全的方法。另外,实现threadLocal注册时为已经在锁块中了,为什么还要做new copy重新替换操作,这样做目的是什么?大家可以想想看。

最后就是replay和restore方法,整体实现逻辑非常清晰,主要是将captured的值在当前线程ThreadLocal中进行重新赋值初始化,以及业务执行后恢复到原来。这里很佩服作者对不同情况的细致考虑,不是直接将当前holder中的上下文直接备份,而是与之前已capture的内容比较,将业务后set的上下文进行剔除,以免在恢复restore时出现前后不一致的情况。

`@NonNull``public static Object replay(@NonNull Object captured) {` `final Snapshot capturedSnapshot = (Snapshot) captured;` `return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));``}``@NonNull``private static HashMap<TransmittableThreadLocal<Object>, Object> replayTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {` `HashMap<TransmittableThreadLocal<Object>, Object> backup = new HashMap<TransmittableThreadLocal<Object>, Object>();` `for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {` `TransmittableThreadLocal<Object> threadLocal = iterator.next();` `// backup` `backup.put(threadLocal, threadLocal.get());` `// clear the TTL values that is not in captured` `// avoid the extra TTL values after replay when run task` `if (!captured.containsKey(threadLocal)) {` `iterator.remove();` `threadLocal.superRemove();` `}` `}` `// set TTL values to captured` `setTtlValuesTo(captured);` `// call beforeExecute callback` `doExecuteCallback(true);` `return backup;``}``private static void setTtlValuesTo(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> ttlValues) {` `for (Map.Entry<TransmittableThreadLocal<Object>, Object> entry : ttlValues.entrySet()) {` `TransmittableThreadLocal<Object> threadLocal = entry.getKey();` `threadLocal.set(entry.getValue());` `}``}``public static void restore(@NonNull Object backup) {` `final Snapshot backupSnapshot = (Snapshot) backup;` `restoreTtlValues(backupSnapshot.ttl2Value);` `restoreThreadLocalValues(backupSnapshot.threadLocal2Value);``}``private static void restoreTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) {` `// call afterExecute callback` `doExecuteCallback(false);` `for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {` `TransmittableThreadLocal<Object> threadLocal = iterator.next();` `// clear the TTL values that is not in backup` `// avoid the extra TTL values after restore` `if (!backup.containsKey(threadLocal)) {` `iterator.remove();` `threadLocal.superRemove();` `}` `}` `// restore TTL values` `setTtlValuesTo(backup);``}`

核心代码分析完之后,再来简单总结下项目中学习到的知识点:

  1. 对ThreadLocal、InheritableThreadLocal有了更加系统和深入的理解,包括两者继承关系、底层数据结构ThreadLocalMap与Thread关联关系等。

  2. 面向gc编程(gc相关)、WeakHashMap(Java对象引用类型强、软、弱等)、线程安全、并发等等

  3. 设计模式相关,装饰模式、工厂、模板方法、代理等

  4. TTL虽然代码量不算多,但短小精悍,也处处体现了作者超高的设计和编程能力,每行代码都值得学习和反复琢磨。

我相信通过类似这样的一个项目学习流程下来,把每个环节都能踏踏实实做好,且过程中有贯穿自己思考和理解。相信你一定能把每个项目吃透,并把项目中的每个技术点都牢牢掌握。

最后,我所在团队是淘系技术部淘系架构团队,主要在负责一站式serverless研发平台建设,为业务不断提升研发效率和极致体验。平台已平稳支撑淘系互动、淘宝人生、金币庄园、特价版、闲鱼、拍卖、品牌轻店等多个业务的6.18、双11、双12、春晚等多个大促活动。

一直想整理出一份完美的面试宝典,但是时间上一直腾不开,这套一千多道面试题宝典,结合今年金三银四各种大厂面试题,以及 GitHub 上 star 数超 30K+ 的文档整理出来的,我上传以后,毫无意外的短短半个小时点赞量就达到了 13k,说实话还是有点不可思议的。

一千道互联网 Java 工程师面试题

内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、mysql、Spring、SpringBoot、SpringCloud、RabbitMQ、Kafka、Linux等技术栈(485页)

初级—中级—高级三个级别的大厂面试真题

阿里云——Java 实习生/初级

List 和 Set 的区别 HashSet 是如何保证不重复的

HashMap 是线程安全的吗,为什么不是线程安全的(最好画图说明多线程环境下不安全)?

HashMap 的扩容过程

HashMap 1.7 与 1.8 的 区别,说明 1.8 做了哪些优化,如何优化的?

对象的四种引用

Java 获取反射的三种方法

Java 反射机制

Arrays.sort 和 Collections.sort 实现原理 和区别

Cloneable 接口实现原理

异常分类以及处理机制

wait 和 sleep 的区别

数组在内存中如何分配

答案展示:

美团——Java 中级

BeanFactory 和 ApplicationContext 有什么区别

Spring Bean 的生命周期

Spring IOC 如何实现

说说 Spring AOP

Spring AOP 实现原理

动态代理(cglib 与 JDK)

Spring 事务实现方式

Spring 事务底层原理

如何自定义注解实现功能

Spring MVC 运行流程

Spring MVC 启动流程

Spring 的单例实现原理

Spring 框架中用到了哪些设计模式

为什么选择 Netty

说说业务中,Netty 的使用场景

原生的 NIO 在 JDK 1.7 版本存在 epoll bug

什么是 TCP 粘包/拆包

TCP 粘包/拆包的解决办法

Netty 线程模型

说说 Netty 的零拷贝

Netty 内部执行流程

答案展示:

蚂蚁金服——Java 高级

题 1:

  1. jdk1.7 到 jdk1.8 Map 发生了什么变化(底层)?

  2. ConcurrentHashMap

  3. 并行跟并发有什么区别?

  4. jdk1.7 到 jdk1.8 java 虚拟机发生了什么变化?

  5. 如果叫你自己设计一个中间件,你会如何设计?

  6. 什么是中间件?

  7. ThreadLock 用过没有,说说它的作用?

  8. Hashcode()和 equals()和==区别?

  9. mysql 数据库中,什么情况下设置了索引但无法使用?

  10. mysql 优化会不会,mycat 分库,垂直分库,水平分库?

  11. 分布式事务解决方案?

  12. sql 语句优化会不会,说出你知道的?

  13. mysql 的存储引擎了解过没有?

  14. 红黑树原理?

题 2:

  1. 说说三种分布式锁?

  2. redis 的实现原理?

  3. redis 数据结构,使⽤场景?

  4. redis 集群有哪⼏种?

  5. codis 原理?

  6. 是否熟悉⾦融业务?记账业务?蚂蚁⾦服对这部分有要求。

好啦~展示完毕,大概估摸一下自己是青铜还是王者呢?

前段时间,在和群友聊天时,把今年他们见到的一些不同类别的面试题整理了一番,于是有了以下面试题集,也一起分享给大家~

如果你觉得这些内容对你有帮助,可以加入csdn进阶交流群,领取资料

基础篇


JVM 篇


MySQL 篇



Redis 篇




由于篇幅限制,详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

需要的小伙伴,可以一键三连,下方获取免费领取方式!
在这里插入图片描述

以上是关于如何吃透一个Java项目?(附学习实践)的主要内容,如果未能解决你的问题,请参考以下文章

全面吃透JAVA Stream流操作,让代码更加的优雅

Java性能优化最佳实践,附答案

从零开始的AI·吃透kNN算法,学完我悟了(附实例代码)

炸裂!手摸手教你如何吃透一个 Java 项目,yyds

三个月吃透腾讯T4推荐247页深度实践Spark机器学习pdf终入腾讯云

大一 Java 课程设计实践 - 迷你版画图板(附源代码和设计文档)