Java实战和面试宝典

这是一篇记录工作和学习过程中遇到的一些问题的文章,每个问题都很有代表性,值得不断回顾和深入思考的,答案未必十分准确,但可以作为一种参考,共勉!

读了这篇不一样的八股文,你将至少拥有三年经验^_^【持续更新中】

目录

一、基础篇

二、框架篇

三、中间件篇

四、算法篇

五、场景篇 


一、基础篇

 1.包装类Boolean是值传递还是引用传递?

代码:

public class TEST {
    public static void main(String[] args) {
        Boolean flag = new Boolean(true);
        setFalse(flag);
        System.out.println(flag);
    }

    private static void setFalse(Boolean flag) {
        flag = new Boolean(false);
    }

}

flag仍旧输出true;

在Java中,所有的参数都是按值传递的,而不是按引用传递的。这意味着你在传递一个对象作为参数时,实际上是将对象的副本传递给了函数。因此,在这个例子中,当你调用setFalse()方法时,它会接收到一个Boolean类型的副本,并将其设置为false。但是原始的flag仍旧保持着原来的值,因为它没有被直接修改。

如果你想在方法中更改原始变量的值,你需要使用一个可变对象类型,例如一个数组或者一个自定义类。你可以将Boolean对象包装在一个数组中,然后将该数组作为参数传递给setFalse()方法。这样就可以修改数组第一个元素,从而更改原始的Boolean值。

结论:当你把整个对象作为引用赋值给参数对象时,实际上只是传递进来的一个拷贝,你修改这个拷贝并不影响这个被传递的引用对象本身,但它如果有内部属性的话,是可以被重新设置的。

参考:【JAVA】值传递与引用传递_java值传递和引用传递-CSDN博客

 2. HashMap链表转红黑树为什么是大于8?

 HashMap链表转红黑树的操作,以提高查找的速度,红黑树的时间复杂度O(logn),而链表是O(n/2),因此只在O(logn)

3.内存告警,但CPU不高,dump中的也只有30M,我们2核4G的机器上配置了3G的内存

启动指令:

nohup nice java -jar -DfileEncoding=utf-8 -Dserver.port=$RUN_PORT -Xms3000m -Xmx3000m -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+PrintGCDetail -jar ${JAR_FULLNAME}>dev/null 2>&1 &

具体表现:

普罗米修斯显示堆内存占用82%,但是老年代还没达到GC的阈值,所以不能触发FULL GC,

但是我们机器的物理内存确占用大于96%,再下去可能宕机。

进程在申请内存时并不是直接分配物理内存的,而是分配一块虚拟空间,到真正堆这块虚拟空间写入数据时才会通过缺页异常(Page Fault)处理机制分配物理内存,也就是我们看到的进程Res指标。

当我们服务启动时,物理内存时一条上升的线,在不断生成对象的过程中堆内存达到-Xmx,然后物理内存就不会释放了,就会看到一条直线,而且FULL GC不会释放物理内存,因为频繁对内存扩容和缩容很耗费性能。
Java实战和面试宝典_第1张图片

结论:这个内存使用变化是正常的,只是-Xmx设置的太大了,然后java对象占用的内存达到之后这个值以后,物理内存就下不来了,重新指定-Xmx的值就行了。
 参考:JAVA 服务内存占用太高_java内存占用高怎么解决_summer_west_fish的博客-CSDN博客

 4.MySQL索引最佳实践有哪些?

①最左匹配原则

指的是查询从索引的最左前列开始并且不跳过索引中的列。

联合索引在查询的时候会先去比对第一个字段,第一个字段比对完成之后再去比对第二个字段,以此类推,不根据索引顺序查询无法精确定位到索引树的某个节点,导致无法根据索引树查找。

Java实战和面试宝典_第2张图片
所以使用了联合索引,查找条件必须要按照联合索引字段出现的顺序排列,否则会导致索引失效。

②不在索引列上做任何操作(计算、函数、自动或者手动类型转换),会导致索引失效而转向全表扫描

SQL中对name字段取了前3位,索引树中没有执行函数之后的值,所以根本走不到索引。

需要考虑使用函数的结果是否可以在索引树中定位,而且还可以保持索引树依然有序。
③存储引擎不能使用索引中范围条件右边的列

Java实战和面试宝典_第3张图片

Java实战和面试宝典_第4张图片

SQL走了部分字段,key_len为78是name字段和age字段之和,position字段并没有走索引。
范围后面的字段都不会再走索引。如果是大于等于则能使用到后面的两个字段索引。

④尽量使用覆盖索引(索引列包含查询列),减少select * 语句
查询尽量指明具体的字段,且尽可能被联合索引覆盖,覆盖索引可以避免回表查询

⑤在使用<>,not in, not exists的时候无法使用到索引
当使用>,<,<>这些时,MySQL内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引

⑥is null,is not null一般情况下也无法使用索引
因为null值比较特殊,在索引树中的字段不好比对。一般建议将字段设置为not null,即便字段没有值,也要设置一个默认值,方便走索引,提高效率。

⑦like以通配符开头,mysql索引失效会变成全表扫描操作

%在前面相当于查询是跳过了前面的部分字符,截取部分去查询,这剩下的部分已经不再有序了,所以不会用到索引。

而%在后面相当于最左前缀,从最开始的字符串匹配,在索引树中的数据还是有序的,所以可以使用索引。
⑧字符串不加单引号索引失效
查询的时候尽量使用相同类型,否则mysql在检测到类型不匹配时,底层可能会强转类型或者使用函数进行转换从而被放弃走索引。

二、框架篇

1.Spring如何解决循环依赖问题?

Spring能解决的循环依赖场景是setter注入的单例Bean,Spring通过三级缓存方案进行解决。

初始化的Bean先放入三级缓存(singletonFactory),填充依赖时从三级缓存中取出并放入二级缓存(early半成品),在此过程中可升级bean(SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference,如AOP中的AbstractAutoProxyCreator),最终完成填充后放入一级缓存(成型的singleton)。

还有一些失效场景:启用@Async+@EnableAsync,A和B相互依赖,但是A中含有@Async修饰的方法,按照缓存解决循环依赖的思路,A初始,填充B,B初始,填充时从三级缓存拿到A,并放到二级缓存(中途在getEarlyBeanReference中可以执行增强操作),最终B完成放入一级缓存,再回到A的填充阶段,一级缓存中拿到了B并填充,最终A填充完毕,执行初始化动作,因为被@Async修饰,因此需要被AOP动态代理,需要用到Bean对象增强处理器(AsyncAnnotationBeanPostProcessor,后置处理器。它只在一处postProcessorAfterInitization()实现了对代理对象的创建,未实现getEarlyBeanReference),最终A被增强了,但是B填充的A是普通对象,因此就失效了。
参考:今天一定要搞清楚Spring怎么解决循环依赖 - 掘金

2.Spring中@Lazy注解加在类上不起作用?

@Lazy是复制阶段的beanPostProcessor做的。
A依赖B期待B延迟初始化,即使B上面加了延迟初始化,它也会进行初始化。因为@Autowire没有增加@Lazy注解去标识这个属性不需要注入,所以会强行注入。

@Lazy注入的是代理对象,只有真正调用它方法时才会创建对象。
很多解决循环依赖的方法,归根到底都是推迟依赖bean的创建。

三、中间件篇

1.Redis预扣库存在并发场景下怎么将库存同步到数据库

可以考虑两种方式:
一是通过定时任务同步,redis预减在并发环境下是可靠的,然后每秒钟同步一下,保证最终一致就行。

二是redis扣减后,发布MQ消息,然后再更新数据库,sql应为set stock = stock - 1,通过行级锁保证数据的准确性。

2.RabbitMQ中防止消息被重复消费,方案:使用redis存储已消费的数据,每次消费时判断redis中是否有该条数据,如果有则不消费,没有则消费,那么当redis宕机了怎么办判断消息是否重复消费呢?

一是在业务侧处理时,业务状态做流转变更,缓存判断后再通过业务状态进行判断。

二是保证消息可查,消息除缓存外做持久化,消息被消费后重新进行标记,这样可以对重复消息或者未消费消息进行最终一致性补偿。

四、算法篇

1. 余弦相似度(COS)计算测试

Java实战和面试宝典_第5张图片Java实战和面试宝典_第6张图片 

 根据题目公式实现算法,三行为一组然后两两计算余弦相似度,再取三个最大值输出。具体步骤如下:

1.相同的caseid为一组,选取两组
2.两组的weight分别相乘求和作为分子
3.一组的weight平方相加再开方与另外一组相乘作为分母。
4.求出多个余弦相似值,然后进行排序,取出最大三个值输出。

2. 抽奖系统设计要求百万分之几的中奖概率如何设计?

第一种:在0-999999中取一个随机数,如果它的大小在0-3之间即为中奖。

第二种:如果当前的中奖概率是3/1000000,那就以1~1000000的范围去随机生成三个数,比如生成的三个随机数是10、20、30分别当做key存到redis中,开始抽奖的时候就同样在1~1000000的范围中随机生成一个数,然后去redis中匹配,匹配到就算中奖。

五、场景篇 

1. 公司要做一个服务区车辆出入检测的项目,现在要从别的公司的数据库表(一张表,只能查)里拿数据,拿到以后做分类整理,根据需求存入到不同的表里。这个项目里有一个可视化大屏,如何去实时获取数据比较好呢?定时任务存在延迟,mq的话感觉又太频繁了(这个服务区的车流量大概每天3000)

平均每分钟两三辆车出入服务区,高峰期可能要多些,可以按照这个频率或者大屏图表刷新的频率调度定时任务。如果有条件使用MQ的话更好,而且我认为也不存在太频繁的问题,自带削峰填谷特性可以很好满足这个场景。

2.1000万数据如何导出到Excel 

 一个Excel可以放10万条数据,循环开启线程,每个线程负责将数据取出并导出到一个Excel或者Sheet,线程编号以保证文档数据有序,每次查询的数据不宜过大,以避免OOM。

 3.SpringCloud项目中如何对接口进行鉴权?

 一种常见的实现方式是,将所有的请求都经过网关,网关作为所有请求的入口,在网关中对请求进行权限验证和路由转发。在这种方式下,网关是唯一的接口入口,可以在网关中进行全局的权限控制,包括对请求的身份认证和授权等。

另一种实现方式是,在每个服务模块中都实现自己的权限控制逻辑。这种方式下,每个服务模块都有独立的接口入口,可以在服务模块中控制对其提供的接口的访问权限。这种方式下,网关仅仅起到了路由转发的作用,没有对请求进行权限控制。
以上两种实现方式各有优劣,取决于具体的业务场景和需求。如果需要对所有请求进行全局的权限控制,第一种方式更为适合;如果每个模块需要单独控制对其提供接口的访问权限,第二种方式更为适合,另外这种方式下定时任务中的feign调用需要另开内部调用接口。

 4.你遇到一个解决不了的问题怎么办?

如果上网搜索不到相关资料,身边也没有人解决,那么可以考虑寻找替代的方案实现相近的效果。如果其它人可以解决,只是自己没有思路,就要加强学习。如果大家都解决不了,要及时向上级反馈,暴露风险,调整方案。

 4.hr面试需要注意什么?

(1)喜欢加班的人,能吃苦耐劳不计较报酬
(2)喜欢中庸,不要表现自己的特性
(3)喜欢稳定的,稳定超过一切包括技术、包括是否能做事
(4)性格不能过激,不要刻意表现出自己的过激个性,要适当克制自己情绪
(5)团队合作很重要,注重集体意识和团队合作的表达,切忌体现个人主义
(6)遇到善于领导他人、有组织力、健谈等表现你的领导力
(7)抗压,承担高压力工作,大事面前不慌张等表现优良工作品质的
(8)守时,按时完成任务
(9)是否有创造力等问题,选轻微同意或者轻微不同意,以免太抢眼容易挂
(10)前后不要矛盾,注意前后的连贯性,有些题目前面出现后面又会变形出现
(11)好交际的×
(12)喜欢人陪伴的×
(13)积极乐观√
(14)诚实守信√
(15)注重创新的形象√

 面试分享
1.介绍一下做过的项目及工作业绩
2.聊一个rpc框架的实现原理及架构设计
3.服务间调用慢处理方式,怎么定位问题
4.做微服务重构的时候遇到过哪些问题
5.还有一个就是框架里面tomcat框架线程设置多少,多线程怎么评估使用

你可能感兴趣的:(面试,职场和发展,java,架构)