Java问题总结

2018-08-23  本文已影响20人  帅可儿妞

在网上看到一篇专门汇总面试题的文章,于是一时兴起,就把其内容整理了一番,因为原文问题和答案不完整,且混在一起,于是有了这篇Java问题总结

一、Java基础

二、Java进阶

三、JavaWeb基础

四、JavaWeb常用框架

五、Java高级框架

六、解决问题

面试及学习建议

  1. 基础->spring/springmvc/mybatis/数据库、缓存->消息中间件(activemq、kafka...)->(后面的仅代表个人看法,未知领域...)springboot、微服务、docker/分布式、zookeeper、rpc/大数据相关)

  2. 说说红黑树,它是基于什么数据结构实现的(红黑树了解不深,只是大致说了下思想-旋转、平衡、大小比较。(我感觉很难,有空得静下心来好好研究))。红黑树可能会再转为链表吗(答:长度低于8会转回链表 问:你确定是8而不是6 答:好像是6... (hashmap的看点真的很多,回去看了源码真的是6,而且扩容时红黑树变化很复杂,暂时看不懂))。说下hashmap put的过程(答:key的hashcode高16位与低16位异或运算得到新的hash,为了让数据(特别是在当前容量不大的情况下)散列更均匀,然后把异或计算出的新hash与此时的hashmap容量-1做&运算,得到插入下标。问:为什么要做&运算,还有什么方式。答:二进制运算速度快,还可以取模。之后如果下标位数组无数据则直接插入,如果有数据则链表往下逐个进行hash比较,如果产生hash碰撞再进行==或者equals比较key是否一样,一样则覆盖原数据,否则添加到链表后面。问:发生hash冲突怎么办 答:刚刚说了(重复)问:你确定是添加到链表后面吗 答:确定(我怀疑他jdk8的hashmap没看全(jdk7是新元素插入至链表头部的)还问了产生hash冲突后为什么还会比较原来key的hashcode,表示没听懂,看了源码也没发现有再次比较的过程,而且之后也重复问rehash后的元素具体去向问题(1.7和1.8元素rehash后元素去向是不同的),还有扩容问题,感觉他对jdk8的hashmap和1.7的有点混淆(还好我没怎么看jdk7,不过如果jdk7也看明白了,再说明与1.8的区别之后估计会大大加分吧,有点遗憾))。继续,当hashmap中数据量超过当前容量*扩容因子(默认0.75)则扩容为原来的2倍,问:还有什么要求 答:好像没了 问:当前插入的位置上没有元素就不扩容吧 答:哦哦。(其实不然,他说的是jdk7的情况,jdk8没有这个要求。当时要是知道7和8的区别,指出这点,那就是亮点了,再次遗憾))。为什么是2次幂扩容。(答:我觉得有3点因素。2进制运算快;hash与当前容量-1做&运算很快且很巧妙地获得元素下标;扩容后能巧妙地重新分配元素位置)说下扩容的rehash,扩容后的部分节点数据会重新定位,具体规则是hash&原容量,得到无非两个结果:0和1,如果是0则该元素所在下标位置不动,如果是1则将该元素放置原位置扩容后的对应位置(假如原先容量为16,元素位置在数组下标14的位置,则扩容后容量为32,该元素移动到数组下标30的位置(即原索引+原容量位置)。当时没这点解释的不够准确,有瑕疵)。为什么看hashmap源码,你觉得看了后对你有什么好处(答:比较喜欢探究(其实是近期面试才认真看的...)。知道了计算机位运算速度会比其它数学运算快;学习了它的思想对我思考问题方式有提升(能吹多少尽量吹多少);扩容是一个费性能的事,如果知道集合中大致会存多少元素最好给它一个初始容量),如果你知道里面会存100个左右数据,你会给它多大初始容量 (答:128) 它是线程安全的吗(答:不是,jdk1.8多线程情况下可能会造成数据丢失,1.8之前更可能造成死循环),线程安全的map有什么(答:hashtable,concurrentHashMap,前者已经被后者替代了,效率更高),说说concurrentHashMap,默认容量多少(答:16。只是了解,太复杂了,没细看,只知道jdk1.8之前采用分段锁方式处理,1.8之后采用cas乐观锁的方式来操作)。其它集合类了解吗(hashSet,treeSet,treeMap,都解释的相对明白,篇幅过长,不细说了),它们是否允许插入空值(treeSet,treeMap不可以)使用treeMap有什么需要注意的 (其中的元素要实现comparator接口)

链接:https://www.nowcoder.com/discuss/36782
来源:牛客网
一面(顺序可能有适当修改):
1、一个数组,有正有负,把正的移到右边,负的移到左边。
2、判断一个链表是否有环(我回答快慢指针,因此引出下一个问题)
3、假设一个节点为100的环形单链表,你这方法要走多少步判断出有环,99个节点呢?
4、tcp三次握手的过程?
5、进程与线程的区别?
6、说说你了解的http状态码,http请求方法?
7、简单说下银行家算法?
8、项目等相关。
9、实现一个生产者和消费者?
然后让我在房间等会,过了几分钟,二面面试官进来。

二面:
1、dubbo怎么实现异步的?(由于项目使用了)
2、dubbo底层怎么通信的?
3、进程和线程的区别?linux有进程和线程的区分吗?
4、中断和异常是什么?有什么区别?
5、tcp三次握手和四次挥手,两次握手会怎么样?
6、说说arp协议,nat协议,局域网是怎么通信的?
7、ip地址和mac地址区别?
8、虚拟内存和虚拟地址是什么?
9、死锁的必要条件?举一个死锁的例子,怎么避免死锁,怎么解决死锁?
10、hashmap、concurrenthashmap、hashtable?
11、项目,具体就不说了。
12、说下dns的过程?
13、最小堆的定义?最小堆的插入与删除的全过程?
14、红黑树的定义?为什么是平衡树?怎么维护平衡?
15、B+树的定义?为什么要用b+树,而不用平衡二叉树?
16、聚集索引和非聚集索引的区别?
17、synchronized的原理?
18、synchronized和lock的区别?
19、如果美团给了你offer,你实习公司又转正了,你还来吗?
然后带我出去,说等会不在这间房间,然后路上问我实习的部门leader是谁,卧槽,他认识我leader。很是尴尬。等了几分钟,三面面试官带我去另一个地方面试。

三面(最难,问的很深入):
1、排序算法的稳定性是指什么?
2、说说常见排序算法的稳定性?选择排序为什么不稳定?归并为什么是稳定的?
3、讲一下堆排序的全过程(这里面试官对每一个参数都会问清楚)?
4、你知道的设计模式(我回答单例,工厂,观察者,装饰者,因此引出更多问题)?
5、你会的单例(我回答懒汉,恶汉,静态内部类,枚举,双重检查)?
6、说说双重检查,volatile关键字的作用,因此也问了内存可见性和重排序?
7、说说原子类实现原理?(cas)?
8、说说cas需要哪些参数,操作系统对应的命令是什么?为什么能保证原子性?
9、volatile和cas有什么关联?什么时候可以互相替代?(一脸闷逼)
10、观察者模式有什么用?怎么实现的?
11、java类库那个是用装饰者模式(io)?
12、装饰着模式和代理模式有什么区别?(一脸懵逼)
13、给你一个64G的内存,你会怎么设置堆大小?
14、说说cms收集器的过程?
15、平时线上项目出现问题怎么排查?
16、hash冲突有哪些解决方法?简单说下再哈希法?怎么保证多个hash函数不会出现死循环?(很懵逼)
17、类加载机制?怎么破坏单例(这里应该想让我用类加载机制来破坏)?

DBMS对数据库的保护通过4个方面来实现:
数据库的恢复
数据库的并发控制
数据库的完整性控制
数据库安全性控制
DBMS中实现事务持久性的子系统是恢复管理子系统。

大多数 JVM 将内存区域划分为


事务属性的种类: 传播行为、隔离级别、只读和事务超时

a) 传播行为定义了被调用方法的事务边界。

传播行为 意义
PROPERGATION_MANDATORY 表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常
PROPAGATION_NESTED 表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和 PROPAGATION_REQUIRED 看起来没什么俩样
PROPAGATION_NEVER 表示方法不能运行在一个事务中,否则抛出异常
PROPAGATION_NOT_SUPPORTED 表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起
PROPAGATION_REQUIRED 表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务
PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起
PROPAGATION_SUPPORTS 表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中

b) 隔离级别
在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别,如下:

隔离级别 意义
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读
ISOLATION_READ_COMMITTED 允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读
ISOLATION_REPEATABLE_READ 一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读
ISOLATION_SERIALIZABLE 这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。

c) 只读

d) 事务超时


Servlet 与 CGI 的比较


Servlet的生命周期

  1. 加载:容器通过类加载器使用Servlet类对应的文件来加载Servlet
  2. 创建:通过调用Servlet的构造函数来创建一个Servlet实例
  3. 初始化:通过调用Servlet的init()方法来完成初始化工作,这个方法是在Servlet已经被创建,但在向客户端提供服务之前调用。
  4. 处理客户请求:Servlet创建后就可以处理请求,当有新的客户端请求时,Web容器都会创建一个新的线程来处理该请求。接着调用Servlet的Service()方法来响应客户端请求(Service方法会根据请求的method属性来调用doGet()和doPost())
  5. 卸载:容器在卸载Servlet之前需要调用destroy()方法,让Servlet释放其占用的资源。

Struts1和Struts2的区别和对比:


AWT & Swing


Struts工作原理


JSP文件的动静包含


常见JVM配置参数释义

常见配置汇总

  1. 堆设置
    • -Xms:初始堆大小
    • -Xmx:最大堆大小
    • -XX:NewSize=n:设置年轻代大小
    • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
    • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
    • -XX:MaxPermSize=n:设置持久代大小
  2. 收集器设置
    • -XX:+UseSerialGC:设置串行收集器
    • -XX:+UseParallelGC:设置并行收集器
    • -XX:+UseParalledlOldGC:设置并行年老代收集器
    • -XX:+UseConcMarkSweepGC:设置并发收集器
  3. 垃圾回收统计信息
    • -XX:+PrintGC
    • -XX:+PrintGCDetails
    • -XX:+PrintGCTimeStamps
    • -Xloggc:filename
  4. 并行收集器设置
    • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
    • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
    • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
  5. 并发收集器设置
    • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
    • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
image.png

-Xms -Xmx分别设置堆的最小值和最大值,如果要设置成堆的大小可变,那么可以将最大值和最小值设置成不一样,如果要将堆大小固定,那么只需将最大值和最小值设置成一样的就行。

jvm中分为堆和方法区

https://www.nowcoder.com/questionTerminal/970cdaaa4a114cbf9fef82213a7dabca

又进一步分为新生代和老年代

方法区为永久代
堆中区分的新生代和老年代是为了垃圾回收,新生代中的对象存活期一般不长,而老年代中的对象存活期较长,所以当垃圾回收器回收内存时,新生代中垃圾回收效果较好,会回收大量的内存,而老年代中回收效果较差,内存回收不会太多。

基于以上特性,新生代中一般采用复制算法,因为存活下来的对象是少数,所需要复制的对象少,而老年代对象存活多,不适合采用复制算法,一般是标记整理和标记清除算法

因为复制算法需要留出一块单独的内存空间来以备垃圾回收时复制对象使用,所以将新生代分为eden区和两个survivor区,每次使用eden和一个survivor区,另一个survivor作为备用的对象复制内存区。

综上:
-Xmn设置了新生代的大小为5120m,而-XXSurvivorRatio=3,所有将新生代共分成5分,eden占三份,survivor占两份,每份1/5


算法问题:# 二叉树中任意两个节点的最近公共祖先

思路:从根节点开始遍历,如果node1和node2中的任一个和root匹配,那么root就是最低公共祖先。 如果都不匹配,则分别递归左、右子树,如果有一个 节点出现在左子树,并且另一个节点出现在右子树,则root就是最低公共祖先. 如果两个节点都出现在左子树,则说明最低公共祖先在左子树中,否则在右子树。
代码实现

public class Solution {  
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {  
        //发现目标节点则通过返回值标记该子树发现了某个目标结点  
        if(root == null || root == p || root == q) return root;  
        //查看左子树中是否有目标结点,没有为null  
        TreeNode left = lowestCommonAncestor(root.left, p, q);  
        //查看右子树是否有目标节点,没有为null  
        TreeNode right = lowestCommonAncestor(root.right, p, q);  
        //都不为空,说明做右子树都有目标结点,则公共祖先就是本身  
        if(left!=null&&right!=null) return root;  
        //如果发现了目标节点,则继续向上标记为该目标节点  
        return left == null ? right : left;  
    }  
}
上一篇下一篇

猜你喜欢

热点阅读