个人Java面试常见题目记录
Java高频面试问题总结
下面是本人在面试Java实习的过程中碰到的一些面试题,当然,只是自己记得的印象较深的一些题,还有很多的其他的技术问题,也包括一些非技术问题,后续还会继续记录,主要方向:
- JVM参数配置、常用调试命令和工具、内存分区、类加载机制,CPU死循环解决方法
- Java并发包常用工具用法和原理、常见集合类原理和用法、volatile和CAS原理等
- MySQL索引存储结构、索引搜索原理、索引原则,事务隔离级别和原理,redis通信机制原理、数据结构及原理(zset)、两种持久化方式原理及使用场景、缓存穿透击穿和雪崩问题和应对方法,ES的倒排索引和分片原理等
- 框架常考知识,如AOP原理、自己如何实现?springboot原理、springmvc原理、mybatis原理?有看过源码吗?框架中使用了哪些设计模式等等
- 微服务治理相关,如dubbo、springcloud等。
Java基础
包括JavaSE、多线程、并发、集合和JVM等常见问题
(1)java中有哪些基本数据类型?
-
整数类型:byte short int long
-
浮点数类型:float double
-
字符型:char
-
布尔型:boolean
-
注意:java还为每一种基本类型提供了相应的包装类,基本类型与包装类型的主要区别在于1、包装类型允许值为空,而基本类型不允许为空;2、包装类型是一个对象,需要消耗更多的内存,也给GC带来了更大的压力,所以在性能上稍差一截。
(2)如何控制多个线程按一定顺序执行?
- 1 使用join()方法,伪代码如下:
Thread thread1 = new Thread(new Runnable(){
@Override
public void run(){
//自定义代码块
}
})
Thread thread2 = new Thread(new Runnable(){
@Override
public void run(){
threa1.join() //保证该线程在thread1之后执行
//自定义代码块
}
})
- 2 使用对象的wait()和notify()方法
- 3 使用线程池的submit()方法,伪代码如下:
ThreadPoolExecutor threadpool = new ThreadPoolExecutor(各个初始参数);
Thread thread1 = new Thread(线程对象参数);
Thread thread2 = new Thread(线程对象参数);
Thread thread3 = new Thread(线程对象参数);
threadpool.submit(thread1);
threadpool.submit(thread2);
threadpool.submit(thread3);
- 4 使用ReentrantLock结合condition控制线程顺序执行
- 5 使用CountDownLatch(递减,只能使用一次)或者CyclicBarrier(递增,可重复利用)
- 6 使用Semaphone信号量控制线程顺序执行
(3)创建线程有哪几种方式?
- 1 继承Thread类并重写run()方法
- 2 实现Runnable接口并重写run()方法
- 3 实现 Callable接口并重写call()方法
- 4 使用线程池的方式
(4)实现线程安全的方式有哪些?线程池有使用过吗,怎么使用的?
- 1 使用synchronized关键字(包括同步方法和同步代码块)
- 2 使用锁(ReentrantLock)
- 3 使用volatile关键字
- 4 使用原子类
- 线程池的创建建议使用ThreadPoolExecutor来创建,这样能够更好地管理线程避免线程资源耗尽,其中需要设置的参数有核心线程数、最大线程数、最大空闲时间、最大空闲时间单位、缓冲队列等,各项参数具体地配置需要根据系统机器配置和具体面对地业务场景来配置。
(5)同一个类中,普通方法和静态方法在获取锁上有什么区别?
- 普通方法指向的锁是this,亦即对当前类上锁
- 静态方法指向的锁是this.class,亦即对当前类的实例上锁
(6)volatile和synchronized的区别
- volatile只能用于修饰变量,而synchronized用于修饰方法或代码块
- volatile的作用是为了解决共享变量对于多线程在内存中的可见性问题,无法解决同步问题;而synchronized不仅可以保证可见性,还能保证原子性,因为只有获取到锁的线程才能够进入临界区才能获得资源,保证了同一时刻只能有一个线程占有资源,其他想要获取该资源的线程则会被阻塞
(7)聊聊垃圾回收算法?
- 在聊垃圾回收算法之前,先介绍下JVM是如何识别垃圾的。最简单的一种垃圾识别方式是引用计数法,即一个对象每被引用一次就在该对象头上计数+1,如果引用计数为0则说明没有被引用,那么就可以回收该垃圾对象,但是引用计数法有一个缺点就是无法解决循环引用问题;为了解决循环引用问题,现代虚拟机都是采用可达性算法来识别垃圾对象,可达性算法的原理是以一系列叫做GC Root的对象为起点出发,引出它们指向的下一个节点,再以下一个节点为起点引出此节点的下一个节点......这样直到所有的节点都遍历完毕组成一条或多条引用链,如果相关对象不在任何一条以GC Root为起点的链上,那么这些对象就会被识别为垃圾,从而可以被GC回收。(注:GC Root指的是虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象和本地方法中JNI引用的对象都可以作为GC Root)
- 常用的垃圾回收算法有标记清除法、复制算法、标记整理算法和分代收集算法;
- 标记清除算法是最简单的,分为垃圾标记阶段和清除阶段,它的优点是简单高效易理解,但是缺点也很明显,就是容易产生大量的空间碎片,大大降低了空间的利用率;
- 复制算法为了解决上面的垃圾碎片问题,将为对象分配空间的原始内存分为两部分,一部分用于给对象分配空间,另一部分用来解决垃圾回收后产生的碎片问题。即一次垃圾回收后,将剩下的对象全部移到另外一块内存中,那么就可以保证原来那块内存是没有碎片空间的。这种方法虽然解决了碎片空间的问题,但是让原本可用的内存空间减半了。
- 标记整理算法分为标记、清除和压缩三个阶段,该算法会使用一个异步线程用于在每次垃圾回收后整理内存空间中留下来的对象,将它们整理到内存的一端,这样就解决了碎片空间的问题;
- 分代回收算法是根据Java对象的特点将给Java对象分配空间的堆内存分为新生代和老年代,然后新生代中使用复制算法,而老年代中使用标记整理算法;
(8)讲一讲你所熟悉的垃圾回收器有哪些?
(9)什么时候会触发GC?
- 当堆中的新生代中的Eden区没有足够的空间为新的对象分配内存的时候会触发MinorGC
- 当新生代中的S0和S1中相同年龄的对象所占内存超过S0和S1总内存的一半的时候会触发MinorGC
- 当老年代中无法为S1中的对象腾出新的内存的时候会触发fullGC
- 当老年代中无法为新的大对象分配内存的时候会触发fullGC
(10)讲一下Java中类加载机制是怎么样的?
(11)你用过的JVM命令有哪些?JVM参数调优和GC调优有做过吗?碰到了什么问题,怎么做的?
框架和中间件
包括Netty、Redis、kafka、spring、springboot等框架和中间件相关的常见问题
(1)redis了解吗,它的应用场景有哪些?
- redis是一个高性能的内存K-V数据库,它是基于C语言实现的;
- 由于它的底层采用了单线程的文件事件处理器机制,因此它是单核内存数据库,但是它是天然支持集群的;
- redis的通信机制使用了IO多路复用机制,因此它能够以单线程的情况处理大量的客户端连接和命令执行请求;
- redis常用于做缓存、消息队列、分布式锁;
(2)redis有哪几种数据结构?用过哪几种,怎么用的?
- redis的数据结构有string、list、hash、set、zset
- 具体用了哪些数据结构以及如何用的要结合业务场景来描述
(3)redis持久化有哪几种方式?它们的区别是什么?你是如何使用的?
- redis持久化有快照持久化RDB和文件追加持久化AOF;
- 区别可以从对系统性能的影响、可靠性、故障恢复效率等几个方面来讨论分析;
- 现在redis的使用一般采用主从结构、哨兵结构或集群结构,而持久化方式一般也是两种同时开启的,一般主节点只开启RDB持久化,而从节点开启AOF持久化,这样两种方式结合使用,保证系统数据可靠性的同时由尽量少的影响系统性能;
(4)redis集群又搭建过吗?说说如何做的?
(5)redis集群中主节点挂了是如何自动切换的?节点之间的通信时怎么样的?节点间的数据一致性如何保证的?
(6)spring了解吗?它的核心模块由哪些?说说你对AOP和IOC的理解
- spring是一个轻量级的Java web开发框架,它的目的时为了简化J2ee开发
- 核心模块主要如有:DATA ACCESS、web、core container、AOP、TEST、BEAN等
- AOP叫做面向切面编程,它不是spring或Java独有的,它是一种编程思想,其主要思想是将项目业务中公共的却又不影响业务代码的逻辑部分抽出来做统一处理,从而减少系统代码重复(如事务管理、日志管理等)。AOP的实现是通过Java动态代理实现的;
- IOC的字面意思是控制反转,它不是什么技术,而是一种设计思想。即将程序中需要原来手动创建依赖对象的过程或权力交给spring来管理,让spring统一来管理对象间的依赖,这样我们使用的时候直接通过配置或注解就可以从IOC中拿来使用。IOC容器就是一个map,用来存储对象和对象间的依赖关系;
(7)简述一下你对spring mvc的理解?
(8)spring boot 有用过吗?它是怎么运行的有了解过吗?
(9)RPC框架有用过吗?Netty有了解过吗,说是它的优势和实现原理?
**(10)常用的Linux命令有哪些?
数据结构及计算机网络
包括数据结构和常用算法、计算机网络基础知识、常用网络协议TCP、HTTP等相关知识问题
数据库
(1)如何优化数据库性能?
个人认为,优化数据库的性能的目的主要是为了提高SQL响应速度。所以,数据库性能的优化可以从下面的步骤进行:
- 1 首先是找到那些SQL响应慢的语句,然后检查是否可以在程序代码中做优化;
- 2 通过数据库的一些检测工具检查SQL慢的原因,如慢查询日志、show status、show global status、show profiles、explain等工具。然后根据检测结果来一一排查问题
- 3 考虑是否系统中的缓存是否部分失效或挂掉,系统中是否存在外部调用延时从而导致SQL响应时间变慢
- 4 考虑是否可以通过创建合适的索引来提升查询效率
- 5 数据表的结构是否可以做进一步的优化,如数据字段类型优化,垂直分表等
- 6 数据库系统的参数配置是否合理,操作系统的配置是否合理
- 7 服务器的性能是否受到其他程序的影响
- 8 如果是由于数据表数据量过大造成的,那么是否可以考虑水平分表,分库,分布式集群等。
(2)数据库中常见的索引有哪些?建索引有哪些好处?建索引的建议和原则了解吗?
- 1 按照索引的数据结构来划分的话有:B-tree索引及其变体、hash索引、R-tree索引、K-D树索引、全文索引和倒排索引等
- 2 对于使用基于B+tree的Innodb引擎的MySQL来说有如下索引:主键索引、唯一索引、联合索引(包含多个列的索引)、前缀索引、后缀索引、覆盖索引及自适应哈希索引。
- 3 索引可以提高查询效率,减少磁盘IO,化随机IO为顺序IO
- 4 索引创建的原则和建议有......
(3)聚簇索引和非聚簇索引的区别是什么?
- 聚簇索引根据索引列的值来有序地紧凑存储数据行记录,这意味着通过聚簇索引列来查询数据是非常快的,因为不需要做回表查询就可以直接拿到整行记录的所有值;
- 非聚簇索引则只存储该索引列的键值,所以如果使用非聚簇索引来查找行记录,那么首先能拿到只有该索引列的键值,然后再回表查询其余列的值,进而得到整行记录的所有值;
- 在MyIsam存储引擎中,是不存在聚簇索引的,即使是主键索引也不属于聚簇索引,主键索引和其他索引没什么区别,只是名字不同罢了。MyIsam存储引擎底层使用B-Tree实现索引,它的每个索引节点都存储了指向对应索引列键值的指针作为节点的值(也就是说,节点只缓存索引,真正的数据需要通过操作系统根据指针调用来找到)所以它需要两次索引才能找到找到需要查找的记录;
- 在Inodb中,支持以主键为索引列的聚簇索引,并且一张表只能有一个聚簇索引(所以一张表只能有一个主键)。建议使用自增的列作为主键,这样在数据表插入数据的时候才能有序插入到内存页从而有序的写入到磁盘,在取数据的时候才能按顺序从磁盘加载到内存中,即将随机IO变为了顺序IO;当然,还建议主键列的字段应该不要太大,因为越小,则一个数据页能存储的数据就越多,从而可以减小IO,进而提高查询效率。
算法
- 有一个整数数组,里面的整数都是成对出现的,但是只有一个数字是只出现一次的。如何快速地找出这个数字?
解答: