(一)Java基础部分
1.Java中有哪些基本数据类型?String是基本数据类型吗?String类是否能够继承?
答:1.java定义了4中类8种基本类型:
整型:byte、short、int、long
浮点型:float、double
布尔型: boolean
字符型: char
2.String不是基本数据类型,String属于引用类型。
3.String类是一个final类,因此不能被继承。
2.简述++i和i++的区别?
答:
int i=0;
System.out.print("++i先自加1再赋值得到结果是=");
System.out.println(++i);
控制台打印:++i先自加1再赋值得到结果是=1
----------------------------------------
int i=0;
System.out.print("i++先赋值,然后再自加1得到结果=");
System.out.println(i++);
控制台打印:i++先赋值,然后再自加1得到结果=0
3.short s=1;s=s+1;有什么错?short s=1;s+=1;有什么错?
答:1.对于short s=1;s=s+1;由于short数据类型和int数据类型表示范围不一样,所以需要进行类型转换,解决方案是:要么将short改为int,要么强制类型转换(short)s+1。
2.对于 short s=1;s+=1;由于+=是Java的规定运算符,Java编译器会对它进行特殊处理,因此可以正确编译。
4.Integer和int的区别?
答:参考博客 Integer是一个封装int类型的封装类,默认值为null,int是Java中8中数据类型之一,默认值为0.
5.&和&&的区别?
答:当&运算符两边的表达式的结果都为true时,整个运算结果才为true。而&&运算符第一个表达式为false时,则结果为false,不再计算第二个表达式。
6.使用最有效的方式计算出2乘以4等于几?
答:使用位运算:2<<2
7.String s=new String(“xyz”)创建了几个对象?
答:创建2个String对象,一个是=null的s,一个是=“xyz”的string。
8. 静态变量和实例变量的区别?
答:1.在语法上的区别:静态变量前需要加static关键字,而实例变量不需要加。
2.静态变量不是某一个实例对象的属性,它属于类属性,只要程序加载类的字节码,不需要创建任何实例对象静态变量就可以分配空间从而被使用。必须先创建实例对象,实例变量才会被分配空间,才能使用这个实例变量。
3. 无论创建多少实例对象,永远只分配一个静态变量。创建一个实例对象静态变量会加1,但是每创建一个实例对象就会分配一个实例变量,每个实例变量的值只会自加1.
public class TestProblem {
static int a=0;
int b=0;
public TestProblem() {
a++;
b++;
System.out.println("静态变量数值:"+a);
System.out.println("实例变量数值:"+b);
}
public static void main(String[] args) {
TestProblem testProblem=new TestProblem();
System.out.println("------------");
TestProblem testProblem1=new TestProblem();
}
}
控制台打印结果:
静态变量数值:1
实例变量数值:1
------------
静态变量数值:2
实例变量数值:1
9.switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
答:1.switch常量表达式的值必须是整型(必须是int)或字符型。
2.在.switch表达式中,括号表达式只能是一个整型表达式或枚举常量整数表达式可以是int基本数据类型会Integer包装类型。由于byte、short、char都可以隐式转换为int,所以这些基本数据类型及其包装类型都可以。
3.long和String无法隐式转换为int类型所以不能在switch中使用。
10.简述String、StringBuffer 和 StringBuilder 区别
答:1.String和StringBuffer的区别:它们都是用于存储和操作字符串,01.String是一个final类表示的类容不可改变。StringBuffer表示的内容可以修改。02.String实现了equals()方法,StringBuffer没有。
2.StringBuffer 和 StringBuilder 区别:01.StringBuffer是线程安全的可以在多线程中使用。 StringBuilder是线程不安全的,但是运行效率非常高!
3.参考博客String、StringBuffer 和 StringBuilder 区别
11.简述自动装箱和自动拆箱
答: 参考:深入剖析Java中的装箱和拆箱
12.简述Java中实现多态的机制是什么?
答:重载(overloading)、重写(overriding)
1.重载(overloading):是一个类中多态的表现,比如一个类中定义多个同名的方法,但它们具有不同的参数或不同参数类型都称之为重载。
2.重写(overriding):子类定义一个方法和父类的方法名称参数都相同,那么父类的方法被重写。
3.参考:理解java的三大特性之多态
13.简述Java反射机制及其作用?
答:1.Reflection:是Java被视为一种动态语言的一个关键性质,这个机制允许程序在运行时通过Reflection API获取任意一个已知名称Class的内部信息。并可在运行时改变字段内容,唤起methods。
2.参考:Java反射机制详解
3.Java API:Class类中的方法
14.简述java中super()和this()、super和this的区别?
答:super()和this()区别:
super():调用父类无形参的构造方法;
super(形参):调用父类中某个带形参的构造方法;
this(形参):调用本类中另一种形式的构造方法
注意:放在方法的首行;
2.super和this的区别:
super.父类的成员变量;
super.父类的方法;
super:当子类中的成员变量、方法和父类的相同时,实现调用父类的成员变量和方法;
this:代表当前的对象;
使用的地方:若函数的形参和成员变量同名时,需要用this.成员变量名
15.try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
public class Main {
static int test(){
int x = 1;
System.out.println("111111111");
try{
System.out.println("22222222");
return x;
}finally {
System.out.println("333333333");
x = 2;
System.out.println(x);
System.out.println("444444444");
}
}
public static void main(String[] args) {
System.out.println(new Main() .test());
}
}
结果:111111111
22222222
333333333
2
444444444
1
16. 以下输出结果为
class A{
static {
System.out.println("1");
}
public A(){
System.out.println("2");
}
{
System.out.println("3");
}
}
class B extends A{
static {
System.out.println("4");
}
public B(){
System.out.println("5");
}
{
System.out.println("6");
}
}
public class Test {
public static void main(String[] args) {
new B();
}
}
1 4 3 2 6 5
(二)java集合基础面试
1.简述ArrayList和LinkedList的区别?
答:1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
4.参考:ArrayList和LinkedList的区别
2.简述HashMap的工作原理及HashMap和Hashtable的区别?
答:1.参考:HashMap的工作原理
2.参考:参考:HashMap的工作原理
3.参考:HashMap和Hashtable的区别
4.参考:深入探讨HashMap的结构实现和功能原理。
3.arrayList和vector的区别
4.List和 Map区别?
List:是存储单列数据的集合,存储的数据是有序并且是可以重复的
Map:存储双列数据的集合,通过键值对存储数据,存储的数据是无序的,Key值不能重复,value值可以重复
5.HashMap如何扩容?
参考:深入理解HashMap的扩容机制
JDK1.8之前HashMap的数据结构:数据+单向链表,默认容量16,加载因子0.75,向HashMap添加键值对时,当添加的数据个数大于等于(默认容量*加载因子),立马进行扩容。
6.HashMap底层实现原理?
参考:Java HashMap工作原理及实现
视频:阿里架构师带你分析HashMap源码实现原理
7.java中Collections.sort()和Arrays.sort()底层所采用的排序算法?
参考:Java中Collections.sort()和Arrays.sort()所采用的排序算法
(三)Java并发编程
1.Java面试题目汇总(一)
2.Java面试题目汇总(二)
3.死锁产生的条件以及如何避免死锁的发生?
参考:Java中死锁的简单例子及其避免
参考:Java 实例 - 死锁及解决方法
/**
* 一个简单的死锁类
* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而导致死锁。
*/
@Slf4j
public class DeadLock implements Runnable {
public int flag = 1;
//静态对象是类的所有对象共享的
private static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
log.info("flag:{}", flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
log.info("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
log.info("0");
}
}
}
}
public static void main(String[] args) {
DeadLock td1 = new DeadLock();
DeadLock td2 = new DeadLock();
td1.flag = 1;
td2.flag = 0;
//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
//td2的run()可能在td1的run()之前运行
new Thread(td1).start();
new Thread(td2).start();
}
}
4.CAS实现原理以及ABA问题的解决方法?
参考:详细解读 CAS 实现原理
CAS简述:
CAS(Compare And Swap/Set)比较并交换,CAS 算法的过程是这样:它包含 3 个参数CAS(V,E,N)。V 表示要更新的变量(内存值),E 表示预期值(旧的),N 表示新值。当且仅当 V 值等于 E 值时,才会将 V 的值设为 N,如果 V 值和 E 值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS 返回当前 V 的真实值。
CAS 操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS 操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
ABA问题:
CAS 会导致“ABA 问题”。CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没有问题的。
部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少。
5.讲讲你对ThreadLocal的一些理解
参考:手撕面试题ThreadLocal
6.线程池复用技术的实现原理?
参考:线程池原理浅析
参考:线程池的执行流程
参考: 深入源码分析Java线程池的实现原理
参考:线程池的实现原理、优点与风险、四种线程池实现
7.synchronized 和 和 ReentrantLock 的区别?
共同点:
不同点:
8.ReentrantLock公平锁和非公平锁的区别,可以从性能方面讲?
参考:ReentrantLock中公平锁/非公平锁详解
9.ConcurrentHashMap的底层实现原理?
参考:JAVA ConcurrentHashMap底层实现
10.谈一下volatile关键字你是怎么理解的?能否保证原子性?比较synchronized关键字不同
1.volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。
但是volatile关键字只能用于变量,而synchronized关键字可以修饰方法以及代码块。
synchronized关键字在JavaSE1.6之后进行了优化,主要包括为了减少获得锁和释放锁带来的性能
消耗而引入的偏向锁和轻量级锁以及其它各种优化,执行效率有了显著提升,实际开发中使
用 synchronized 关键字的场景还是更多一些。
2.多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞。
3.volatile关键字能保证数据的可见性,但不能保证数据的原子性。
synchronized关键字两者都能保证。
4.volatile关键字主要用于解决变量在多个线程之间的可见性,
而synchronized关键字解决的是多个线程之间访问资源的同步性。
11.谈一下乐观锁和悲观锁?
参考:面试必备乐观锁和悲观锁
12.以下程序打印结果为
class A implements Runnable{
@Override
public void run() {
System.out.println("aaaaaa");
}
}
public class Test {
public static void main(String[] args) {
A a =new A();
Thread thread = new Thread(a){
@Override
public void run() {
System.out.println("bbbbbb");
}
};
thread.start();
}
}
//打印结果为:bbbbbb
13.一下程序的输出结果?如何保证先打印1,后打印2?如何保证先打印2后打印1?
class ThreadTest{
public void test() throws InterruptedException {
Thread t = new Thread(
new Runnable() {
@Override
public void run() {
System.out.println("1");
}
}
);
t.start(); //1.保证“1”会被打印
t.join(); //2.保证“1”先打印,“2”后打印
//3.如果线程没有调用start()、join()只打印“2”
System.out.println("2");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
new ThreadTest().test();
}
}
14.控制线性执行顺序的8种方法
参考:让线程按顺序执行8种方法
(四) 数据库面试题总汇
1.讲述一下MySQL 四种隔离级别
参考:面试问烂的MySQL 四种隔离级别
参考:MySQL的四种事务隔离级别
参考:Spring事务底层原理分析全集
2.讲述一下MySQL有哪些存储引擎各自的优缺点及应用场景
参考:MySQL有哪些存储引擎,各自的优缺点,应用场景
参考:MySQL常用存储引擎
3.MySQL 如何避免索引失效?
参考:索引优化:避免索引失效
4.针对千万级别的数据表,如何优化分页查询?
参考:MySQL大数据量分页查询优化的锦囊妙计
5.说一下MySQL行锁、表锁、悲观锁、乐观锁的特点与应用场景?
参考:MySQL行锁、表锁、悲观锁、乐观锁的特点与应用
6.使用mysql索引有哪些原则?索引采用了什么数据结构?这些索引数据结构各有什么特点?
参考:MySql创建索引的原则
B+树:
磁盘读取代价更低
查询效率更加稳定
有利于对数据库的扫描
支持范围查查
Hash:
Hash索引查询速度比B+树高
只能满足“=”、“IN”不能满足范围查询
无法被用来避免数据排序操作
不能利用部分索引键查询
不能避免表扫描
遇到大量Hash 值相同的情况下性能不一定会比B+树索引高
不支持范围查查
(五) Spring面试题总汇
1.Spring AOP工作中用到哪些场景?讲述一下Spring AOP的实现原理?
AOP用于日志记录,权限验证,事务控制,性能检测,错误信息检测等
参考:SpringAOP实现事务
参考:完全读懂Spring框架之AOP实现原理
2.spring aop能否拦截类内部的方法调用?比如在类中A方法嵌套类中的B方法,B方法是否能够被AOP拦截?
参考:Spring AOP无法拦截内部方法调用
3.讲述一下Spring的IOC原理 ?
参考:Spring IOC 容器源码分析
参考:Java中高级面试题中的Spring的IOC原理
4.比较JDK动态代理和CGLib代理的区别及优缺点?
参考:Spring AOP中的JDK和CGLib动态代理哪个效率更高
5.描述一样Spring事务的7种传播行为?
参考:Spring事务管理(详解+实例)
参考:看完就明白_spring事务的7种传播行为
(六)设计模式面试题目
1.代码写出5中单例模式的实现
参考:java 单例模式5种写法
2.Spring中的常见设计模式有哪些?
Spring 中常见的设计模式
工厂模式 : BeanFactory
装饰器模式: BeanWrapper
代理模式: AopProxy
单例模式: ApplicationContext
委派模式: DispatcherServlet
策略模式: HandlerMapping
适配器模式: HandlerApdapter
模板方法模式: JdbcTemplate
观察者模式: ContextLoaderListener
Spring 的四大模块及典型的设计模式
1、Spring IOC 工厂模式、单例模式、装饰器模式
2、Spring AOP 代理模式、观察者模式
3、Spring MVC 委派模式、适配器模式
4、Spring JDBC 模板方法模式
(六) JVM面试题汇总
1.谈谈JVM内存模型
参考:深入理解JVM-内存模型
2.谈谈JMM内存模型
参考:Java内存模型
3.谈谈导致OOM的原因及解决方法
参考:高手总结的9种 OOM 常见原因及解决方案
4.JVM内存溢出的原因?内存溢出时为什么JVM没有回收?
答:死循环导致内存溢出,存在无用但可达的对象,这些对象不能被JVM回收,导致耗费内存资源
5.JVM运行时数据区包括哪几个部分?有什么作用?
6.如和判断一个对象是否存活?
引用计数算法:给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收.
缺点:无法解决对象之间循环引用导致GC收集器无法收集而产生OOM
可达性分析算法:
从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC
Roots 没有任何引用链相连时,则说明此对象不可用。
7.java中垃圾收集的算法有哪些?
1.标记-清除算法
这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被
回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不
高,标记和清除的效率都很低;2.会产生大量不连续的内存碎片,导致以后程序在
分配较大的对象时,由于没有充足的连续内存而提前触发一次 GC 动作
2.复制算法:
为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只
使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然
后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,
内存的代价太高,每次基本上都要浪费一般的内存。
于是将该算法进行了改进,内存区域不再是按照 1:1 去划分,而是将内存划分为
8:1:1 三部分,较大那份内存交 Eden 区,其余是两块较小的内存区叫 Survior 区。
每次都会优先使用 Eden 区,若 Eden 区满,就将对象复制到第二块内存区上,然
后清除 Eden 区,如果此时存活的对象太多,以至于 Survivor 不够时,会将这些对
象通过分配担保机制复制到老年代中。(java 堆又分为新生代和老年代)
3.标记-整理算法:
该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高
时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回
收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。
4.分代收集算法:
现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生
代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那
么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担
保,所以可以使用标记-整理 或者 标记-清除。
8.垃圾回收中GC Root对象有哪几种?
9.简述java内存分配策略
10.Minor GC&Full GC&Major GC区别及触发条件
参考:Minor GC&Full GC&Major GC区别及触发条件
(七)分布式系统面试题汇总
1.在分布式系统中如何保证事务一致性?
参考:十年架构师经验总结:分布式事务解决方案
2.分布式系统是如何保证幂等性
参考:分布式系统中实现幂等性的几种方式
参考:分布式幂等问题解决方案三部曲
参考:分布式系统、微服务架构的一致性和幂等性问题相关概念解析
3.分布式锁有哪些常见的实现方式?列出几种,并说明其实现思路
参考:分布式锁简单入门以及三种实现方式介绍
4.分布式系统如何解决Redis中的数据和数据库中的数据一致?
参考:数据库缓存最终一致性的四种方案
5.两个Quartz定时任务同时向数据库修改数据如何解决?
参考:分布式 集群环境下如何避免定时任务执行多次的解决方法
(八)Linux面试题总汇
1.常见日志分析命令
1.cat命令适用于打开较小的文件:
-- cat show.log
-- cat show.log -n 显示文件行号
2.more命令分页显示文件:
-- more show.log
-- Enter按键显示下行
-- F按键显示下一屏幕内容
-- B按键显示上一屏幕内容
3.less命令打开并可在文件末尾查询内容
-- less show.log
4.tail命令查看日志末尾内容
-- tail -n2 -f show.log
5.head命令查看日志文件前几行内容
-- head -n2 -f show.log
2.实时查看服务器日志文件内容
答案:tail -f access.log
3.查找进程名称为tomcat的进程ID
答案:ps -ef | grep tomcat 或 ps -aux | grep tomcat
4.查看进程号所占用的端口号
答案:netstat -nap | grep 进程pid
参考:Linux下查看进程和端口
5.查看实例(进程)启动参数(JVM参数)
答案:jsp -v
6.查看gc情况
参考:jstat命令查看jvm的GC情况
7.检查内存泄漏
参考:jmap命令使用场景
8.线上服务CPU飙高怎么排查?
(九)设计模式面试题目
参考:23种设计模式汇总整理