快手后端Java开发一面面经
面试官人很好,有引导,会对你的错误给出提示,体验很好。
编程题是放在中间的,有些问题是递进抛出来的,我这里大概整理了一下。
自我介绍
面试官那边似乎只能看到我的实习经历,看不到我的项目经历,所以就简单的走个过场。基本数据类型相关
说出java有哪几种基本数据类型。
int,long,double,float,char,boolean,short,byte。注意String不是基本数据类型
int long占多少位?int的范围。
int占32位,long占64为。int范围-2^31 ~ 2^31-1。
浮点数double如何表示0.2?
因为记不起具体的IEEE754标准了,所以我只大概回答了0.2 = 1/8 + ....这样的形式。这里附上IEEE754的wiki:
S为符号位,Exp为指数字,Fraction为有效数字。指数部分即使用所谓的偏正值形式表示,偏正值为实际的指数大小与一个固定值(64位的情况是1023)的和。采用这种方式表示的目的是简化比较。因为,指数的值可能为正也可能为负,如果采用补码表示的话,全体符号位S和Exp自身的符号位将导致不能简单的进行大小比较。正因为如此,指数部分通常采用一个无符号的正数值存储。双精度的指数部分是−1022~+1023加上1023,指数值的大小从1~2046(0(2进位全为0)和2047(2进位全为1)是特殊值)。浮点小数计算时,指数值减去偏正值将是实际的指数大小。
两个new Integer(127)使用==操作符比较的结果。
false,虽然java内置-128 ~ 127的常量池,但是两个实例在堆上分配了不同的空间,==比较的是两个对象的地址是否相等,故不相等。
Map
map有哪些,底层都是怎么实现的?扩容机制?
这里面试官不谈并发,所以我只说了HashMap,TreeMap,LinkedHashMap。
1)jdk1.8之前,HashMap使用数组+链表实现;1.8之后,当链表数组长度过长时,会将其转化为红黑树(在此之前还会检查map的容量,若容量小于64,则先扩容进行重hash)。2)LinkedHashMap则是多维护了一条双向链表。
3)TreeMap需要key实现compareTo或者提供一个Compactor比较器,TreeMap它还实现了
NavigableMap
接口和SortedMap
接口。
实现NavigableMap
接口让 TreeMap 有了对集合内元素的搜索的能力。实现SortMap
接口让 TreeMap 有了对集合中的元素根据键排序的能力。讲讲并发的map:ConcurrentHashMap HashTable底层。
HashTable是对整表加锁,现在基本被抛弃了。jdk1.8之前,ConcurrentHashMap采用分段锁(Segment),对不同段的Entry进行操作互不影响;
jdk1.8之后,ConcurrentHashMap直接使用Node数组+链表+红黑树,通过synchronized和CAS来保证并发的可靠性。并发下的hashMap会有哪些安全问题?
这里我只是简单的说了写覆盖跟不可重复读(联想到数据库了),面试官就放我过了。
添加元素时头插还是尾插?
1.7头插,1.8尾插。
并发相关
刚刚你说了CAS,来讲讲CAS。
CAS,compare and swap,在写入之前先比较对象已有值和预期值是否相同,只有对象当前值与预期值一致才赋值成功。
讲讲乐观锁、悲观锁。
乐观锁,认为其他线程不会修改值,所以不上锁,只在写入时候简单一下其他线程是否修改过。常见的实现方式有CAS跟版本号机制。
CAS前面已经提过了,他主要的缺点就是:
1)自旋消耗CPU性能。
2)ABA问题。版本号机制就是变量每次赋值时版本号+1,赋值之前先比较版本号是否一致,若不一致则代表该变量被修改过了,这能解决ABA问题。
如何保证i++多线程下正确执行?
使用AtomicInteger类的getAndIncrement方法,底层是去调用unsafe类的getAndAddInt方法,不断自旋直至成功为止。
你刚刚提到了自旋,那他会一直自旋下去吗?
不会,jvm有个参数指定了最大的自旋次数,超过这个次数线程就被挂起了。
你刚刚提到了getAndIncrement,那底层实现他用了volatile,来说说volatile的作用。
volatile主要有三特性:
1)保证内存可见性。
2)不保证原子性。
3)防止指令重排序。
在这里主要是用于保证内存可见性,当一个线程写volatile变量时,会使得其他线程的缓存行失效,其他线程必须从主存中读取最新的值。synchronized和Lock的区别。
1)synchronized是关键字,Lock是一个接口。
2)synchronized是可重入的、独占的,而Lock只是一个接口,JDK提供ReeanLock、ReadWriteLock,但你完全可以遵从Lock+AQS的规范自己实现一个锁。
3)synchronized是且只能是阻塞的,而Lock可以超时等待。
4)Lock需要手动释放锁,所以一般都会使用try...finally来确保锁的释放。synchronized上锁的几种情况。
synchronized可以锁对象也可以锁函数,锁对象比较简单这里我们讨论锁函数的情况。
1)synchronized修饰普通方***获取实例的锁。不同线程调用不同实例的同一方法不会阻塞,因为他们不会抢占一把锁。
2)synchronized修饰static方法,锁的是Class对象。不同线程即使调用同一个类的两个synchronized static方法都会阻塞,因为他们会抢占Class的锁。
3)无synchronized修饰的方法和synchronized修饰的普通方法不会互相阻塞,因为前者不需要获取锁。
4)synchronized修饰的静态/非静态方法不会抢占锁,因为他们获取的锁不同。
jvm
这里只是简单的问了一下内存划分,把pc、虚拟机栈、本地方法栈、堆、方法区这些讲清楚就好了。异常
异常的类结构,分为哪几种,各举出几个例子。
这一部分直接拉跨了,基本都是瞎答,好在老哥没为难我。
error属于异常吗?举出你常见的异常。
error不属于异常,StackOverflow和OOM是比较常见的Error。
异常又分为checked和unchecked,运行时异常都属于unchecked,不需要手动捕获,checked异常需要手动捕获。
数据库:
讲讲聚簇索引、联合索引、覆盖索引
一般的教材都说聚簇索引是指物理顺序与索引顺序一致。在《Mysql技术内幕》里面提到了,聚簇索引并非严格要求物理顺序与索引顺序一致,因为这会导致性能下降。mysql通过双向链表维护页与页之间的关联、页内数据,只要维护好双向链表实现逻辑上的顺序一致即可。
Innodb采用聚簇索引,数据文件本身就是索引文件。而MyISAM是非聚簇索引,索引和数据分离。联合索引指对多个列建立索引,只有当查询满足最左前缀匹配原则索引才生效。这里面试官还让我讲清楚不满足最左前缀为什么不生效,当时没有工具结结巴巴了半天。
假设有一联合索引(a,b,c),现有数据:(1,2,3),(2,1,4),(3,0,1)那么其在B+树中的位置应当如下:
可以发现单独使用列b显然不符合搜索树的定义,故索引会失效。
页大小,mysql是如何在某一页中找到数据的呢?
16k。页内的行记录通过双向链表连接,一开始我说错了说的是二分查找,然后面试官提醒我链表可以二分吗我才反应过来不是二分查找。
然后这里没转过来就说挨个挨个找,其实Mysql技术内幕中提到了Page Directory:
算法题
- 现在有两个很大的整数用String表示,返回两数相加之和。
逻辑题
- A,B两人约定6-7点碰面,两人只会等对方15分钟,求两人相遇的概率。
横轴表示A到的时间,纵轴表示B到的时间:
两条红线之间的面积/总面积就是相遇概率,最终结果0.4375。
- A,B两人约定6-7点碰面,两人只会等对方15分钟,求两人相遇的概率。