插曲1:
有意思的是,我之前做过两个项目:快递e栈,天天吃货平台,自认为还不错的两个练手的项目,大家可以点击链接跳转,我把他们整理成了博客。
快递e栈1
快递e栈2
天天吃货平台
没想到这两个项目在面试官那里就是个“弟弟”,现在想一下也难免会是这样,自己的项目仅仅是做了一些个业务方面的逻辑的实现,和企业比起来可谓是“毫无并发量”可言,也就没有什么意义。面试时间其实是很宝贵的,没必要在这上面问很多。
插曲2:
在面试官那里得到的反馈,HashMap已经被玩烂了,99%的面试者都可能会讲,“我比较了解HashMap”,所以就会有一个问题:明明这一块的问题回答的很好,但面试官也并不觉得你有很特别的过人之处,如果回答的不是很好,那就更糟糕了,所以不到万不得已实在尴尬到无法继续了的情况下,还是不要把话题引到这个上面来了,性价比非常低了。
关于HashMap的详细整理,可以参考我的博客:一篇详尽的HashMap整理
下面进入正题,我凭着回忆尽力把今晚的问题复述出来:
项目,技术点、困难,怎么解决的?
classLoader执行原理?
JVM 新生代、老年代、垃圾回收算法介绍一下?
JVM做垃圾回收时会不会对正在运行中的线程产生影响?会不会杀死线程?
FullGC时过来大量QPS请求,会不会产生影响?
线程有几种状态,以及他们之间的转化讲一下?
notify()和notifyAll()的区别
wait()和sleep()的区别?
Java同步机制介绍一下?synchronize,CAS,ReentryLock的原理?
==和equals的区别?
string的底层实现,equals方法的底层实现,hashcode方法怎么重写的?
equals方法先使用==来判断两个字符串对象地址是否相等,相等则直接返回true,否则再一个字符一个字符的比。
在String类中有个私有实例字段hash表示该串的哈希值,在第一次调用hashCode方法时,字符串的哈希值被计算并且赋值给hash字段,之后再调用hashCode方法便可以直接取hash字段返回。
String类中的hashCode计算方法还是比较简单的,就是以31为权,每一位为字符的ASCII值进行运算.
计算公式:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
hashmap怎么实现的?
hashmap是怎么计算插入位置的
hashmap在高并发场景下会出现什么问题?(扩容方向思考一下)
红黑树特性?
红黑树插入操作?
红黑树和二叉树各自性能优势的场景?
这里姑且认为红黑树和AVL比较。
AVL平衡二叉搜索树,左右子树深度之差的绝对值不超过1且左右子树仍然为平衡二叉树。
AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;红黑是弱平衡的,用非严格的平衡来换取增删节点时候旋转次数的降低;所以简单说,搜索的次数远远大于插入和删除,那么选择AVL树,如果搜索,插入删除次数几乎差不多,应该选择RB树。
linux命令:杀死所有名字带有java的进程,命令怎么写?
ps -ef | grep java
1
kill掉相应的进程号
mysql:select * from xxx where name = xxxxx,name是非唯一索引,说一下内部具体执行过程?
这里要注意非聚集索引和聚集索引的区别,我把B+树中查找子节点数据的过程模拟讲了一遍。
Redis 持久化方案?
Redis 底层有哪些数据结构?
TCP和UDP的区别
HTTP使用的是TCP还是UDP?长连接还是短连接?
HTTP使用的是TCP,在HTTP/1.0中默认使用短连接,从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入Connection:keep-alive
介绍下HTTP的缓存?
HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,将其分为两大类(强制缓存,对比缓存)。
对比缓存,需要进行比较判断是否可以使用缓存:
浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。
再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。
设计模式–迭代器模式
2021年3月11日补充:
没错,我又面试阿里了,这次还是有几个题让我印象深刻,整理一下:
介绍一下ForkJoinPool
通常大家说的Fork/Join框架其实就是指由ForkJoinPool作为线程池、ForkJoinTask(通常实现其三个抽象子类)为任务、ForkJoinWorkerThread作为执行任务的具体线程实体这三者构成的任务调度机制。
ForkJoinPool充分利用多cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来。感觉有点像二分法。
ForkJoinPool 的工作特点是work stealing,ForkJoinPool底层维护着一个双端队列,当一个线程的任务队列执行完毕后,其他线程的任务队列还没有执行完毕,这时,已经执行完毕的线程就会到另一个线程的双端任务队列的尾部去偷取任务执行。
ForkJoinPool 的每个工作线程都维护着一个工作队列(WorkQueue),这是一个双端队列(Deque),里面存放的对象是任务(ForkJoinTask)。
每个工作线程在运行中产生新的任务(通常是因为调用了 fork())时,会放入工作队列的队尾,并且工作线程在处理自己的工作队列时,使用的是 LIFO 方式,也就是说每次从队尾取出任务来执行。
每个工作线程在处理自己的工作队列同时,会尝试窃取一个任务(或是来自于刚刚提交到 pool 的任务,或是来自于其他工作线程的工作队列),窃取的任务位于其他线程的工作队列的队首,也就是说工作线程在窃取其他工作线程的任务时,使用的是 FIFO 方式。
在遇到 join() 时,如果其他需要 join 的任务尚未完成,则会先处理其他任务,并等待其完成。
在既没有自己的任务,也没有可以窃取的任务时,进入休眠。
Spring Boot 打成的 jar 和普通的 jar
Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 \BOOT-INF\classes 目录下才是我们的代码,因此无法被直接引用。
Java8的函数式编程(Stream流)
https://blog.csdn.net/mu_wind/article/details/109516995
BeanFactory和ApplicationContext的区别
BeanFactory:
是Spring里面最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。
ApplicationContext:
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
国际化(MessageSource)
访问资源,如URL和文件(ResourceLoader)
载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
消息发送、响应机制(ApplicationEventPublisher)
AOP(拦截器)
BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化。