背了几道面试题就敢说熟悉Java源码?我们不招连源码都不会看的人
来自:CSDN
作者:Baldwin_KeepMind
链接:https://blog.csdn.net/shouchenchuan5253/article/details/105196154
标题是我2019.6.28在深圳某500强公司面试时候面试官跟我说的话,即使是现在想起来,也是觉得无尽的羞愧,因为自己的愚钝、懒惰和自大,我到深圳的第一场面试便栽了大跟头。
我确信我这一生不会忘记那个燥热的上午,在头一天我收到了K公司的面试通知,这是我来深圳的第一个面试邀约。收到信息后,我激动得好像已经收到了K公司的Offer,我上网专门查了下K公司的面经,发现很多人都说他们很注重源码阅读能力,几乎每次都会问到一些关于源码的经典问题,因此我去网上找了几篇关于String、HashMap等的文章,了解到了很多关于Java源码的内容。看完后我非常的自信,心想着明天的所有问题我肯定都可以回答上来,心满意足的睡觉。
面试的那天上午,我9点钟到了K公司楼下,然后就是打电话联系人带我上去,在等待室等待面试,大概9:30的时候,前台小姐姐叫到了我的名字,我跟着她一起进入到了一个小房间,里面做了两个人,看样子都是做技术的(因为都有点秃),一开始都很顺利,然后问道了一个问题“你简历上说你熟悉Java源码,那我问你个问题,String类可以被继承么”,当然是不可以继承的,文章上都写了,String是用final修饰的,是无法被继承的,然后我又说了一些面试题上的内容,面试官接着又问了一个问题:
“请你简单说一下substring的实现过程”
是的,我没有看过这一题,平时使用的时候,也不会去看这个方法的源码,我支支吾吾的回答不上来,我能感觉到我的脸红到发烫。他好像看出了我的窘迫,于是接着说“你真的看过源码么?substring是一个很简单的方法,如果你真的看过,不可能不知道”,到这个地步,我也只好坦白,我没有看过源码,是的我其实连简单的substring怎么实现的都不知道,我甚至都找不到String类的源码。
面试官说了标题上的那句话,然后我面试失败了。
我要感谢这次失败的经历,让我打开了新世界,我开始尝试去看源码,从jdk源码到Spring,再到SpringBoot源码,看得越多我越敬佩那些写出这优秀框架的大佬,他们的思路、代码逻辑、设计模式,是那么的优秀与恰当。不仅如此,我也开始逐渐尝试自己去写一些框架,第一个练手框架是“手写简版Spring框架–YzSpring”,花了我一周时间,每天夜里下班之后都要在家敲上一两个小时,写完YzSpring之后,我感觉我才真正了解Spring,之前看网上的资料时总觉得是隔靴搔痒,只有真正去自己手写一遍才能明白Spring的工作原理。
再后来,我手上的“IPayment”项目的合作伙伴一直抱怨我们接口反馈速度慢,我着手优化代码,将一些数据缓存到Redis中,速度果然是快了起来,但是每添加一个缓存数据都要两三行代码来进行配套,缓存数据少倒无所谓,但是随着越来越多的数据需要写入缓存,代码变得无比臃肿。有天我看到@Autowired的注入功能,我忽然想到,为什么我不能自己写一个实用框架来将这些需要缓存的数据用注解标注,然后用框架处理呢?说干就干,连续加班一周,我完成了“基于Redis的快速数据缓存组件”,引入项目之后,需要缓存的数据只需要用@BFastCache修饰即可,可选的操作还有:对数据进行操作、选择数据源、更新数据源、设置/修改Key等,大大提高了工作效率。第一次自写轮子,而且效果这么好,得到了老大哥的肯定,真的很开心。
那么现在我要问你三个问题:
- 你看源码么?
- 你会看源码么?
- 你从源码中有收获么?
1.快速查错、减少出错
在编码时,我们一般都发现不了RuntimeException,就比如String的substring方法,可能有时候我们传入的endIndex大于字符串的长度,这样运行时就会有个错误:
有时候稀里糊涂把代码改正确了,但是却不知道为什么发生这个异常,下次编写的时候又发生同样的问题。如果我们看过源码,我们就可以知道这个异常发生的原因:
源码中给出了三个可能抛出上面异常的情景,那我们就可以根据这三种情景去检查我们的代码,也以后在编码的时候注意这些问题。
2.学习编程习惯
还是说上面的substring源码,请注意他的return,如果是你,你会怎么写?如果没有看过源码,我肯定会写成下面:
虽然功能是一样的,但是运用三元运算可以用一行代码解决问题,而且又不用写if语句,现在我已迷上了三元运算符,真的很好用。
3.学习设计模式(针对新手)
好吧!我摊牌了,作为一个半路出家的程序员,我没有接受过系统化的教学,所有的都是自学,在之前我完全不了解设计模式,只知道有23种设计模式,最多知道单例模式。
不了解设计模式最主要的原因是当时没有实战经验,自己写的项目都是比赛项目,完全不用不上设计模式,基本上是能跑就行。我第一次接触设计模式是在log4j的工厂模式,当时是完全不懂工厂模式该怎么用,就是看着log4j的源码一步步学会了,然后自己做项目的时候就会有意无意的开始运用设计模式,下面是我项目中使用单例模式获取配置类的代码:
3.小总结
你们可能很多人都会觉得上面的东西很简单,请不要被我误导,因为上面都是最简单的例子,源码中值得学习的地方非常多,只有你自己去看,才能明白。
我们这里以一个热度非常高的类HashMap来举例,同时我非常建议你使用IDEA来阅读编码,其自带反编译器,可以让我们快速方便的看到源码,还有众多快捷键操作,让我们的操作爽到飞起。
1.定位源码
其实定位的时候也有多种情况:
Ctrl+左键
像这种情况,我们要进入只属于HashMap类的方法,我们可以直接Ctrl+左键就可以定位到源码位置了。
Ctrl+Alt+B
HashMap的put方法是重写了Map的方法,如果我们用Ctrl+左键,会直接跳到Map接口的put方法上,这不是我们想要的结果,此时我们应该把鼠标光标放到put上,然后按下Ctrl+Alt+B,然后就出现了很多重写过put方法的类。
找到我们需要查看的类,左键点击就可以定位到put方法了。
2.查看继承关系
一个类的继承关系很重要,特别是继承的抽象类,因为抽象类中的方法在子类中是可以使用的。
上一步中我们已经定位到了HashMap源码上,现在拉到最上面,我们可以看到类定义的时候是有一下继承关系:
当然,如果想更直观更详细的话,在IDEA中有个提供展示继承关系的功能,可以把鼠标放在要查看的类上,然后Ctrl+Alt+Shift+U,或者右键=》Diagrams=》Show Diagram,然后我们就可以看到继承关系:
然后大致查看下AbstractMap抽象类,因为有可能等下会用到。
3.查看类常量
我们进到HashMap构造函数时,发现了以下代码:
我们只知道initialCapacity是我们传入的初始容量,但完全不知道这个DEFAULT_LOAD_FACTOR是什么、等于多少,我们可以先大致看一下这个类所拥有的的常量,留个印象就好,有利于等下阅读源码,Ctrl+左键定位到这个量的位置,然后发现还有好几个常量,常量上面有注释,我们看一下,这有助于我们理解这些常量:
这样,我们就对HashMap中常量的作用和意义有所理解了
4.查看构造函数
我们一般看一个类,首先得看这个类是如何构建的,也就是构造方法的实现:
很明显,上面的构造函数指向了另一个构造函数,那么我们点进去看看
这里就是我们构造函数实现的地方了,我们来一行一行的去分析:
1.我们的initialCapacity参数是我们一开始传进来的16,loadFactor是上一步中用的默认参数0.75f。
2.判断初始容量是否小于0,小于0就抛出异常,不小于0进行下一步。
3.判断初始容量是否大于最大容量(1 << 30),如果大于,就取最大容量。
4.判断加载因子是否小于等于0,或者是否为数字,抛出异常或下一步。
5.初始化这个HashMap的加载因子。
6.最后一行是HashMap的扩容机制,根据我们给的容量大小来确定实际的容量,我们来看一下该方法的源码。
这一步其实就是为了求大于我们设定的容量的最小2的幂数,以这个值作为真正的初始容量,而不是我们设定的值,这是为了随后的位运算的。现在我们解释一下上面的运算:
以cap=13为例,那么n初始=12,n的二进制数为00001100,随后一次右移一位并进行一次与n的或运算,以第一次为例,首先|=右边运算为无符号右移1位,那么右边的值为00000110,与n进行或运算值为00001110,反复运算到最后一步的时候,n=00001111,然后在return的时候便返回了n+1,也就是16.
至此,我们完成了一个空HashMap的初始化,现在这个HashMap已经可以操作了。
5.查看方法逻辑
我们一般使用HashMap的时候,put方法用的比较多,而且他涉及的内容也比较多,现在来定位到HashMap的put方法。
put方法又调用了putVal方法,并且将参数分解了,key和value没什么好说的,我们来先看一下hash(key)这个方法干了什么。
如果当前key是null,那么直接返回哈希值0,如果不是null,那就获取当前key的hash值赋值给h,并且返回一个当前key哈希值的高16位与低16位的按位异或值,这样让高位与低位都参与运算的方法可以大大减少哈希冲突的概率。
OK!多出来的三个参数,其中hash值的内容我们已经知道了,但是三个值都不知道有什么用,不要急,我们进入putVal方法。
看这上面一堆代码,是不是又开始头疼了,不要怕他,我们一行一行分解他,就会变得很容易了。
第一步还是要看注释,注释已经翻译好了,请享用。
然后来看内容
1.创建了几个变量,其中Node是HashMap的底层数据结构,其大致属性如下:
2.判断当前table是否为空,或者table的长度是否为0,同时给tab和n赋值,如果条件成立(当前的HashMap是空的),那就进行resize,并将resize的值赋予tab,把tab数组的长度赋予n,由于篇幅原因,这里不详细解说resize()方法,这个方法内容比较多,在其他文章中也说了很多,今天的重点是说明如何去读源码,而不是HashMap。
3.判断底层数组中当前key值元素的hash值对应的位置有没有元素,如果没有,直接将当前元素放进去即可。
4.接上一步,如果底层数组对应位置中已经有值,那就进行其他的一些列操作把数据写入,并返回oldValue。
我们走完整个流程后,总结几个需要注意的点,比如HashMap.put方法里要注意的就是resize,尾插,树与列表之间的转换。
由于篇幅问题,这个方法里的内容,我只是简略的说一下,具体的查看源码的方式和之前大同小异,一步步分析即可。
6.小总结
查看源码的几个技巧:
1.Ctrl+左键或Ctrl+Alt+B定位到正确的源码位置
2.查看类里面一些量,有个大概的认识
3.查看构造函数看实例的初始化状况
4.如果代码比较复杂,分解代码,步步为营
5.其他的源码的阅读都可以按照这个套路来分析
作者=萌新,如有错误,欢迎指出。
阅读源码绝对是每个程序员都需要的技能,即使刚开始很难读懂,也要慢慢去习惯。
对了,在这里说一下,我目前是在职Java开发,如果你现在正在学习Java,了解Java,渴望成为一名合格的Java开发工程师,在入门学习Java的过程当中缺乏基础入门的视频教程,可以关注并私信我:01。获取。我这里有最新的Java基础全套视频教程。
全网首发!从入门到源码讲解,让你真正读懂看懂Spring源码笔记
之前一起工作的小伙伴最近参加某一线互联网公司的面试,被问到了一些Spring 源码的问题,看看大家能否答出来:
- 1、bean的创建默认是单例
- 2、bean的生命周期
- 3、Spring事务在controller层不起作用的原因?
- 4、Spring的两种IOC容器。
- 5、Spring的几种注 入bean的方式。
- 6、Spring事务在controller层不起作用的原因?
如果平时只有CRUD的经验不了解Spring源码,面对这样面试题,大概率一头雾水,直接熄火。所以小编今天给大家免费分享的一份关于Spring源码的学习笔记和一套java架构师面试题大汇总,图文结合,内容详细,非常适合再学习Spring 源码的朋友学习!绝对是你从未见过的全新版本,独家首发!!!
下面将这份文档的内容以图片的形式展现出来,但篇幅有限只能展示部分,如果你需要“高清完整的pdf版”,可以直接前往文末自取。
- 第1节 Spring 简介
- 第2节 Spring 发展历程
- 第3节 Spring 的优势
- 第4节 Spring 的核心结构
- 第5节 Spring 框架版本
想都不用想,无非就是IoC和AOP。
思考:
IoC:什么是IoC?解决了什么问题?IoC和DI的区别?
AOP:什么是AOP?AOP在解决什么问题?为什么叫做面向切面编程?
七步法层层递进,从实战步步解决:
银行转账案例界面——银行转账案例表结构——银行转账案例代码调用关系——银行转账案例关键代码——银行转账案例代码问题分析——问题解决思路——案例代码改造
第1节 Spring IoC容器初始化主体流程
- 1.1 Spring IoC的容器体系
- 1.2 Bean生命周期关键时机点
- 1.3 Spring IoC容器初始化主流程
第2节 BeanFactory创建流程
- 2.1 获取BeanFactory子流程
- 2.2 BeanDefinition加载解析及注册子流程
第3节 Bean创建流程
第4节 lazy-init 延迟加载机制原理
第5节 Spring IoC循环依赖问题
- 5.1 什么是循环依赖
- 5.2 循环依赖处理机制
第1节 AOP 相关术语
- 1.1 业务主线
- 1.2 AOP 术语
第2节 Spring中AOP的代理选择
第3节 Spring中AOP的配置方式
第4节 Spring中AOP实现
- 4.1 XML 模式
- 4.2 XML+注解模式
- 4.3 注解模式
第5节 Spring 声明式事务的支持
- 5.1 事务回顾
5.1.1 事务的概念
5.1.2 事务的四大特性
5.1.3 事务的隔离级别
5.1.4 事务的传播行为
- 5.2 Spring中事务的API
- 5.3 Spring 声明式事务配置
第1节 代理对象创建
- 1.1 AOP基础用例准备
- 1.2 时机点分析
- 1.3 代理对象创建流程
第2节 Spring声明式事务控制
- 2.1 @EnableTransactionManagement
- 2.2 加载事务控制组件
一切技术的产生与发展都离不开最基本的原理,只要追本溯源,了解技术体系最基本、最底层的原理,我们就能根据这些知识储备建立自己的后端技术框架,从而搭建一条完整的成长道路。
学Java就是学Spring,学Spring也就是吃透其核心IoC和AOP,这应该是我目前为止看到讲Spring源码讲的最好的笔记了,看着很少,但全是精华,没有一点点废话!
文中资料已整理打包,有需要的小伙伴可以私信小编【666】,即可获得免费领取方式啦!!
spring源码笔记!从入门到源码讲解,让你真正读懂看懂源码
- Spring 概述(基本情况)
- 核⼼思想 IoC 和 AOP
- ⼿写实现 IoC 和 AOP(⾃定义spring框架)
- Spring IoC ⾼级应⽤
- 基础知识
- ⾼级特性
- Spring IoC 源码深度剖析
- 设计⾮常优雅
- 设计模式
- 注意:原则、⽅法和技巧
- Spring AOP ⾼级应⽤
- 声明式事务控制
- Spring AOP 源码深度剖析
- 必要的笔记、必要的图、通俗易懂的语⾔化解知识难点
Spring 是分层的 full-stack(全栈) 轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 SpringMVC 和业务层事务管理等众多的企业级应⽤技术,还能整合开源世界众多著名的第三⽅框架和类库,已经成为使⽤最多的 Java EE 企业应⽤开源框架。
- 1997年 IBM 提出了EJB的思想;
- 1998年,SUN 制定开发标准规范EJB1.0;
- 1999年,EJB 1.1发布;
- 2001年,EJB 2.0发布;
- 2003年,EJB 2.1发布;
- 2006年,EJB 3.0发布;
- 2017 年 9 ⽉份发布了 Spring 的最新版本 Spring 5.0 通⽤版(GA)
- ⽅便解耦,简化开发
- AOP编程的⽀持
- 声明式事务的⽀持
- ⽅便程序的测试
- ⽅便集成各种优秀框架
- 降低JavaEE API的使⽤难度
- 源码是经典的 Java 学习范例
Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决⽅案的零侵⼊的轻量级框架。
- Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。
- ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。
- 数据访问与集成(Data Access/Integration)Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。
- Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。
- Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。
- IoC Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现描述的事情:Java开发领域对象的创建,管理的问题
- 传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象
- IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列事情)
- 控制:指的是对象创建(实例化、管理)的权利
- 反转:控制权交给外部环境了(spring框架、IoC容器)
- IoC解决对象之间的耦合问题
- DI:Dependancy Injection(依赖注⼊)怎么理解:
- IOC和DI描述的是同⼀件事情,只不过⻆度不⼀样罢了
- AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程
- AOP是OOP的延续,从OOP说起
- OOP三⼤特征:封装、继承和多态
- oop是⼀种垂直继承体系
在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
- 「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑
- 「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个⾯的概念在⾥⾯
提⾼培养代码架构思维、深⼊理解框架
定焦原则:抓主线宏观原则:站在上帝视⻆,关注源码结构和业务流程(淡化具体某⾏代码的编写细节)
断点(观察调⽤栈)反调(Find Usages)经验(spring框架中doXXX,做具体处理的地⽅)
下载源码(github)安装gradle 5.6.3(类似于maven) Idea 2019.1 Jdk 11.0.5导⼊(耗费⼀定时间)编译⼯程(顺序:core-oxm-context-beans-aspects-aop)⼯程—>tasks—>compileTestJava
IoC容器是Spring的核⼼模块,是抽象了对象管理、依赖关系管理的框架解决⽅案。Spring 提供了很多的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器 必须遵从的⼀套原则,具体的容器实现可以增加额外的功能,⽐如我们常⽤到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等⼀系列的内容,AnnotationConfigApplicationContext 则是包含了注解解析等⼀系列的内容。Spring IoC 容器继承体系⾮常聪明,需要使⽤哪个层次⽤哪个层次即可,不必使⽤功能⼤⽽全的。BeanFactory 顶级接⼝⽅法栈如下
BeanFactory 容器继承体系
在讲解AOP术语之前,我们先来看⼀下下⾯这两张图,它们就是第三部分案例需求的扩展(针对这些扩展的需求,我们只进⾏分析,在此基础上去进⼀步回顾AOP,不进⾏实现)
上图描述的就是未采⽤AOP思想设计的程序,当我们红⾊框中圈定的⽅法时,会带来⼤量的重复劳动。程序中充斥着⼤量的重复代码,使我们程序的独⽴性很差。⽽下图中是采⽤了AOP思想设计的程序,它把红框部分的代码抽取出来的同时,运⽤动态代理技术,在运⾏期对需要使⽤的业务逻辑⽅法进⾏增强。
Spring 实现AOP思想使⽤的是动态代理技术默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。
- 在Spring的AOP配置中,也和IoC配置⼀样,⽀持3类配置⽅式。
- 第⼀类:使⽤XML配置
- 第⼆类:使⽤XML+注解组合配置
- 第三类:使⽤纯注解配置
需求:横切逻辑代码是打印⽇志,希望把打印⽇志的逻辑织⼊到⽬标⽅法的特定位置(service层transfer⽅法)
感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞!
由于篇幅原因只能画出这些重点先讲一下,这一整套笔记我已经整理成了PDF文档,具体如下:
资料获取方式:转发和评论这篇文章,然后关注小编,后台私信【java】即可打包带走所有资料~
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。