java社招一年半面试问题总结
问题点
态度方面:
要自信点,专业点,语气要自信,回答错了也要自信承认错误
不要当场请教问题(摆正态度,我是去面试的,而且有些问题面试官也懒得理你)
只回答他问的是不行的,要学会自己扩展
语气方面:
要咬字清晰,少吸鼻子,少用‘呃’,‘就’,“就是”这些语气词
一句话不连贯,说到一半开始想
紧张(已经不那么紧张了,面了也有10来家了,虽然只拿到一个offer,更多的是不服和失落)
技能方面(立flag吧):
八股背的不是很熟,有些八股还需要背
项目经历过于简单,目前只有分布式锁(要去了解高并发,大数据这些情况,并且去接触一些面试题,然后学会自己包装一下)
面试官评价
个人很自信,(这里不能打断面试官),回答问题要尽量从真实经历去切入;
简历写得还行,问起来就一般;
项目一般(高并发,大数据这些情况要去接触一些面试题,然后学会自己包装一下)
java基础
stream流和for的区别?
stream流可以看作是for循环的一个语法糖;
stream有并发流,在超过百万级数据量时,使用stream流效率更高;
重载和重写的区别?
首先他们都是实现多态的方式
然后说一下他们基本的概念
重载:
同一类中的同名函数,具有不同参数个数或类型(不要求返回值),由静态类型确定,属于静态分派
重写:
子类含有父类相同名字、返回类型、和参数列表,则为重写,属于动态分派;
重写要求子类重写方法更容易访问,不能抛出更多异常,以及访问修饰符的限制不能比父类小;
不同点:
重载是编译时多态性,重写是运行时多态性
重载发生在一个类中,重写发生在子类和父类之间
重载不要求返回类型,重写要求返回类型相同
构造器和静态方法能被重载,但不能被重写
注意点:重写的返回类型不要求完全一样
- 返回类型使用使用泛型
- 返回类型使用父类的子类
抽象类和接口的区别?
你看了集合的源码,主要有哪些?
ArrayList、HashMap、ConcurrentHashMap这些(源码跟着b站或者咕泡再看看,一定要亲手点一点)
我比较熟悉的是 HashMap 这一块
项目中用的比较多的java集合类有哪些?
ArrayList,HashMap这些,然后多扯一些,讲讲具体的案例。
那你讲讲 HashMap 吧?
HashMap 是一种基于哈希表实现的 key-value键值对形式的数据结构。可以在 O(1) 的时间复杂度内对元素进行操作。
首先讲一下 hashMap 的底层数据结构:
底层采用数组+链表的形式去进行存储,在1.8的时候引入了红黑树,主要是对链表去做一个转化,进行效率的提升(引诱面试官提问链表和红黑树的转换)
hashmap的初始容量是16,扩容方式是2的幂
隐藏点:链表和红黑树的转换
如果链表长度超过8,并且数组长度大于64,就转换为红黑树;
如果链表长度小于等于 6 ,红黑树重新转化为链表
然后讲一下 put 方法的过程:
hashmap对 key 求哈希值然后计算下标,如果没有 哈希碰撞 就直接插入, 如果碰撞了就以链表的形式链接到后面。(引诱面试官提问哈希值计算过程)如果节点已经存在就替换旧值。
如果元素总个数大于 阈值(threshold) (容量 * 加载因子0.75) 就 resize 扩容,扩容还发生在当链表长度大于 8 但是此时数组容量小于 64 ,主要是为了扩容后让节点分步更均匀一些。
然后讲一下 HashMap 的哈希函数怎么实现:
(h = key.hashCode())^ (h >>> 16),首先调用 hashCode() 方法对 key 求 hash值,然后将hash值的低 16 位bit和高 16 位 bit 做异或运算获得 新的 hash 值,然后 (n - 1) & hash 获得下标 (n 指数组的长度)
为什么要和高16位进行 ^ 运算?
由于和 (length -1) 运算,length 绝大多数情况小于 2 的 16 次方。 所以始终是 hashcode 的低 16 位(甚至更低) 参与运算。 但是这样高 16 位是用不到的,为了让得到的下标更加散列,需要让高16位也参与运算,所以就需要低16位和高16位进行 ^ 运算。
为什么 & 位必须是(length - 1)?
长度是2的幂次,length -1 的所有二进制都是1,相当于 取余数,但是比 % 运算更快, table[i = (n -1)& hash];
为什么用 ^ 而不是用 & 或 |
因为 & 和 | 都会使结果偏向 0 或者 1,并不是均匀的概念,所以用 ^。
然后讲一下 hashmap 线程并发安全问题
多个线程同时 put , 当 put 的 key 一样造成一个线程 put 的数据被覆盖
多个线程同时检测到元素个数超过数组大小 * loadFactor, 同时对Node 数组 进行扩容,都重新计算元素位置和复制数据,最终只有一个线程扩容后的数据会复制成功,其他线程丢失,并且 put 的数据也丢失。
链表和红黑树转换的时候会抛出类型转化异常:两个线程同时将红黑树转换成链表,一个线程转换成功,红黑树变成链表了,另一个线程开始转换就会发现红黑树变成了链表,就会抛出类型转化异常。
hashMap 是怎么扩容的?
HashMap 的扩容操作分为两个步骤:
- 创建一个新的数组,长度是当前数组长度的两倍。
- 将原数组中的元素重新映射到新数组中的位置。这个过程需要重新计算每个元素的哈希值,然后重新映射到新数组的位置中。如果新数组中的某个位置已经有了元素,则需要进行链表或红黑树的操作,以保证元素可以正确地插入到数组中。
hashtable和 concurrenthashmap 的区别?
数据结构方面:
ConcurrentHashMap 1.7 采用分段数组+链表实现,1.8采用数组+链表+红黑树
hashtable采用数组+链表
线程安全方面:
ConcurrentHashMap 1.7 采用分段锁,使用的是 ReentrantLock,锁住部分分段数组 ,1.8采用 synchronized 和 CAS 来操作,一把锁锁住一个node节点
hashtable:使用synchronized 锁住整个容器,效率低。
分段锁怎么实现的?
上面说了,使用 ReentrantLock,锁住部分分段数组
数据结构中,哪些是线程安全的,哪些是非线程安全的,能举两个例子吗?
ArrayList,HashMap,StringBuilder是非线程安全的;
vector,HashTable,StringBuffer是线程安全的;
什么是线程安全?(线程安全的特性)
线程安全是指某个方法或某段代码,在多线程中能够正确的执行,不会出现数据不一致或数据污染的情况,我们把这样的程序称之为线程安全的,反之则为非线程安全的。
线程安全的三大特性:原子性、可见性、有序性
原子性:一个或者多个操作,要么全部执行,要么全部不执行,可以通过各种锁去保证;
可见性:线程之间的可见性,一个线程状态的修改对另一个线程是可见的,可以通过 volatile 关键字去保证;
有序性:执行的顺序按照代码的先后顺序执行;
怎么预防并发问题?
保证线程安全的方式有哪些?
保证线程安全主要是保证它的原子性、可见性和有序性
基于synchronized的同步代码块和同步方法,然后使用锁、使用并发集合、使用原子类
使用CAS:保证共享变量的线程安全
使用ThreadLocal: 线程本地变量
volatile 能保证线程安全吗?
不能,volatile 不具备原子性,当多个线程进行 i++ 操作,最后结果是不准确的
什么是死锁?
从三个方面回答
什么是死锁:
死锁是一组互相竞争资源的线程因互相等待导致“永久”阻塞的现象
发生死锁的原因 :
- 互斥条件,共享资源 X 和 Y 只能被一个线程占用
- 占有且等待,线程 T1 已经取得共享资源 X 在等待共享资源 Y 的时候 不释放共享资源 X
- 不可抢占,其他线程不能强行抢占 线程 T1 占有的资源
- 循环等待 , 线程 T1 等待线程 T2 占有的资源 , 线程 T2 等待线程 T1 占有的资源
如何避免死锁:
打破其中任意一个条件,即可避免死锁问题。
互斥条件无法被破坏;
针对 占用且等待, 可以一次性申请所有的资源 ;
针对 不可抢占, 占用部分资源的线程, 进一步申请其他资源时, 如果申请不到, 可以主动释放它占有的资源
针对 循环等待, 可以靠按序申请资源来进行预防,是指资源是有线性顺序的 申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环等待了
发生了死锁怎么解决?
排查:通过sql进行排查
show engine innodb status;
然后定位到相关代码并修改。
说一下java对象中的对象拷贝?
jdk1.8的新特性有哪些?
lamad表达式,stream流,localDate和localDateTime日期类
localDate和 Date的区别?
localDate 和 localDateTime是对java.util.Date强有力的补充,解决了 Date 类的大部分痛点:
- 非线程安全
- 时区处理麻烦
- 各种格式化、和时间计算繁琐
- 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。
stream流可以收集多次吗?
stream属于管道流,只能消费一次,当第一个stream流调用完毕方法,数据就会流转到下一个Stream上
而这时第一个stream流已经使用完毕,就会关闭了,所以第一个Stream流就不能再调用方法
stream().map()时,stream是否已经被操作了?
不是,stream流属于惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
实体类为什么要实现序列化?
标明这个类可以被序列化,然后传给前端
jdk的5个包名
java.util
java.lang
java.math
java.io
java.sql
java.time
jdbc访问数据库的步骤有哪些?
消息队列
消息推送给oa为什么不直接推送,而是走kafka?
通过异步处理提高系统性能(减少响应所需时间)
削峰/限流
降低系统耦合性
线上的消息堵塞怎么去排查?
检查系统状态或者消息队列的状态是否正常,以及看日志是否有异常等等。
怎么解决消息堵塞?
目的是为了保证消息队列的流通,可以采用限制发送速度、增加消费速度、优化发送代码、异步、消息队列扩容等方式。
怎么去提高消息队列消费的上限?
加机器,加内存,加线程数
增加代码的消费能力
锁
举几个锁,并说一下使用场景?
悲观锁乐观锁:
悲观锁认为冲突一定发生,每次拿数据时认为别人 会修改,所以每次拿数据时都会上锁,这样别人拿数据就会阻塞,直到悲观锁被释放
乐观锁认为冲突不会发生,每次拿数据的时候认为别人不会修改,所以不上锁,如果要更新数据,会先检查这段时间别人有没有修改数据,如果修改了则重新读取并尝试更新,一直循环直到成功获得锁或者更新失败线程放弃。基于的原理是CAS原理
然后悲观锁阻塞事务,乐观锁回滚重试,各有优缺点。乐观锁适用于写比较少的时候,冲突少,省去锁的开销,加大了系统的吞吐量;悲观锁适合于写比较多容易冲突的情况;
公平锁和非公平锁:
公平锁:多个线程申请一把锁,先申请的先得到;
非公平锁:后申请的可能先得到锁,随机的或者按照其他优先级排序
读写锁,共享锁,互斥锁:
读写锁(更新数据加写锁,需要读锁全部释放后才能操作),读取数据加读锁,是共享锁,其他线程也能加共享锁去读
可重入锁和不可重入锁:
不可重入锁:当前进程执行中已经获取了某个锁,如果再次获取该锁时,就会获取不到被阻塞
可重入锁:再次获取锁时可以获得,ReentranLock和synchronized都是可重入锁,加上对锁次数的统计,是自己线程获得锁,当前获取的时候次数加1
分布式锁:见下方
为什么要手动加锁?
rc隔离级别下保证数据一致性
了解锁升级吗?
synchroinzed升级过程:无锁---->偏向锁----->轻量级锁(自旋锁)----->重量级锁
具体流程:
初次执行到synchronized代码块时候,锁对象变成偏向锁,偏向于第一个获得它的线程的锁,执行完同步代码块后,线程不主动释放偏向锁,第二次到达同步代码块时,线程会判断此时持有锁的线程是否是自己,正常则往下执行,由于没有释放锁,这里不需要重新加锁,性能高
有第二个线程加入锁竞争,偏向锁升级为轻量级锁,在锁竞争下,没有获得到锁的线程自旋:不停地循环判断是否能够成功获取锁,自旋线程会原地空耗CPU,执行不了任务,处于忙等状态,如果多个线程用一个锁但是没有锁竞争或者轻微锁竞争,synchronized用轻量级锁。轻量级锁的目的是用短时间忙等换取线程在用户态和内核态切换的开销。
如果锁竞争严重,某个达到最大自旋次数的线程会升级为重量级锁,后续线程尝试获取锁时,发现被占用的是重量级锁直接将自己挂起
ReenTrantLock和Synchronized区别
首先都是可重入锁,然后他们的区别主要有几个方面,第一个是锁的实现不同,
synchronized是JVM实现的关键字
ReentrantLock是JDK实现的类
第二个是性能,
synchronized与ReentrantLock大致相同,但是新版本Java对synchronized进行了很多优化,如自旋锁
第三个是等待可中断
可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,处理其他事情
ReentrantLock可中断
synchronized不可中断
第四个是公平锁
synchronized的锁是非公平的
ReentranlLock默认非公平,也可以调为公平
第五个是可以实现选择性通知
一个ReentrantLock可以同时绑定多个Condition对象,可以指定线程信息去实现选择性通知
当时错误的回答:
说了分布式锁
面试官提示:可以结合业务场景去说
业务场景:防止用户点击多次,要保证只有一个请求能进方法里面(这里应该是同单据number下),需要加分布式锁,用单据号作为分布式锁的key,原理采用到了 ReentrantLock ,以及lua脚本去保证它的原子性
Synchronized 和 Lock 的区别?
锁的获取方式不同:synchronized是JVM实现的关键字,可以对方法块和代码加锁;Lock是juc包中的关键字,需要使用其具体的实现类进行加锁
锁的释放方式:synchronized自动释放,Lock需要手动释放
灵活度不同:Lock更为灵活,颗粒度更细,不同子类可以实现不同功能。比如公平锁和等待和中断等等。
说一下项目中用到的熟悉的技术栈
智能审核模块中用到了分布式锁
为什么要用分布式锁,而不是用java自带的锁?
分布式锁怎么保证跨服务的一个情况?
redis实现分布式锁基本的原理是什么?
项目上怎么用的分布式锁,原理是什么?
首先分布式锁是一种跨进程跨机器节点的互斥锁,可以保证在多机器节点下对共享资源的排他性,通过第三方服务比如 reids 去共享锁,保证同一时刻只能有一个实例能够获取到锁。
然后分布式锁主要使用 Redisson 去实现的,Redisson的底层逻辑是基于 lua 脚本去实现的;
如果是第一次加锁,就会在 key 对应的 hash结构中添加一个 UUID:线程标识和1,代表了该线程对这个 key加锁了一次;
并且key的过期时间默认为30秒,如果启用了 watchdog机制,就会在后台启用一个线程,该线程会去执行一个定时任务,每10秒检查一次,如果key存在,就重置key的生存时间为30秒;
并且 Redisson 也实现了可重入锁的机制,当再次加锁,会对key对应的value加1,当value为0或者宕机,锁就会释放。
分布式锁的几种实现方式?
数据库:
悲观锁:select .... for update
CSA机制:共享和交换
jdk自带的并发包:如concurrentHashMap
Redisson:
手动加锁:(一致性问题:原子性,锁过期,释放到其他线程的锁,lua脚本保证原子性)
使用 redLock:解决主从不一致问题:多节点选举,多数票通过
Zookeeper框架
获取锁:生成临时节点,如果在第一位就获取锁,不在就监听前面一位
释放锁:调用删除指令,如果意外终止,Zookeeper会释放连接断开的锁。
分布式锁用的redis的哪种数据结构?
hash结构,用来线程id+重入次数,(然后扯一下它的流程和原理)
为什么不用字符串结构?
当然用string也可以,但是就比较复杂,要用json序列化对象
分布式锁可能会失效的场景是什么?
锁丢失的几个场景:
- redis宕机
- redis数据丢失
- 锁的key过期时间到了
- 网络故障
spring框架
spring主要作用?(谈谈你对spring的理解)
Spring的两大核心功能是什么?
spring是一款轻量级的开源框架,为了解决开发的复杂性而建立的,拥有一系列的产品,比如 jdbc框架、web框架等等。
Spring核心容器的主要组件是Bean工厂(BeanFactory),Bean工厂使用控制反转模式来降低程序代码之间的耦合度,依赖注入到对象而不是创建或寻找依赖对象,并提供面向切面编程的实现。
spring核心的两个东西是 AOP 和 IOC
Spring IOC
IOC控制反转,是指创建对象的控制权被反转到Spring框架上,而不是由用户new一个对象;
然后 IOC 主要的实现方法是依赖注入(DI),,把依赖注入到对象而不是创建或寻找依赖对象;
对于IOC来说最重要的就是容器,容器管理着Bean的生命周期,控制着Bean的依赖注入。
容器的话主要有两个,BeanFactory和ApplicationContext,他们简单区别就是:BeanFactory是低级容器,延迟加载bean,编程时方式创建;ApplicationContext是 BeanFactory 的子接口,是高级容器,一次性加载bean,以声明式方式创建
Spring AOP
aop用于对代码的一个增强,或者定义一些核心的功能,比如日志管理和事务管理
主要实现方式是代理模式,详见下方
通过哪些方式可以将对象交给spring(IOC)去管理?
xml
静态代理为什么是静态代理?
aop就是用的代理实现,代理分为静态代理和动态代理
静态代理比如 AspectJ,AOP框架会在编译阶段生成AOP代理类,属于编译时的增强
动态代理比如 jdk动态代理 和 CGLIB ,AOP框架不会去修改字节码,而是每次运行时在内存临时为方法生成一个AOP对象,AOP对象包含了目标对象的全部方法,在特定切点做了增强处理,并回调原对象的方法。
当时的回答:spring使用的就是动态代理,动态代理有两个:jdk自带的和Cglib,jdk的代理效率会高一些,Cglib是动态代码生成库。
Spirng AOP 里面用到了 aspectJ 吗? 他们是一个什么关系?
他们是两个不同的框架,在一些场景下可以结合使用。
为什么要加动态代理?
Spring框架使用代理模式来实现AOP,主要是因为:
- 代理模式可以在不修改原始对象的情况下实现横切逻辑的织入,使系统更加灵活和可靠。
- 代理模式可以代理接口和普通Java类,因此可以更加方便的使用。
- 代理模式可以对原始对象的方法进行拦截,从而实现AOP中定义的通用逻辑,大大提高了系统的复用性、可维护性和可扩展性。
- 由于静态代理需要特定的编译器进行处理,所以采用动态代理。
面试官提示:可以结合一下场景来说,为什么要用动态代理?我直接用类生成不行吗?
直接用类生成需要对原有类做修改,一般适用于修改量少的情况,如果修改的类较多就会导致代码难以维护。
当时的回答:动态代理是在运行时做一个代码的...不是很清楚
两种动态代理的优劣势是什么?
- 动态代理的两个方法:JDK动态代理和CGLIB动态代理
两种动态代理效果差不多,CGLIB由于不用实现接口,会方便一些。
为什么会同时封装这两种动态代理?
使用场景不同
讲一下Spring Bean的生命周期?
Spring 中的 bean 的生命周期是指bean从创建到销毁的一个过程,主要包含四个阶段:
- 实例化 Bean
- Bean 属性赋值
- 初始化 Bean
- 销毁 Bean
(面试官如果问初始化这块,就简单说一下 aware,前后置初始化,自定义初始化这些)
初始化那块,针对所有 Bean 还是指定的 Bean?
动态代理在哪里有用到?
aop,rpc,事务管理,数据库连接池
spring声明式事务失效场景有哪些?
- 当前类没有被spring管理,就是说没有加@Controller等注解。
- 权限修饰符非 public
- 使用了 static ,final 关键字
- 抛出的异常被捕获或者不被spring的事务所支持
- 数据库本身不支持事务
- 类中方法进行直接调用(this问题)
- 方法内new一个线程,导致事务失效
嵌套事务有什么影响?
主要是不同传播级别之间的区别
方法B如果是新事物或者事务隔离,那方法A就不会回滚
Spring Cloud核心组件有哪些?
Ribbon——负载均衡
Hystrix——服务熔断
Zuul——网关
Eureka——服务注册与发现
Feign——服务调用
Config——配置中心
Mybatis框架
说一下 mybatis 的常用标签
常见的select,insert,update,delete标签;
<resultMap>,解决属性名和字段名映射问题
sql片段通过<include>标签引入;
动态sql的9个标签: trim,where,set,foreach,if,choose.when,otherwise,bind等;
mybatis中& 和 # 的区别?
springMVC框架
springMVC定义一个请求,会用到哪些注解?
RequestMapping,getmapping,postmapping,pathvaliate注解,
后端返回JSON是用什么实现的?怎么生成一个JSON,然后返回给前端
使用@RestController 注解,注意点就是返回的对象必须要继承可序列化注解才能序列化
mysql
mysql为什么要用不同的隔离级别?
由于锁竞争的存在,隔离级别越高,性能越差
默认是rr,是innoDB 用 mvcc实现的,有些公司会将隔离级别改成rc
mvcc多版本并发控制详解(了解 lvcc 和 mvcc吗?)
mvcc是 InnoDB 实现隔离级别的一种方式,用于处理并发下的读写冲突,发生冲突也可以做到读不加锁。mvcc主要是通过快照读的形式对 RC 和 RR的做一个隔离级别的实现。
实现原理:
通过Read View+ Undo Log 实现,undoLog存储所有历史快照信息,通过回滚指针把所有快照连接起来;Read View可见性规则,通过一些可见性id的字段,帮助判断当前版本的数据是否可见。
同一事务内,RC隔离级别下每次都会读取最新的快照信息,RR读取第一次查询语句的历史快照信息。
参考链接:InnoDB存储引擎对MVCC的实现
聚簇索引一个表为什么只能有一个?
由于聚簇索引是将索引和数据放在一起的,有多个的话会出现数据冗余。
隐藏点:通常主键索引就是聚簇索引,但也可以指定为非聚簇索引,这样就可以建立其他聚簇索引
索引的原理是什么?
通过数据结构进行高效查询,mysql默认为B+树,然后讲一下B+树的特点,和其他树的区别
B+和B、红黑树、hash结构的区别?
B+树的数据都存放在叶子节点上,且叶子节点本身是有序的,所以B+树的查询效率也就更高
区别:
b树的索引和数据存放在一起。树的高度比B+高,IO消耗也越多。
红黑树需要存储颜色信息,占用内存比较大,同时不支持范围查询,不适合海量数据的查询 (还有一个点:到时候再复盘一下)
hash结构等值查询是比较快的,但是不支持范围查询
为什么查询效率更高?
B+树的节点不存放数据,所以能存放更多的关键字数量,从而降低树的高度,减少IO次数
索引有哪些?
主键,唯一,联合,正常,聚簇索引
主键和唯一有什么区别?
一个表只能有一个主键,但可以有多个唯一性索引;
主键可以作为外键的关联字段,且主键的key不能为空
对哪些字段加索引?
首先要求字段的区分度比较高
具体使用场景:
在where,group by,order by,表关联中常用的字段
这些情况都比较适合加索引
什么情况索引会失效?
- 索引不在查询条件或者查询的列
- 联合索引最左列不匹配
- not in, <> 不走索引
- or 前后没有同时使用索引
- 左模糊匹配(like %在前面)
- 对于count(*)索引字段有not null约束 走索引,否则不走
- 对索引列进行函数操作
- 索引列的隐式转换
- 查询结果大于表的百分之25,优化器没必要走索引 (使用Limit限制,放redis里)
sql怎么调优?
sql本身优化,尽可能走索引,避免全表查询,减少无用数据的查询
索引优化:
尽可能走索引
建立联合索引
查询优化:
使用 Explian 关键字分析select语句
切分大查询
说一下数据库这方面怎么优化
sql,表数据,索引,数据库分区(mysql5.7出了个数据库的磁盘分区)等等
用的mysql几?
5.7
mysql5.7和8.0的区别是什么?
对 InnoDB 做了很多优化,还有就是隔离级别的不同,mysql5.7默认rr,8.0默认rc
对单表建索引,有什么要求跟限制吗?
看具体业务场景,一般在搜索常用的字段加索引,并且字段的区分度要比较高。这样在索引树上的搜索的效率会高很多。(个人感觉回答的还行)
怎么看执行计划?
通过 EXPLAIN 关键字去查看 sql 的执行计划,看是否走索引,没有走索引的原因是什么,以及哪些表的执行时间比较高,去做一个针对性优化。
实际案例是,oracle那边一段sql由于数据量比较大,而且没有走索引,解决方案是改为精确搜索,就走了索引,时间从200多秒下降到2秒多。
where 和 group by 可以用在同一个语句里面吗?
可以,where在前,group by having 在后
怎么查询慢sql?
可以通过一些工具
数据库分库分表有什么方案?
垂直,水平
mysql查看执行计划的方式有几种?
先记住 explain 关键字吧。
了解过mysql的脏读吗?
讲了下脏读,不可重复度,幻读这些,讲了下对应隔离级别
count(*) 和 count(列)的区别?
null值的区别
什么是回表操作?
讲了下聚簇索引和非聚簇索引
有null值的字段适合建立索引吗?
可以建立过滤掉null值的索引,因为无法确定null值是否有值,还是说本来就是空
左模糊查询在什么情况下索引会生效?
可以强制使用索引。
innodb 和 mysqml 的区别?
Redis
为什么要使用redis?
为什么说redis性能高?
完全基于内存
专门的数据结构
采用单线程
采用多路I/O复用模型
哪里用到了redis?
分布式锁,用户token,以及一些热点数据
击穿,雪崩,穿透
有没有在公司项目搭建过redis?
无(这里要说有,flag:搭建主从redis集群)
redis淘汰策略有哪些?默认是什么?
有很多,先不记下来
默认:
volatile-lru:基于LRU算法淘汰最近最少被使用的那些key
redis的基本数据类型有哪些?
字符串,list,hash,set,zset(跳表)
为什么用SDS重构字符串?
reids搭建了主从结构,怎么保证数据的一致性?
通过读写分离,和主从复制
分布式事务
分布式事务用到哪些组件?(待完善)
分布式事务理论:CAP理论和BASE理论
除银行项目等一些项目要求强一致性外,即CP模型,其他都是用的AP模型
组件:seata、hmily、raincat、myth等
分布式事务解决方案:2pc、3pc
当时的回答:项目上没用过,我了解分布式事务有很多组件,是使用ap协议去进行。a是高可用性,p是分区容错性。c(一致性)是通过最终一次性去保证的(面试官提示:不是问你这个,请听清楚题目啊)
cap为什么不能三个同时满足?
由于网络波动,无法保证分区容错性(P),所以无法保证CA
举了例子:如果系统出现“分区”,系统中的某个节点在进行写操作。为了保证C(一致性),必须要禁止其他节点的读写操作,这就和A发生冲突了。如果为了保证A,其他节点的读写操作正常的话,那就和C发生冲突了。
ACID 是数据库事务完整性的理论,CAP 是分布式系统设计理论,BASE 是 CAP 理论中 AP 方案的延伸。
当时的回答:由于网络波动,无法保证一致性(c)
JVM
说一下你了解的JVM
详见幕布,一整块的问题都去复习一下
jvm调优实操过吗?用的什么工具?
授权码模式的大概原理是什么?
MQ怎么实现重试机制?
固定重试或者死信队列-----日后研究
RocketMQ的确认机制了解吗?
日后研究
一个服务部署多个节点,一个节点对应一个消费者,怎么保证同一时间内只消费一条消息,或者你说一下他的一些相关的配置。
幂等性设计,通过redis或者数据库唯一键
被黑客攻击,每秒一G,持续一天,怎么解决?
多线程
线程的几种可用状态分别是什么?
新建状态NEW
可运行RUNNABLE
阻塞BLOCKED
无限等待WAITING
限期等待TIME_WAITING
终止TREMINATED
创建多线程有几种方式?
创建多线程有几种方式?
为什么阿里不推荐用Executor创建线程池?
默认最大线程数为Interge的最大值
应当自定义线程池,让开发人员更清晰线程池运行原理
说一下线程池的几个参数?
corePoolSize:核心线程数
maximumPoolSize:最大线程数
workQueue:任务等待队列
keepAliveTime:非核心线程空闲时间
threadFactory:构造Thread的方法
defaultHandler:丢弃策略
说一下线程池的拒绝策略?
AbortPolicy: 抛出异常,丢弃任务DiscardPolicy: 不抛出异常,丢弃任务DiscardOldestPolicy:丢弃最早的未处理的任务请求
CallerRunsPolicy: 调用execute的线程本身来执行,很可能造成当前线程池也被阻塞
杂项
接口性能怎么提升?
java层:代码优化
数据库层:
分库分表
数据库分区存储
表设计优化
数据量优化
sql优化
索引优化
需求层:需求是否合理
服务之间是如何调用的?
rest api形式下http的一个feign调用
rpc、grpc、消息队列等方式进行调用。
项目上用到了哪些设计模式?
策略模式,讲一下自己写的比较方法的策略模式+简单工厂模式
文章访问量、博客PV、UV怎么做的?
通过redis去统计每天的访问量,通过定时任务将访问量同步到数据库。同时将pv、uv通过日志统计的形式同步到数据库。
Linux的一些常见命令是什么?
对文件进行编辑:vi 和 vim
安装软件:yum
查看日志:cat,less,more,tail -f
权限设置: chomd
创建文件夹:mkdir,或者mkdir -p
检查网络连通性:ping
复制:cp
移动或重命名:mv
栈和队列的区别?
栈是先入后出,队列先入先出,没了???瞎扯吧
了解AQS吗?
AQS是JUC中很多同步组件的构建基础的一个抽象类,ReentrantLock等组件就是基于AQS实现的。
简单来说内部实现是状态变量state和一个FIFO队列的双向链表完成的。
同步队列头结点是当前获取到同步状态的节点,获取同步状态失败的线程,会被构造成一个节点加入到同步队列尾部(采用CAS保证此操作的线程安全),随后线程阻塞,释放时,唤醒头节点的后续节点,使其加入对同步状态的争夺中。
然后AQS有一个模板方法,使用AQS可以构建我们需要的同步组件,只需重写它的模板方法,去决定同步状态的释放和获取,背后复杂的线程排队,线程阻塞/唤醒,如何保证线程安全,都由AQS完成
AQS使用双向链表原因?
在队列同步器中,头节点是成功获取到同步状态的节点,而头节点的线程释放了同步状态后,将会唤醒其他后续节点,后继节点的线程被唤醒后需要检查自己的前驱节点是否是头节点,如果是则尝试获取同步状态。
讲一下项目上遇到的棘手的问题
先说结果:由于sql解析依赖导致的解析异常。
当时发现项目上很多接口都有一个类似的问题,控制台都会抛出sql解析异常,有的没什么,有的就导致程序无法正常运行. 通过我一系列的排查下来,终于是解决了这个问题。
空闲下来后我就尝试去解决它,先从debug的角度找问题,一直进到二进制层都没发现问题点,然后问了项目的其他同事也没什么思路,最后我发现这个错误是由一个解析包爆出来了,我就去github上找到了这个开源项目,询问了作者
最后也是知道了原因,由于依赖本身存在这个问题,解决方式也简单,要么升级这个依赖,要么在sql中规避一些本地化sql,最后由于处于运维阶段了,就采用了后者
有点简单....熟悉八股后升级下难题解决
redis的Hash和java的Hash有什么区别?
它们的实现思路很类似,主要一个区别体现在扩容方法上,hashMap通过new 一个2倍的新的 HashMap, 然后将数据重新计算hash值,并保存到到新的hashMap中。
redis的hash扩容主要是依靠两个hashTable去完成,一个hashTable用来存储数据,如果达到扩容条件后,会通过rehash方法渐进式转移数据到另一个hashTable。
(多扯点hashmap的东西,说自己比较了解hashMap)
授权码模式的大概原理是什么?
MQ怎么实现重试机制?
固定重试或者死信队列-----日后研究
RocketMQ的确认机制了解吗?
日后研究
一个服务部署多个节点,一个节点对应一个消费者,怎么保证同一时间内只消费一条消息,或者你说一下他的一些相关的配置。
幂等性设计,通过redis或者数据库唯一键
被黑客攻击,每秒一G,持续一天,怎么解决?
设计模式6大原则是什么?
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
开放-封闭原则:对扩展开放,对修改封闭
依赖倒转原则:高层模块不应该依赖底层模块,两个都应该依赖抽象。
抽象不应该依赖细节,细节应该依赖抽象。
里氏替换原则:子类型必须能够替换掉它们的父类型。
迪米特原则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。
合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承。
项目
做过的比较久的项目是什么?
费用管理系统(需要针对这个项目包装一番,什么高并发大数据jvm调优都塞进去)
说一下你负责的一些东西
首先说一下背景,这个项目主要是围绕申请单和报销单去做了一系列开发,包括预提,冲销,预时对比,预算控制等会计人员的操作。
我主要负责的模块有智能审核模块和保险索赔模块。智能审核模块是基于页面维护的规则信息,对申请单和报销单中的对应字段进行审核校验,这个模块的核心方法是比较方法,是基于不同业务场景和不同比较类型去做比较,比如金额比较、日期比较、字符串比较等。基于此我采用了设计模式中的策略模式进行实现,以及简单工厂模式对它的上下文类进行封装。另外还用到了分布式锁去保证在多机器节点下对共享资源的排他性。
保险索赔模块是相对独立的一个小模块,是基于四种角色和五种单据类型进行的一系列审批流操作。
这个系统的并发用户能达到多少?
不是很多
控制并发量的方案?
加锁,或者乐观锁,控制线程池大小,使用消息队列
聊一下策略模式?
说了下项目中用到的场景
系统的数据量多大?
蛮大的,百万级吧
怎么查询一张千万级数据量的表?
索引,分页查询,大数据切分这样
有什么是自己提出来,然后解决的业务?
讲了员工更新的bug,mybatis一级缓存导致的数据不一致问题
哪些业务做得比较好?
讲了智能审核
#java找工作##社招#