“北科Java面试宝典(211最详细讲解)“

Version : V1.0

  • 北科Java面试宝典
    • 一、Java基础面试题【24道】
    • 二、JVM虚拟机面试题【14道】
    • 三、集合相关面试题【17道】
    • 四、多线程 【25道】
    • 五、IO【5道】
    • 六、网络编程 【9道】
    • 七、MySQL以及SQL面试题【20道】
    • 八、常用框架【19道】
    • 九、中间件和分布式 【54道】
    • 十、设计模式面试 【2道】
    • 十一、 数据结构 【19道】
    • 十二、 工具使用 【3道】

“北科Java面试宝典(211最详细讲解)“_第1张图片

北科Java面试宝典

一、Java基础面试题【24道】

  1. 聊一聊Java平台的理解!
    Java 本身是一种面向对象的语言,最显著的特性有两个方面,一是所谓的“书写一次,到处运行”,能够
    非常容易地获得跨平台能力;另外就是垃圾收集,Java 通过垃圾收集器回收分配内存,大部分情况下,
    程序员不需要自己操心内存的分配和回收。 一次编译、到处运行”说的是Java语言跨平台的特性,Java的
    跨平台特性与Java虚拟机的存在密不可分,可在不同的环境中运行。比如说Windows平台和Linux平台
    都有相应的JDK,安装好JDK后也就有了Java语言的运行环境。其实Java语言本身与其他的编程语言没有
    特别大的差异,并不是说Java语言可以跨平台,而是在不同的平台都有可以让Java语言运行的环境而
    已,所以才有了Java一次编译,到处运行这样的效果。 Java具有三大特性: 封装,继承,多态.利用这三大特
    性增加代码的复用率和灵活度.
  2. String、StringBuffer、StringBuilder的区别!
    String的典型的不可变类型,内部使用final关键字修饰,所以每次进行字符串操作(拼接,截取等)都
    会产生新的对象!在开发中,会使用到大量的字符串操作!所以字符串的临时对象,会对程序操作较大
    的性能开销!产生了大量的内存浪费!
    StringBuilder比StringBuffer速度快, StringBuffer比String速度快
    StringBuffer线程安全
  3. int和Integer的区别!
    int是基本数据类型,是Java的8个原始数据类型之一,直接存数值。
    Integer是int对应的包装类,在拆箱和装箱中java 可以根据上下文,自动进行转换,极大地简化了相关
    编程。
    int是基本类型,Integer是对象,用一个引用指向这个对象。由于Integer是一个对象,在JVM中对象需
    要一定的数据结构进行描述,相比int而言,其占用的内存更大一些。
  4. == 和 equals 的区别是什么?
    是直接比较的两个对象的堆内存地址,如果相等,则说明这两个引用实际是指向同一个对象地址的。
    因此基本数据类型和String常量是可以直接通过
    来直接比较的。
    对于引用对象对比是否相等使用equals方法,equals方法比较的是对象中的值是否相等, 但是要从写
    hashcode和equals
  5. 聊一聊JDK1.8版本的新特性!
    拉姆达表达式
    函数式接口,
    方法引用
    新的日期类LocalDate
    引入了stream类,支持链式编程
  6. final关键字的作用?
    final关键字可以用来修饰变量、方法和类。
    被final修饰的类该类成为最终类,无法被继承。
    被final修饰的方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。
    final修饰类中的属性或者变量
    无论属性是基本类型还是引用类型,final所起的作用都是变量里面存放的“值”不能变。
    对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用
    类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  7. 什么是内存泄漏和内存溢出!
    内存泄漏(memoryleak),是指应用程序在申请内存后,无法释放已经申请的内存空间,一次内存泄漏危
    害可以忽略,但如果任其发展最终会导致内存溢出(outofmemory)。如读取 文件后流要进行及时的
    关闭以及对数据库连接的释放。
    内存溢出(outofmemory)是指应用程序在申请内存时,没有足够的内存空间供其使用。如我们在项目中
    对于大批量数据的导入,采用分批量提交的方式。
  8. 抽象类和接口的区别!
    抽象类 : 抽象类必须用 abstract 修饰,子类必须实现抽象类中的抽象方法,如果有未实现的,那么子类
    也必须用 abstract 修饰。抽象类默认的权限修饰符为 public,可以定义为 public 或 procted,如果定
    义为 private,那么子类则无法继承。抽象类不能创建对象
    接口和抽象类的区别 : 抽象类只能继承一次,但是可以实现多个接口
    接口和抽象类必须实现其中所有的方法,抽象类中如果有未实现的抽象方法,那么子类也需要定义为抽
    象类。抽象类中可以有非抽象的方法
    接口中的变量必须用 public static final 修饰,并且需要给出初始值。所以实现类不能重新定义,也不能
    改变其值。
    接口中的方法默认是 public abstract,也只能是这个类型。不能是 static,接口中的方法也不允许子类
    覆写,抽象类中允许有static 的方法
  9. Error和Execption的区别?
    Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出
    (throw)或者捕获(catch),它是异常处理机制的基本组成类型。
    Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。Exception 是程序正常运行中,可
    以预料的意外情况,可能并且应该被捕获,进行相应处理。
    Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM自身)处于
    非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如
    OutOfMemoryError 之类,都是 Error 的子类。
    Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常
  10. 说一说常见的Execption和解决方案!
    数组越界异常 : Java.lang.ArrayIndexOutofBoundsException
    产生的原因:访问了不存在的索引
    解决的办法:索引0到数组长度-1的范围内取值
    空指针异常 : Java.lang.NullPointerException
    产生的原因:对象没有创建就访问了元素或者方法或者属性
    解决的办法: 先找出出现的所有引用类型,判断哪个对象是没有new的元素或者方法或者属性,如
    果没有就创建该对象
    没有这样的元素异常 : Java.util.NoSuchElementException
    产生的原因:在迭代器迭代的时候没有下一个元素了
    解决的办法:在迭代器之前做相对应得判断,如果没有元素了就不迭代输出了
    并发修改异常 : Java.util.ConcurrentModificationException
    产生的原因:在迭代器迭代的同时使用集合修改元素
    解决的办法:使用普通for循环来遍历 , 使用toArray来遍历 , 使用ListIterator来遍历
    类型转换异常 : Java.lang.ClassCastException
    产生的原因:在向下转型的过程中,没有转换成真实的类型
    解决的方法:在向下转型之前使用instanceof关键字对所有子类做逐一判断
    算法出错异常 : Java.lang.ArithmeticException
    产生的原因:除数不能为零
    解决的办法:改变除数的结果再进行测试
    没有序列化异常 : Java.io.NotSerialzableException
    产生的原因:没有实现serializable接口
    解决的办法:对需要的写入到文件的类实现serializable接口,表示允许该类的该类写入到文件
  11. 重写和重载区别
    重写: 子类继承父类, 在子类中存在和父类中一模一样的方法, 从新编写方法中的实现业务代码.
    重载: 在同一个类中存在多个方法名相同, 传参个数,顺序和类型不同, 返回值可以相同也可以不同的方法.
  12. 什么是反射, 反射的好处?
    Java 反射,就是在运行状态中:
    获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
    获取任意对象的属性,并且能改变对象的属性
    调用任意对象的方法
    判断任意一个对象所属的类
    实例化任意一个类的对象
    Java 的动态就体现在这。通过反射我们可以实现动态装配,降低代码的耦合度;动态代理等。反射的过
    度使用会严重消耗系统资源。
  13. 事务控制在哪一层? 可否控制在dao层? 为什么?
    事务必须控制在service层, 不可以在dao层.
    因为dao层需要按照单一职责原则设计, 一个类对应一张表, 类中最好都是单表增删改查, 增加代码复
    用率而事务具有隔离性, service会调用多个dao方法组装业务, 如果事务控制在dao层就会被分隔成
    多个事务, 无法整体控制
    所以事务必须控制在service层, 保证一套业务操作要么全成功, 要么全失败.
  14. Cookie和session的区别和联系?
    Cookie是客户端浏览器用来保存数据的一块空间, cookie没有session安全, cookie中保存的数据量
    有大小限制
    Session是服务器端用来保存数据的一块空间, session比较安全
    Cookie和session联系:
    当用户第一次发送请求访问网站的时候, cookie中没有信息, 这个时候服务器就会生成一个session
    对象, 这个session对象有个唯一id叫做sessionID, 这个id会被保存到用户的浏览器cookie中.
    当用户再次访问服务器的时候, cookie中就会有sessionID, 会被带到服务器, 服务器会根据这个id找
    到对应的session对象使用.
    如果服务器没有找到对应的session对象则会新生成一个session对象, 然后将新的session对象的id
    再次写入到cookie中保存.
  15. —个线程两次调用start。方法会出现什么情况?
    Java的线程是不允许启动两次的,第二次调用必然会抛出legalThreadStateException,这是一种运行时
    异常,多次调用 start 被认为是编程错误。
  16. 谈谈final、finally、 finalize有什么不同!
    final 可以用来修饰类、方法、变量,分别有不同的意义,final 修饰的 class 代表不可以继承扩展,final
    的变量是不可以修改的,而 final 的方法也是不可以重写的(override)。
    finally 则是 Java 保证重点代码一定要被执行的一种机制。我们可以使用 try-finally 或者 trycatch-finally
    来进行类似关闭 JDBC 连接、保证 unlock 锁等动作。
    finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源
    的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为deprecated。
  17. 强引用、软引用、弱引用、幻象引用有什么区别!
    所谓强引用(“Strong” Reference) :
    就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收
    集器不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域
    或者显式地将相(强)引用赋值为 null,就是可以被垃圾收集的了,当然具体回收时机还是要看垃
    圾收集策略。
    软引用(SoftReference) :
    是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当 JVM 认为内存不足时,
    才会去试图回收软引用指向的对象。JVM 会确保在抛出OutOfMemoryError 之前,清理软引用指
    向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内
    存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。
    弱引用(WeakReference):
    并不能使对象豁免垃圾收集,仅仅是提供一种访问在弱引用状态下对象的途径。这就可以用来构建
    一种没有特定约束的关系,比如,维护一种非强制性的映射关系,如果试图获取时对象还在,就使
    用它,否则重现实例化。它同样是很多缓存实现的选择。
    幻象引用 :
    有时候也翻译成虚引用,你不能通过它访问对象。幻象引用仅仅是提供了一种确保对象被 finalize
    以后,做某些事情的机制,比如,通常用来做所谓的 Post-Mortem 清理机制,我在专栏上一讲中
    介绍的 Java 平台自身 Cleaner 机制等,也有人利用幻象引用监控对象的创建和销毁。
  18. 父子类静态代码块, 非静态代码块, 构造方法执行顺序
    父类 - 静态代码块
    子类 - 静态代码块
    父类 - 非静态代码
    父类 - 构造函数
    子类 - 非静态代码
    子类 - 构造函数
  19. 如何实现对象克隆
    实现 Cloneable 接口并重写 Object 类中的 clone()方法;
    实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
  20. 什么是 java 序列化,如何实现 java 序列化?
    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对
    象进行读
    写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的
    问题。
    序 列 化 的 实 现 : 将 需 要 被 序 列 化 的 类 实 现 Serializable 接 口
  21. 深拷贝和浅拷贝区别是什么?
    浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值
    都会随之变化,这就是浅拷贝
    深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝
  22. jsp 和 servlet 有什么区别?
    jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别Java的类,不能识别JSP的代码,
    Web容器将JSP的代码编译成JVM能够识别的Java类)
    jsp更擅长表现于页面显示,servlet更擅长于逻辑控制。
  23. 说一下 jsp 的 4 种作用域?
    JSP中的四种作用域包括page、request、session和application:
    page : 代表与一个页面相关的对象和属性。
    request : 代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉
    及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
    session : 代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该
    放在用户自己的session中。
    application : 代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包
    括多个页面、请求和会话的一个全局作用域。
  24. 请求转发(forward)和重定向(redirect)的区别?
    请求转发forward比重定向redirect快
    请求转发的url地址只能是本网站内部地址, 重定向的url地址可以是外部网站地址
    请求转发浏览器url不发生变化, 重定向浏览器url地址发生改变.
    请求转发request域中的数据可以带到转发后的方法中, 重定向request域中的数据无法带到重定向后的
    方法中.

二、JVM虚拟机面试题【14道】

  1. JDK1.7、1.8的JVM内存模型,哪些区域可能发生OOM(out of memory)内存溢出!
    Out of memory: 堆内存溢出
    JDK 1.7:有永久代,但已经把字符串常量池、静态变量,存放在堆上。逐渐的减少永久代的使用。
    JDK 1.8:无永久代,运行时常量池、类常量池,都保存在元空间。但字符串常量池仍然存放在堆上。
    JVM 内存区域划分图 :
    内存溢出通俗的讲就是内存不够用了,并且 GC 通过垃圾回收也无法提供更多的内存。实际上除了程序
    计数器,其他区域都有可能发生 OOM, 简单总结如下:
    【1】堆内存不足是最常见的 OOM 原因之一,抛出错误信息
    java.lang.OutOfMemoryError:Java heap space
    原因也不尽相同,可能是内存泄漏,也有可能是堆的大小设置不合理。
    【2】对于虚拟机栈和本地方法栈,导致 OOM 一般为对方法自身不断的递归调用,且 没有结束点,导
    致不断的压栈操作。类似这种情况,JVM 实际会抛出 StackOverFlowError , 但是如果 JVM 试图去拓展栈
    空间的时候,就会抛出 OOM.
    【3】对于老版的 JDK, 因为永久代大小是有限的,并且 JVM 对老年代的内存回收非常不积极,所以当我
    们添加新的对象,老年代发生 OOM 的情况也非常常见。
    【4】随着元数据区的引入,方法区内存已经不再那么窘迫,所以相应的 OOM 有所改 观,出现 OOM,
    异常信息则变成了:“java.lang.OutOfMemoryError: Metaspace”。
  2. 请介绍类加载过程和双亲委派模型!
    类加载过程 : 加载 -> 连接 -> 验证 -> 准备 -> 解析 -> 初始化
    双亲委派模型: 先找父类加载器, 让父类加载器加载, 如果父类加载器无法加载就让子类加载器加载.这个
    加载过程叫做双亲委派模型或者双亲委派机制.
  3. 谈一谈堆和栈的区别!
    栈: 存储局部变量, 基本类型数据, 动态链接(堆中对象的内存地址)
    堆: 凡是new出来的对象都在堆中, 也就是存储数组和对象,垃圾回收器不定期到堆中收取垃圾.
  4. 说一说jvm常见垃圾回收器和特点!
    按照内存空间来划分,年轻代的垃圾回收器有:
    Serial:是一个串行的垃圾回收器
    ParNew:是Serial的并行版本
    Parallel Scavenge:也是一个并行的垃圾回收器,区别在于它注重吞吐量
    年轻代的三个垃圾回收器采用的垃圾回收算法都是复制算法,所以在进行垃圾回收时都会暂停所有的用
    户线程;
    然后是老年代的垃圾回收器:
    Serial Old:是Serial的老年代版本,串行,采用的是标记-整理算法,同样会STW
    Parallel Old:是Parallel Scavenge的老年代版本,并行,采用的是标记-整理算法,会STW,注重
    吞吐量
    CMS:注重低延迟,采用的是标记-清除算法,分为四个阶段:初始标记、并发标记、重新标记、
    并发清除;在初始化和重新标记阶段中是并行的,会STW,其余两个阶段都是并发执行与用户线程
    同时执行;由于采用标记清理算法,会产生空间碎片
    最后是整堆收集器:G1收集器,G1收集器的特点有:能够独立管理整个堆空间、可利用多CPU、多核的
    硬件优势缩短STW的时间、采用分代收集算法不会产生空间碎片
  5. Java创建对象的过程!
    对象的创建过程一般是从new指令开始的,JVM首先会对符号引用进行解析,解析完毕后JVM会为对象在
    堆中分配内存,之后,JVM会将该内存进行零值初始化。最后,JVM会调用对象的构造函数。此时,一般
    会有一个引用指向这个对象,将对象的地址值赋值给变量。在Java对象初始化过程中,主要涉及三种执
    行对象初始化的结构,分别是 实例变量初始化、实例代码块初始化 以及 构造函数初始化。
  6. Java中垃圾回收机制
    垃圾回收机制用到finalize。当程序创建对象、数组等引用类型实体时,系统都会在堆内存中为之分
    配一块内存区,对象就保存在这块内存中,当这块内存不再被任何引用变量引用时,这块内存就会
    变成垃圾,等待垃圾回收机制进行回收。
    分析对象是否为垃圾的方法 : 可达性分析法, 引用计数法两种
    强制垃圾回收 : 当一个对象失去引用后,系统何时调用它的finalize ()方法对它进行资源清理,何时
    它会变成不可达状态,系统何时回收它所占有的内存。对于系统程序完全透明。程序只能控制一个
    对象任何不再被任何引用变量引用,绝不能控制它何时被回收。强制只是建议系统立即进行垃圾回
    收,系统完全有可能并不立即进行垃圾回收,垃圾回收机制也不会对程序的建议置之不理:垃圾回收
    机制会在收到通知后,尽快进行垃圾回收。
    强制垃圾回收的两个方法 : 调用System类的gc()静态方法System.gc(), 调用Runtime对象的gc()实例
    方法:Runtime.getRuntime().gc()方法
    垃圾回收基本算法有四种 : 引用计数法, 标记清除法, 标记压缩法, 复制算法
    垃圾回收复合算法 – 分代收集算法:
    当前虚拟机的垃圾收集都采用分代收集算法,这种算法就是根据具体的情况选择具体的垃圾回收算
    法。一般将java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集
    算法。
    比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的
    复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对
    它进行分配担保,所以我们必须选择“标记-清除”或“标记-压缩”算法进行垃圾收集。
  7. Java中垃圾回收算法
    (1)标记-清除算法:使用可达性分析算法标记内存中的垃圾对象,并直接清理,容易造成空间碎片
    (2)标记-压缩算法:在标记-清除算法的基础上,将存活的对象整理在一起保证空间的连续性
    (3)复制算法:将内存空间划分成等大的两块区域,from和to只用其中的一块区域,收集垃圾时将存
    活的对象复制到另一块区域中,并清空使用的区域;解决了空间碎片的问题,但是空间的利用率低
    (4)复合算法 - 分代收集算法:是对前三种算法的整合,将内存空间分为老年代和新生代,新生代中存
    储一些生命周期短的对象,使用复制算法;而老年代中对象的生命周期长,则使用标记-清除或标记-整
    理算法。
  8. YoungGC和FullGC触发时机?
    Young GC :Young GC其实一般就是在新生代的Eden区域满了之后就会触发,采用复制算法来回
    收新生代的垃圾。
    Full GC : 调用System.gc()时。系统建议执行Full GC,但是不必然执行
    触发时机 :
    老年代空间不足
    方法区空间不足
    进入老年代的平均大小大于老年代的可用内存
    由Eden区,幸存者0区向幸存者1区复制时,对象大小大于1区可用内存,则把该对象转存到
    老年代,且老年代的可用内存大小小于该对象大小。
    注意:full GC是开发或调优中尽量要避免的,这样STW会短一些
  9. FullGC触发可能产生什么问题
    fullgc的时候除gc线程外的所有用户线程处于暂停状态,也就是不会有响应了。一般fullgc速度很快,毫秒级
    的,用户无感知。除非内存特别大上百G的,或者fullgc也无法收集到足够内存导致一直fullgc,应用的外在表
    现就是程序卡死了.
  10. jvm调优工具和性能调优思路?
    Jvm中调优使用的分析命令如下, 先试用下面命令分析jvm中的问题 :
    Jps : 用于查询正在运行的JVM进程。 直接获取进程的pid
    Jstat : 可以实时显示本地或远程JVM进程中装载,内存,垃圾信息,JIT编译等数据
    Jinfo : 用于查询当前运行着的JVM属性和参数的值
    Jmap : 用于显示当前java堆和永生代的详细信息
    Jhat : 用于分析使用jmap生成的dump文件,是JDK自带的工具
    Jstack: 用于生成当JVM所有线程快照,线程快照是虚拟机每一条线程正在执行的方法,目的是定位
    线程出现长时间停顿的原因。
    下面是JVM常见的调优参数, 经过上面命令分析定位问题后使用下面参数优化:
    -Xmx 指定java程序的最大堆内存
    -Xms 指定最小堆内存
    -Xmn 设置年轻代大小 , 整个堆大小=年轻代大小+年老代大小。所以增大年轻代后,会减小年老代
    大小。此值对系统影响较大,Sun官方推荐为整个堆的3/8
    -Xss 指定线程的最大栈空间,此参数决定了java函数调用的深度,值越大说明调用深度越深,若值
    太小则容易栈溢出错误(StackOverflowError)
    -XX: PermSize 指定方法区(永久区)的初始值默认是物理内存的1/64。
    -XX:MetaspaceSize指定元数据区大小, 在Java8中,永久区被移除,代之的是元数据区
    -XX:NewRatio=n 年老代与年轻代的比值,-XX:NewRatio=2,表示年老代和年轻代的比值为2:1
    -XX:SurvivorRatio=n Eden区与Survivor区的大小比值,-XX:SurvivorRatio=8表示Eden区与
    Survivor区的大小比值是8:1:1,因为Survivor区有两个(from,to);
  11. Jvm内存模型?
    注意 : 大多数面试官混淆了内存模型和内存结构的区别, 如果面试官这么问, 可以咨询下面试官是否问的
    是jvm内存的结构都包含哪些东西, 还是问JMM(Java memory model)内存模型原理
    Jvm内存结构由9块组成:
    类加载器, 垃圾回收器, 执行引擎, 方法区, 堆, 栈, 直接内存, 本地方法栈, 程序计数器(pc寄存器)
    JMM(java memory model)java内存模型:
    JMM规定了所有变量(除了方法参数和本地变量,包括实例变量和静态变量)都放在主内存中。每
    个线程都有自己的工作内存,工作内存保存了该线程使用的主内存的变量副本,所有的操作都在工
    作内存中进行,线程不能直接操作主内存。线程之间通过将数据刷回主内存的方式进行通信。
    JMM定义了原子性,可见性和有序性。
    原子性:一个操作不可分割,不可中断,不可被其他线程干扰。JMM提供moniterenter和
    monitereixt俩个字节码指令保证代码块的原子性
    可见性:当一个变量被修改后,其他线程能够立即看到修改的结果
    有序性:禁止指令重排序
  12. GC如何识别垃圾?
    引用计数算法(已被淘汰的算法)
    给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就
    减1;任何时刻计数器为0的对象就是不可能再被使用的。
    可达性分析算法
    目前主流的编程语言(java,C#等)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判
    定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这
    些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有
    任何引用链相连, 就是从GC Roots到这个对象不可达时,则证明此对象是不可用的。
    分配流程
  13. 详细介绍一下 CMS 垃圾回收器?
    CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾
    回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。
    在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
    CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候会产生大量的内存碎片,当剩余内存不能满足
    程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃
    圾清除,此时的性能将会被降低。
    CMS 工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下 4 个阶段:
    (1)初始标记
    只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
    (2)并发标记
    进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。
    (3)重新标记
    为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,
    仍然需要暂停所有的工作线程。
    (4)并发清除
    清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标
    记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作, 所以总体上来看CMS 收集器的内存
    回收和用户线程是一起并发地执行。
  14. Java创建对象的内存分配流程?

三、集合相关面试题【17道】

  1. 聊一聊Java中容器体系结构!
    Collection:集合接口
    List:是Collection的子接口,用来存储有序的数据集合
    ArrayList:是List集合的实现类,底层采用动态数组进行存储数据,默认是空数组,如果存值则
    扩容至10,如果不够则以1.5倍进行扩容。
    LinkedList:是List集合的实现类,底层采用双向链表结构进行存储数据,增删数据比较方便,
    速度快。
    Vector :Vector类实现了可扩展的对象数组, 像数组一样,它包含可以使用整数索引访问的组
    件。但是,Vector的大小可以根据需要增长或缩小,以适应在创建Vector之后添加和删除。
    【注】Vector是同步的(线程安全)。 如果不需要线程安全的实现,建议使用ArrayList代替
    Vector.
    Set: 是Collection的子接口,用来存储无序的数据集合
    HashSet:底层采用哈希表(HashMap)的方式存储数据,数据无序且唯一
    TreeSet:采用有序二叉树进行存储数据,遵循了自然排序。
    Map: 与Collection并列,用来存储具有映射关系(key-value)的数据
    HashMap:哈希表结构存储数据,key值可以为null
    TreeMap:红黑树算法的实现
    HashTable:哈希表实现,key值不可以为null
    Properties:HashTable的子类,键值对存储数据均为String类型,主要用来操作以.properties
    结尾的配置文件。
    【特点】存储数据是以key,value对进行存储,其中key值是以Set集合形式存储,唯一且不可重复。
    value是以Collection形式存储,可以重复。
  2. List、Set和Map的区别,以及各自的优缺点
    List :
    可以允许重复的对象。可以插入多个null元素。是一个有序容器,保持了每个元素的插入顺
    序,输出的顺序就是插入的顺序。
    常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的
    随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
    Set :
    不允许重复对象。无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或
    者 Comparable 维护了一个排序顺序。只允许一个 null 元素。
    Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于
    HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其
    compare() 和 compareTo() 的定义进行排序的有序容器。而且可以重复。
    Map :
    Map不是collection的子接口或者实现类。Map是一个接口。
    Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但
    键对象必须是唯一的。
    TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
    Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
    Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。
    (HashMap、TreeMap最常用)
  3. ArrayList,LinkedList,Vector的区别!
    ArrayList和Vector是基于数组实现的。 ArrayList线程不安全,速度快, Vector线程安全,速度慢, 因为
    底层数组实现所以查询快, 修改快, 添加和删除慢,
    LinkedList是基于双向链表实现的, 线程不安全, 添加, 删除快, 查询和修改慢.
    ArrayList和Vector都是使用Object的数组形式来存储的,当向这两种类型中增加元素的时候,若容
    量不够,需要进行扩容。ArrayList扩容后的容量是之前的1.5倍,然后把之前的数据拷贝到新建的
    数组中去。而Vector默认情况下扩容后的容量是之前的2倍。
    Vector可以设置容量增量,而ArrayList不可以。
  4. TreeSet如何保证对象的有序性!
    TreeSet是有序不可重复集,具有以下特点:
    数据会按自然排序(可以理解为从小到大排序)
    不可存储null
    数据不可重复
    非线程安全
    TreeSet底层依赖于TreeMap,TreeMap的数据结构是二叉树(红黑树),由底层数据结构决定了
    TreeSet中元素有序且唯一。
    我们使用TreeSet时,如果添加对象的话,我们就必须去实现Comparable接口,当然也可以实现
    Comparator接口来保证对象的有序性。
  5. HashTable、HashMap、TreeMap的区别!
    Hashtable、HashMap、TreeMap都实现了Map接口,使用键值对的形式存储数据和操作数据。
    Hashtable是java早期提供的,方法是同步的(加了synchronized)。key和value都不能是null
    值。
    HashMap的方法不是同步的,支持key和value为null的情况。行为上基本和Hashtable一致。由于
    Hashtable是同步的,性能开销比较大,一般不推荐使用Hashtable。通常会选择使用HashMap。
    HashMap进行put和get操作,基本上可以达到常数时间的性能
    TreeMap是基于红黑树的一种提供顺序访问的Map,和HashMap不同,它的get或put操作的时间
    复杂度是O(log(n))。具体的顺序由指定的Comparator来决定,或者根据键key的具体顺序来决定。
    关系图:
  6. HashMap的底层数据存储结构
    JDK1.7采用 : 数组 + 链表结构
    JDK1.8采用 : 数组 + 链表 + 红黑树
    红黑树:jdk1.8最重要的就是引入了红黑树的设计,当hash表的单一链表长度超过 8 个的时候,链表结
    构就会转为红黑树结构。
    HashMap的底层数据存储结构为哈希表加红黑树。实现过程如下:
    调用HashMap的无参构造方法,加载因子loadFactor赋值0.75,table数组是空。
    当添加第一个元素时,创建长度16的数组,threshold=12。
    当‘链表长度大于等于8时,并且数组长度大于等于64时,链表调整红黑树
    当红黑树的节点个数小于6时,调整为链表
    当HashMap的容量超出阈值时,扩容为原来大小的2倍,减少元素的移动,提高效率。
  7. HashMap的扩容机制
    为什么会需要扩容:
    当它在初始化时会创建一个容量为16的数组,当元素数量超过阈值(当前容量X加载因子(通
    常为0.75)=扩容阈值)时,会将数组大小扩展为当前容量的两倍。
    但是HashMap的容量是有上限的。如果hashmap的数组当前容量达到了1073741824,则该
    数组不会再增长。且阈值将被设置为Integer.MAX_VALUE(2^31-1)即永远不会超出阈值。
    Hashmap的扩容机制在JDK8中,容量变化通常有以下集中情况:
    无参构造:实例化的hashmap默认内部数组是null,也就是无实例化。第一次调用put方法
    时,会开始第一次初始化扩容,长度为16.
    有参构造:用于指定容量。会根据指定的正整数找到不小于指定容量的2的幂数,将这个数设
    置赋值给阈值。第一次调用put方法时,会将阈值赋值给容量,然后让 阈值=容量X负载因子。
    因此并不是手动指定了容量就一定不会触发扩容,超过阈值后一样扩容。
    如果不是第一次扩容,则容量变为原来的2倍,阈值也变为原来的2倍。但负载因子不变。
    补充:
    首次put时会以扩容的方式初始化,然后存入数据,之后判断是否需要扩容
    如果不是首次put,则不咋需要初始化,会直接存入数据,然后判断是否需要扩容。
    元素迁移:
    在JDK8中,由于数组的容量是以2的幂次方扩容的,那么一个数组在扩容时,一个元素要么在
    原位置要么在原长度+原位置的位置。
    数组长度变为原来的2倍表现在二进制上多了一个高位参与数组下标确定。此时,一个元素通
    过hash转换坐标的方法计算后会出现一个现象:
    最高位是0,则坐标不变,最高位是1则坐标变为10000+原坐标,即原长度+原坐标。
  8. HashMap链表和红黑树转化时机!
    HashMap中维护有一个Node类型的数组table,当新添加的元素的hash值(hashCode & (length - 1))所
    指向的位置已有元素时就会被挂在该位置的最后面而形成链表。当在插入一个元素后某一位置的链表长
    度大于等于树化的阈值(TREEIFY_THRESHOLD = 8)时,会先去检查table的size,如果此时table数组
    的长度小于最小树化容量(MIN_TREEIFY_CAPACITY = 64),会先对数组进行扩容,扩容大小为原来的
    二倍。如果此时数组的长度已经大于64,则会将链表转化为红黑树。在扩容时,如果发现发现某处已经
    变为红黑树的树上的节点数量总和小于等于红黑树转化为链表的阈值(UNTREEIFY_THRESHOLD =
    6),则会将此处的红黑树转化为链表。
    简而言之,当链表长度超过8且哈希表容量大于64,会将链表转化为红黑树。而当红黑树的大小小于6,
    则由树转化为链表。
  9. 什么是Hash碰撞以及常见的解决方案
    Hash碰撞 : 对象Hash的前提是实现equals()和hashCode()两个方法,那么HashCode()的作用就是保证
    对象返回唯一hash值,但当两个不同对象计算值一样时,这就发生了碰撞冲突。
    解决方案:
    开放地址法(再散列法)开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)
    其中,m为哈希表的表长。di 是产生冲突的时候的增量序列。如果di值可能为1,2,3,…m-1,称线性
    探测再散列。
    如果di取1,则每次冲突之后,向后移动1个位置.如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,…kk,-
    kk(k<=m/2),称二次探测再散列。如果di取值可能为伪随机数列。称伪随机探测再散列。
    再哈希法Rehash当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。
    缺点:计算时间增加。
    比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再
    冲突,第三位,直到不冲突为止.这种方法不易产生聚集,但增加了计算时间。
    链地址法(拉链法)将所有关键字为同义词的记录存储在同一线性链表中.基本思想:将所有哈希地
    址为i的元素构成一个称为同义词
    链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词
    链中进行。链地址法适用于经常进行插入和删除的情况。
  10. HashMap在多线程操作中会出现什么问题!
    会出现线程安全问题, 可以使用加锁解决, 但是速度慢, 所以建议使用juc包下的ConcurrentHashMap替代
    HashMap, 速度快且线程安全.
  11. 如何保证集合是线程安全的?
    第一: 使用JUC包下的ConcurrentHashMap, 线程安全, 并且速度也快
    第二: 选用synchronize或者Lock加锁解决, 速度慢.
  12. 聊一聊JUC包下的并发工具
    CountDownLatch 闭锁,使用时需要传入一个int类型的参数,一个线程等待一组线程执行完毕后再恢复
    执行;await() 等待其他线程都执行完毕,通过计数器来判断等待的线程是否全部执行完毕。计数器:
    countDown方法,被等待线程执行完毕后将计数器值-1,当CountDownLatch的值减为0时无法恢复,
    这就是叫做闭锁的原因。
    CyclicBarrier 循环栅栏, 一组线程 同时到达临界点后再同时恢复执行(先到达临界点的线程会阻塞,直到
    所有线程都到达临界点),当多个线程同时到达临界点时,随机挑一个线程执行barrierAction后再同时恢
    复执行。await():调用该方法时表示线程已经到达屏障,随即阻塞。模拟:多线程向磁盘写入数据,计
    数器的值可以恢复。
    SemaPhore-信号量:SemaPhore是synchronized或Lock的加强版,作用是控制线程的并发数量。作
    用:用来控制同时访问特定资源的线程数量,通过协调保证合理的使用公共资源。acquire():尝试占用一
    个信号量,失败的线程会阻塞,直达有新的信号量,再恢复执行release():释放一个信号量;acquire(n):
    尝试占用n信号量,失败的线程会阻塞,直达有新的信号量,再恢复执行,release(n):释放n信号量。
    Exchanger 线程交换器,Exchange类似于一个交换器,Exchange类允许在两个线程之间定义同步点。
    当两个线程都到达同步点时,他们交换数据,因此第一个线程的数据进入到第二个线程中,第二个线程
    的数据进入到第一个线程中。
  13. ConcurrentHashMap如何保证线程安全
    底层采用CAS(内存比较交换技术) + volatile + synchronize实现, 初始化为无锁状态也就是使用CAS +
    volatile解决安全问题, 但是会涉及ABA问题. 但是几率很小, 如果涉及ABA问题, 底层自动切换成使用
    synchronize实现.
  14. ConcurrentHashMap在1.7和1.8的底层实现
    JDK1.7底层采用 : 数组 + 链表,采用Segement保证安全
    JDK1.8底层采用: 数据 + 链表 + 红黑树,采用CAS+synchronized代码块保证安全
  15. ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别
    首先二者都是线程安全的得队列,都可以用于生产与消费模型的场景。
    LinkedBlockingQueue是阻塞队列,其好处是:多线程操作共同的队列时不需要额外的同步,由于具有
    插入与移除的双重阻塞功能,对插入与移除进行阻塞,队列会自动平衡负载,从而减少生产与消费的处
    理速度差距。
    由于LinkedBlockingQueue有阻塞功能,其阻塞是基于锁机制实现的,当有多个线程消费时候,队列为
    空时消费线程被阻塞,有元素时需要再唤醒消费线程,队列元素可能时有时无,导致用户态与内核态切
    换频繁,消耗系统资源。从此方面来讲,LinkedBlockingQueue更适用于多线程插入,单线程取出,即
    多个生产者与单个消费者。
    ConcurrentLinkedQueue非阻塞队列,采用 CAS+自旋操作,解决多线程之间的竞争,多写操作增加冲
    突几率,增加自选次数,并不适合多写入的场景。当许多线程共享访问一个公共集合时,
    ConcurrentLinkedQueue 是一个恰当的选择。从此方面来讲,ConcurrentLinkedQueue更适用于单线
    程插入,多线程取出,即单个生产者与多个消费者。
    总之,对于几个线程生产与几个线程消费,二者并没有严格的规定, 只有谁更适合。
  16. 说一下 HashSet 的实现原理
    HashSet底层使用了哈希表来支持的,特点:存储快, 底层由HashMap实现
    往HashSet添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过元
    素 的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。
    如果算出的元素存储的位置目前没有任何元素存储,那么该元素可以直接存储在该位置上
    如果算出的元素的存储位置目前已经存在有其他的元素了,那么还会调用该元素的equals方法与该位置
    的元素再比较一次,如果equals方法返回的是true,那么该位置上的元素视为重复元素,不允许添加,
    如果返回的是false,则允许添加
  17. HashSet和TreeSet有什么区别?
    HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时
    间复杂度是O(1)。
    TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方
    法的时间复杂度是O(logn)。

四、多线程 【25道】

  1. 聊一聊并行和并发的区别?
    并发是指同一时间一起发生的.
    例如: 一个处理器同时处理多个任务叫并发处理, 同一时间好多个请求一起访问你的网站叫做并发请
    求等
    并行是指多个任务在同一时间一起运行.
    例如: 多个处理器或者是多核的处理器同时处理多个不同的任务, 这叫并行执行.
  2. 线程和进程的区别?
    进程是执行中的一个程序, 而一个进程中执行的一个任务即为一个线程
    一个线程只属于一个进程, 但一个进程能包含多个线程
    线程无地址空间, 它包括在进程的地址空间中
    线程的开销比进程小
  3. 说一说什么是原子性、可见性、有序性?
    原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(Atomic、CAS算法、
    synchronized、Lock)
    可见性:一个主内存的线程如果进行了修改,可以及时被其他线程观察到(synchronized、volatile)
    有序性:如果两个线程不能从 happens-before原则 观察出来,那么就不能观察他们的有序性,虚拟机
    可以随意的对他们进行重排序,导致其观察结果杂乱无序(happens-before原则)
  4. Java中实现多线程的方式
    通过扩展Thread类来创建多线程:
    定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。
    通过实现Runnable接口来创建多线程
    定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线
    程执行体
    通过线程池实现
    定义线程池实例, 使用的时候从线程池中获取线程使用.
    通过Callable和Future创建线程
    创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值
    创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该
    Callable对象的call()方法的返回值。
    使用FutureTask对象作为Thread对象的target创建并启动新线程。
    调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
  5. Java中提供的线程池种类,及其特点!
    五种线程池
    Single Thread Executor : 这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这
    个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执

    代码: Executors.newSingleThreadExecutor()
    Cached Thread Pool : 创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收
    空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
    代码:Executors.newCachedThreadPool()
    Fixed Thread Pool : 拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待, 代
    码: Executors.newFixedThreadPool(4) 在构造函数中的参数4是线程池的大小,你可以随意设
    置,也可以和cpu的核数量保持一致,获取cpu的核数量
    代码 : int cpuNums = Runtime.getRuntime().availableProcessors();
    Scheduled Thread Pool : 创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类
    似于Timer。
    代码:Executors.newScheduledThreadPool()
  6. 线程池的工作原理以及重要参数含义
    原理:
    线程池会初始化一定数量线程, 每次使用线程从线程池中拿一个使用, 省去了创建线程的开销和时间,
    使用完毕, 放回线程池中, 不销毁, 省去了销毁的开销和时间.
    重要参数:
    corePoolSize:线程池核心线程数量
    maximumPoolSize:线程池最大线程数量
    keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
    unit:存活时间的单位
    workQueue:存放任务的队列
    threadFactory:创建线程的工厂
    handler:拒绝策略
  7. 线程池的阻塞队列有哪些?
    ArrayBlockingQueue : 数组有界阻塞队列FIFO, 按照阻塞的先后顺序访问队列,默认情况下不保证线程
    公平的访问队列, 如果要保证公平性,会降低一定的吞吐量
    LinkedBlockingQueue : 链表有界阻塞队列,默认最大长度为Integer.MAX_VALUE。此队列按照先进先
    出的原则对元素进行排序SynchronousQueue : 不存储元素的阻塞队列
    DelayedWorkQueue : 延迟获取元素队列,指定时间后获取,为无界阻塞队列。
  8. 线程池的拒绝策略有哪些?
    拒绝策略核心接口:
    java.util.concurrent.RejectedExecutionHandler
    实现:
    CallerRunsPolicy : java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
    当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程
    执行,也就是谁提交任务,谁就负责执行任务。
    AbortPolicy : java.util.concurrent.ThreadPoolExecutor.AbortPolicy
    拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionException 的
    RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放
    弃提交等策略。
    DiscardPolicy : java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
    当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我
    们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
    DiscardOldestPolicy : java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
    如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的
    任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长
    的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险。
  9. synchronized和reentranlock锁的区别?
    Synchronized属于重量级锁, JDK早期版本使用了线程的状态变化来实现, JDK1.8中使用了自适应自旋锁
    实现. 总体来说安全, 但是效率慢
    Reentranlock是JUC包下的锁, 底层使用CAS + Volatile关键字实现. 效率高.
  10. synchronized底层如何实现?什么是锁升级、降级!
    jdk 中做了优化,提供了三种不同的 Monitor实现,分别是:
    偏斜锁 (Biased Locking)
    轻量级锁
    重量级锁
    所谓锁的升级,降级,实际上是 JVM 对 synchronized优化的一种策略,JVM 会检测不同的竞争状态,
    然后自动切换到合适的锁实现,这种切换就是锁的升级,降级。
    当没有出现锁的竞争时,默认使用的是偏斜锁。JVM 会利用 CAS 实现.
    如果有另一个线程试图锁定某个以被加持偏斜锁的对象时,JVM 就需要撤销偏斜锁,并切换到轻量级锁
    实现。如果获取成功,就使用轻量级锁,否者,进一步升级到重量级锁
  11. reentranlock的底层实现原理
    reentranlock 是基于AQS(AbstractQueueSynchronizer) 采用FIFO的队列表示排队等待的线程.
    AQS底层采用CAS(内存比较交换技术) + Volatile关键字实现.
  12. volatile关键字的特点
    保证线程之间的可见性,当一个线程对共享的变量进行了修改,其他的线程能够通过此关键字发现
    这个修改
    禁止指令重排序,编译器在编译的过程中会对程序进行优化,在保证结果不变的前提下,调整指令
    执行的顺序,提高执行效率,如果加了volatile关键字,则会禁止指令重排序。
    不能保证原子性
  13. Java为什么会指令重排
    java中源代码文件会被编译成.class的字节码文件, 字节码指定在执行之前, jvm底层有内置的对字节码的
    优化策略, 也就是指令重排机制, 会调整指令执行顺序. 目的是加快执行速度.
  14. 悲观锁和乐观锁的区别
    悲观锁 : 无论读还是更改对象都要加锁, 所以慢, 但是安全.
    乐观锁 : 读不加锁, 更改对象加锁, 所以快, 但是没有悲观锁安全
  15. 什么是CAS, 以及它的ABA问题, 如何解决ABA问题
    CAS的含义 :
    CAS是compare and swap的缩写,即我们所说的比较交换。
    CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值
    和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第
    一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能
    机会执行。
    CAS的ABA问题 :
    CAS容易造成ABA问题。一个线程a将数值改成了b,接着又改成了a,此时CAS认为是没有变化,其
    实是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次version加1。在
    java5中,已经提供了AtomicStampedReference来解决问题。
    CAS造成CPU利用率增加。之前说过了CAS里面是一个循环判断的过程,如果线程一直没有获取到
    状态,cpu资源会一直被占用。
  16. Atomic变量如何保证的原子性
    它底层是使用CAS + volatile关键字来保证原子性操作,从而达到线程安全的目的.
  17. ThreadLocal是什么,有什么作用
    ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映
    射,各个线程之间的变量互不干扰,在高并发场景下可以实现无状态的调用,特别适用于各个线程依赖
    不通 的变量值完成操作的场景。
    简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的
    ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全的问题了。
  18. ThreadLocal的内存泄漏问题
    ThreadLocalMap 中的 key 是一个 ThreadLocal 对象,且是一个弱引用,而 value 却是一个强引
    用。
    存在一种情况,可能导致内存泄漏。如果在某一时刻,将 ThreadLocal 实例设置为 null ,即
    ThreadLocal 没有强引用了,如果发生 GC 时,由于 ThreadLocal 实例只存在弱引用,所以被回收
    了,但是 value 仍然存在一个当前线程连接过来的强引用,其不会被回收,只有等到线程结束死亡或
    者手动清空 value 或者等到另一个 ThreadLocal 对象进行 get 或 set 操作时刚好触发
    static class ThreadLocalMap {
    static class Entry extends WeakReference> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) {
    super(k);
    value = v;
    }
    }
    // 。。。。
    }
    ThreadLocal的内存泄漏问题
    expungeStaleEntry 函数并且刚好能够检查到本 ThreadLocal 对象 key 为空(概率太小),这样才
    不会发生内存泄漏。否则, value 始终有引用指向它,它也不会被 GC 回收,那么就会导致内存泄漏。
    虽然发生内存泄漏的概率比较小,但是为了保险起见,也建议在使用完 ThreadLocal 对象后调用一下
    remove 方法清理一下值。
    作者:guozhchun
  19. jdk1.8对锁进行了哪些优化
    LongAdder 类似automicLong, 但是提供了“热点分离”。过程如下:如果并发不激烈,则与
    automicLong 一样,cas赋值。如果出现并发操作,则使用数组,数组的各元素之和为真实value值,让
    操作分散在数组各个元素上,把并发操作压力分散,一遇到并发就扩容数组,最后达到高效率。一般cas
    如果遇到高并发,可能一直赋值失败导致不断循环,热点分离可以解决这个问题
    stampedLock 改进读写锁,读不阻塞写。
    completableFuture 对Future进行增强,支持函数式编程的流式调用
  20. 死锁发生的情况和如何避免死锁
    死锁就是多个线程同时等待其他线程释放锁,导致被无限期阻塞的现象。
    发生死锁的四个必要条件:
    互斥条件
    不可抢占条件
    占有且申请条件
    循环等待条件
    避免死锁:
    尽量避免一个线程同时获取多个锁
    尽量避免一个线程在锁内部占用多个资源,尽量保证每个锁只占用一个资源
    顺序加锁
    加锁时限
    死锁检验
  21. 介绍锁的升级过程
    锁状态一种有四种 : 从级别由低到高依次是:无锁、偏向锁,轻量级锁,重量级锁,
    锁状态只能升级,不能降级
    升级过程 :
    无锁状态 - > 当一个线程访问同步代码块时升级成偏向锁 - > 偏向锁 -> 有锁竞争时升级成轻量级锁 - >
    轻量级锁 - > 自旋N次失败, 锁膨胀, 升级成重量级锁 - > 重量级锁
  22. 介绍锁的降级过程
    锁降级
    锁降级发生在读写锁中,写锁降级读锁的过程
    读写锁 : ReentrantReadWriteLock
    读写锁,既可以获取读锁,也可以获取写锁
    写锁是独占锁,所谓独占即为独自占有,别的线程既不能获取到该锁的写锁,也不能获取到对
    应的读锁。
    读锁是共享锁,所谓共享即是所有线程都可以共同持有该读锁
    锁降级过程 :
    锁降级指的是写锁降级为读锁的过程,他的过程是持有写锁,获取读锁,然后释放写锁
  23. 怎么解决多线程的大量访问时的数据同步
    可以加锁解决, 至于锁可以使用synchronized重量级锁, 也可以使用Lock轻量级锁
    还可以使用线程安全的对象作为多线程共用的数据操作对象, 比如ConcurrentHashMap, 或者Atomic原
    子操作类等
    速度快慢则是, 线程安全操作对象ConcurrentHashMap或者原子操作类比轻量级锁快, 轻量级锁比重量
    级锁要快.
  24. 线程的 run()和 start()有什么区别
    每个线程都是通过某个特定Thread对象所对应的run()方法来完成其操作的,方法run()称为线程体。也
    就是run方法中是线程具体要执行的任务或者业务.
    Start方法是启动线程, 调用线程类的Start方法线程开始运行. 这时无需等待run方法体代码执行完毕,可
    以直接继续执行下面的代码, 真正实现了多线程运行
  25. JDK1.6对Synchronized底层做了哪些优化
    Synchronized底层早期JDK版本是采用线程的状态转换实现的, 主要使用了线程的阻塞和唤醒来实现.
    JDK1.6中使用了自适应自旋锁实现, 还加入的锁消除, 锁粗化等优化策略
    自适应自旋锁 : JDK 1.6引入了更加聪明的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不
    再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
    锁消除 : 为了保证数据的完整性,我们在进行操作时需要对这部分操作进行同步控制,但是在有些情况
    下,JVM检测到不可能存在共享数据竞争,这是JVM会对这些同步锁进行锁消除
    锁粗化 : 就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁

五、IO【5道】

  1. Java提供了哪些IO方式?
    传统BIO同步阻塞IO, 这里面又分为字节流和字符流
    JDK1.4引入NIO, 同步非阻塞IO, 速度比传统BIO快, 并且更节省资源
    JDK1.7引入NIO2也叫做AIO, 异步非阻塞IO, 基于事件和回调机制实现, 速度更快
  2. NIO, BIO, AIO区别?
    NIO:在JDK1.4以前,Java的IO模型一直是BIO,从JDK1.4开始,JDK引入的新的IO模型NIO,它是同步
    非阻塞的。而服务器的实现模式是多个请求一个线程,即请求会注册到多路复用器Selecter上,多路复
    用器轮询到连接有IO请求时才启动一个线程处理
    BIO:同步阻塞,服务器的实现模式是一个连接一个线程,这样的模式很明显的一个缺陷是:优于客户
    端连接数与服务器线程数成正比关系,可能造成不必要的线程开销,严重的还会导致服务器内存溢出,
    当然,这种情况可以通过线程池改善,但并不能从本质上消除这个弊端
    AIO:JDK1.7发布了NIO2.0,这就是真正意义上的异步非阻塞,服务器的实现模式为多个有效请求一个
    线程,客户端的IO请求都是由OS完成再通知服务器应用去启动线程处理(回调)
  3. 有哪些缓冲流?如何实现缓冲功能!
    缓冲流也叫高效流,是对四个基本的FileXxx流的增强,按照数据类型分类:
    字节缓冲流 :BufferedInputStream,BufferedOutputStream
    字符缓冲流:BufferedReader,BufferedWriter
    基本原理:
    是在创建流对象的时候,会创建一个内置默认大小的缓冲区数组,减少系统IO次数,从而提高读写
    效率
    字节缓冲流
    public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
    public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流。
  4. 实现文件拷贝的几种方式!
    通过字节流实现文件拷贝
    通过字符流实现文件拷贝
    通过字节缓冲流实现文件拷贝
    通过字符缓冲流实现文件拷贝
    通过JAVA NIO非直接缓冲区拷贝文件
    通过JAVA NIO直接缓冲区拷贝文件
    通过JAVA NIO通道传输拷贝文件
  5. 什么Java序列化,如何实现序列化!
    序列化:就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的
    对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作
    时所引发的问题。
    序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements
    Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造
    一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object
    obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。

六、网络编程 【9道】

  1. http协议和RPC协议区别
    RPC是远程过程调用, 是JDK底层定义的规范, 它的实现既可以使用TCP也可以使用http, 底层可以使用二
    进制传输, 效率高.
    Http是协议, http协议底层位于传输层是tcp协议. 通用性好可以传输json数据, 效率稍微慢一些
  2. OSI网络模型七层都有哪些
    OSI参考模型分为7层,分别是物理层,数据链路层,网络层,传输层,会话层,表示层和应用层。
  3. 传输层上的TCP和UDP区别
    TCP与UDP的特点:
    UDP:无连接、不可靠、传输速度快. 适合传输语音, 视频等
    TCP:面向连接、可靠、传输速度没有UDP快, 例如: http, smtp等协议底层就是tcp
  4. http协议1.0和1.1版本区别
    主要有4个方面:缓存处理,带宽优化和网络连接使用,Host请求头的区别,长连接
    ● 缓存处理:HTTP1.1则引入了更多的缓存控制策略
    ● 长连接:HTTP1.1默认使用长连接,可有效减少TCP的三次握手开销,在一个TCP连接上可以传送多个
    HTTP请求和响应,减少了建立和关闭连接的消耗和延迟
    ● Host请求头的区别:HTTP1.0是没有host域的,HTTP1.1才支持这个参数
    ● 带宽优化和网络连接使用:在1.1后,现在只会发送head信息,果服务器认为客户端有权限请求服务
    器,则返回100,当接收到100后才会将剩下的信息发送到服务器
  5. http是短连接还是长连接, 如何实现的
    HTTP协议与TCP/IP协议的关系
    HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP
    协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层
    之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。
    TCP有可靠,面向连接的特点。
    如何理解HTTP协议是无状态的
    HTTP协议是无状态的,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状
    态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。
    HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使
    用的是UDP协议(无连接)。
    什么是长连接、短连接?
    在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立
    一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包
    含有其他的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web
    资源,就会建立一个HTTP会话。
    但从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有
    加入这行代码:Connection:keep-alive
    在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP
    连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
    Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设
    定这个时间。实现长连接要客户端和服务端都支持长连接。
    HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。
    TCP连接
    当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连
    接,当读写操作完成后,双方不再需要这个连接 时它们可以释放这个连接,连接的建立是需
    要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗
    三次握手建立连接 , 四次挥手关闭连接
    短连接的操作步骤是:
    建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
    长连接的操作步骤是:
    建立连接——数据传输…(保持连接)…数据传输——关闭连接
  6. 简述tcp三次握手四次挥手过程
    先向HTTP服务器发起TCP的确认请求(三次握手)
    客户端 --> SYN --> 服务器
    服务器 --> SYN+ACK —>客户端
    客户端 --> ACK --> 服务器
    客户端要和服务器断开TCP连接(四次挥手)
    客户端 --> FIN +ACK —> 服务器
    服务器 --> FIN —> 客户端
    服务器 --> ACK --> 客户端
    客户端 --> ACK —> 服务器
  7. http有多少类响应码?分别什么含义
    响应码由三位十进制数字组成,它们出现在由HTTP服务器发送的响应的第一行。
    响应码分五种类型,由它们的第一位数字表示:
    1xx:信息,请求收到,继续处理
    2xx:成功,行为被成功地接受、理解和采纳
    3xx:重定向,为了完成请求,必须进一步执行的动作
    4xx:客户端错误,请求包含语法错误或者请求无法实现
    5xx:服务器错误,服务器不能实现一种明显无效的请求
  8. tomcat的实现原理!tomcat如何进行优化?
    tomcat是一个基于JAVA的WEB容器,其实现了JAVA EE中的 Servlet 与 jsp 规范,与Nginx Apache 服务
    器不同在于一般用于动态请求处理。在架构设计上采用面向组件的方式设计。即整体功能是通过组件的
    方式拼装完成。另外每个组件都可以被替换以保证灵活性。
    实现原理:
    Tomcat是运行在JVM中的一个进程。它定义为“中间件”,顾名思义是一个在Java项目与JVM之间的中间容
    器。
    Web项目的本质,是一大堆的资源文件和方法。Web项目没有入口方法(即main方法),这意味着
    Web项目中的方法不会自动运行起来。Web项目部署进Tomcat的webapp中的目的是很明确的,那就是
    希望Tomcat去调用写好的方法去为客户端返回需要的资源和数据。
    Tomcat可以运行起来,并调用写好的方法。那么,Tomcat有一个main方法。对于Tomcat而言,它并
    不知道用户会有什么样的方法,这些都只是在项目被部署进webapp下后才确定的。由此,可知Tomcat
    用到了Java的反射来实现类的动态加载、实例化、获取方法、调用方法。但是部署到Tomcat的中的Web
    项目必须是按照规定好的接口来进行编写,以便进行调用。
    优化:
    修改内存的相关配置
    修改TOMCAT_HOME/bin/catalina.sh,在其中加入,也可以放在 CLASSPATH=下面, 加一些
    对内存调优的参数
    优化连接器Connector
    Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector
    的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待
    甚至超时放弃,所以我们需要提高这方面的处理能力。
    在TOMCAT_HOME/conf/server.xml添加最大线程数量和最小空闲线程数,请求的数量
    配置线程池
    Executor代表了一个线程池,可以在Tomcat组件之间共享。使用线程池的好处在于减少了创
    建销毁线程的相关消耗,而且可以提高线程的使用效率。
  9. get 和 post 请求有哪些区别?
    GET在浏览器回退时是无害的,而POST会再次提交请求。
    GET请求会被浏览器主动cache,而POST不会,除非手动设置。
    GET请求只能进行url编码,而POST支持多种编码方式。
    GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
    GET请求在URL中传送的参数是有长度限制的,而POST没有。
    参数的数据类型,GET只接受ASCII字符,而POST没有限制。
    GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
    GET参数通过URL传递,POST放在Request body中

七、MySQL以及SQL面试题【20道】

  1. 说一说什么是数据库事务!
    数据库事务就是在一套业务操作的多条sql语句执行中要么全成功, 要么全失败. 保证了数据的一致性.
    事务的四个属性:原子性,一致性,隔离性,持久性。
    原子性:在事务中进行的修改,要么全部执行,要么全不执行。如果在事务完成之前系统出现故
    障,SQLServer会撤销在事务中的修改。
    一致性:为了事务在查询和修改时数据不发生冲突。
    隔离性:隔离性是一种用于控制数据访问的机制,能够确保事务只能访问处于期望的一致性级别下
    的数据。SQLServer使用锁对各个事务之间正在修改和查询的数据进行隔离。
    持久性:在将数据修改写入到磁盘之前,总是先把这些修改写入到事务日志中。这样子,即使数据
    还没有写入到磁盘中,也可以认为事务是持久化的。这是如果系统重新启动,SQL Server也会检查
    数据库日志,进行恢复处理。
    隔离级别:读未提交、读已提交、可重复读、串行化
  2. 事务并发产生的问题和隔离级别!
    事务并发产生的问题:
    脏读:一个事务读取另一个事务的未提交数据! 真错误!read UNCOMMITTED;
    不可重复读:一个事务读取;另一个事务的提交的修改数据!read committed;
    虚读()幻读:一个事务读取了另一个数的提交的插入数据!repeatable read
    隔离级别:读未提交、读已提交、可重复读、串行化
    隔离级别选择:
    数据库隔离级别越高!数据越安全,性能越低!
    数据库隔离级别越低!数据越不安全,性能越高
    建议:设置为第二个隔离级别 read committed;
  3. Spring中事务的传播特性有哪些?
    七种传播特性:
    propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事
    务中,这是最常见的选择。
    propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
    propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
    propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
    propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
    propagation_nested:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启
    动一个新的事务,并在它自己的事务内运行。
    Spring 默认的事务传播行为是 PROPAGATION_REQUIRED
  4. MySQL的内部有哪几部分组成?
    连接器
    MYSQL拥有非常多的客户端比如:navicat,jdbc,SQLyog等客户端,这些客户端都需要向
    MYSQL通信都必须要建立连接,这个建立连接的工作就是由连接器完成的
    查询缓存(5.8版本开始取消了这个)
    连接建立之后,你就可以执行select语句了 这个时候首先会去查询下缓存,缓存的存储形式类似于
    key-value,key保存的是sql语句value 保存的是结果集,如果查询不在缓存中就会执行sql语句执
    行完成后把执行结果放入缓存中,如果是能在缓存中查询到那么直接返回
    词法分析器
    解析sql语句中的词法, 语法
    优化器
    对解析后生成的执行计划进行自动优化, 提升查询效率
    执行器
    执行sql语句分析后生成的执行计划, 执行后返回结果
  5. MySQL如何实现乐观锁和悲观锁
    ①乐观锁实现:版本号控制及时间戳控制。
    版本号控制:表中加一个 version 字段;当读取数据时,连同这个 version 字段一起读出;数据每
    更新一次就将此值加一;当提交更新时,判断数据库表中对应记录的当前版本号是否与之前取出来
    的版本号一致,如果一致则可以直接更新,如果不一致则表示是过期数据需要重试或者做其它操
    作。
    时间戳控制:其原理和版本号控制差不多,也是在表中添加一个 timestamp 的时间戳字段,然后
    提交更新时判断数据库中对应记录的当前时间戳是否与之前取出来的时间戳一致,一致就更新,不
    一致就重试。
    ②悲观锁实现:必须关闭mysql数据库的自动提交属性,开启事务,再进行数据库操作,最后提交事
    务。
  6. MySQL常用的存储引擎及区别
    InnoDB默认的存储引擎, 不需要任何配置, 默认使用的就是它, 支持事务, 支持主外键连接, 速度一般
    MyISAM不支持事务, 速度快, 以读写插入为主的应用程序,比如博客系统、新闻门户网站。
    Memory存储的数据都放在内存中, 服务器断电, 数据丢失, 速度最快, 现基本被redis取代.
  7. 说一说对MySQL索引的理解
    什么是数据库索引?
    数据库索引,是数据库管理系统中一个排序的数据结构,索引实现通常使用B树及变种的B+树。在
    数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指
    向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构就是索引。
    索引的作用?
    协助快速查询、更新数据库表中数据。
    副作用:
    增加了数据库的存储空间插入和修改数据时要花费较多的时间(因为索引也会随之变动)
  8. MySQL中什么字段适合添加索引
    表的主键、外键必须有索引;
    经常与其他表进行连接的表,在连接字段上应该建立索引;
    经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
    索引应该建在选择性高的字段上;
    索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
    复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
  9. MySQL中常见的索引类型
    普通索引:是最基本的索引,它没有任何限制
    唯一索引:与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索
    引,则列值的组合必须唯一
    主键索引:是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时
    创建主键索引
    组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被
    使用。使用组合索引时遵循最左前缀集合
  10. MySQL中索引失效的场景!
    WHERE字句的查询条件里有不等于号(WHERE column!=…),MYSQL将无法使用索引,类似地,如果
    WHERE字句的查询条件里使用了函数(如:WHERE DAY(column)=…),MYSQL将无法使用索引
    在JOIN操作中(需要从多个数据表提取数据时),MYSQL只有在主键和外键的数据类型相同时才能使用
    索引,否则即使建立了索引也不会使用
    如果WHERE子句的查询条件里使用了比较操作符LIKE和REGEXP,MYSQL只有在搜索模板的第一个字符
    不是通配符的情况下才能使用索引。比如说,如果查询条件是LIKE ‘abc%’,MYSQL将使用索引;如果条
    件是LIKE ‘%abc’,MYSQL将不使用索引。
  11. 说一说Hash和B+树结构索引区别?
    hash索引 : 等值查询效率高,不能排序,不能进行范围查询;Hash索引仅仅能满足"=",“IN"和”<=>"查
    询,不能使用范围查询
    B+树索引 : 数据有序,支持范围查询, 查询效率没有hash索引高.
  12. B+比B树索引的区别?
    B树,每个节点都储存key和dta,所有节点组成这棵树,并且叶子节点指针为null,叶子节点不包含任何
    关键字的信息。
    B+树,所有叶子节点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子节点本身
    依关键字的大小自小尔达的顺序链接,所有的非终端节点可以看成是索引部分,节点中仅含有其子树根
    节点中最大的(或最小的)关键字。
  13. 聚集(集中)索引和非聚集(稀疏)索引的区别
    【聚集索引】:也称 Clustered Index。是指关系表记录的物理顺序与索引的逻辑顺序相同。由于一张表
    只能按照一种物理顺序存放,一张表最多也只能存在一个聚集索引。与非聚集索引相比,聚集索引有着
    更快的检索速度。
    MySQL 里只有 INNODB 表支持聚集索引,INNODB 表数据本身就是聚集索引,也就是常说 IOT,索引
    组织表。非叶子节点按照主键顺序存放,叶子节点存放主键以及对应的行记录。所以对 INNODB 表进行
    全表顺序扫描会非常快。
    【非聚集索引】:也叫 Secondary Index。指的是非叶子节点按照索引的键值顺序存放,叶子节点存放
    索引键值以及对应的主键键值。MySQL 里除了 INNODB 表主键外,其他的都是二级索引。MYISAM,
    memory 等引擎的表索引都是非聚集索引。简单点说,就是索引与行数据分开存储。一张表可以有多个
    二级索引。
  14. 什么是索引倒排!
    倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具
    有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称
    为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted
    file)。
  15. 如何快速的复制一张表!
    将表结构和数据导出成sql脚本文件, 可以根据要求修改脚本中的表名字防止重名, 然后再导入到mysql中
    在库中创建新表后, 使用insert into 表名(字段名) select 字段名 from 表名
  16. SQL语句优化的方案?
    第一, 开启慢查询日志,让系统运行一段时间, 打开慢查询日志找到具体需要优化的sql语句
    第二, 使用explain执行计划分析sql语句中问题出现的哪里
    第三, 根据sql语句编写规范, 进行优化, 例如: 不要使用like模糊查询, 因为索引会失效; 尽量避免使用
    select*, 因为会返回无用字段; 条件中不要使用or关键字, 因为会全表扫描等.
  17. 组合索引的最左原则?
    MySQL 的联合索引会首先根据联合索引中最左边的、也就是第一个字段进行排序,在第一个字段排序的
    基础上,再对联合索引中后面的第二个字段进行排序,依此类推。联合索引当遇到范围查询(>、<、
    between、like)就会停止匹配,建立索引时匹配度高的字段在前,匹配度低的字段在后
    举例 : a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立
    (a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
    =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮
    你优化成索引可以识别的形式
  18. 什么是行锁, 表锁, 页锁?
    行锁 : 开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低
    行锁就是针对数据表中行记录的锁。这很好理解,比如事务A更新了一行,而这时候事务B也要更新
    同一行,则必须等事务A的操作完成后才能进行更新。
    表锁 : 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突概率高
    表锁的语法是 lock tables … read/write。可以用unlock tables主动释放锁,也可以在客户端断开
    的时候自动释放。需要注意,lock tables语法除了会限制别的线程的读写外,也限定了本线程接下
    来的操作对象。
    页级锁
    页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲
    突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
    特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度
    一般
  19. 数据库的三范式是什么
    第一范式:1NF 是对属性的原子性约束,强调的引擎是列的原子性,即数据库表的每一列都是不可分割
    的原子数据项。
    第二范式:2NF 是对记录的惟一性约束,要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能
    存在仅依赖主关键字一部分的属性。
    第三范式:3NF 是对字段冗余性的约束,任何非主属性不依赖于其它非主属性。即任何字段不能由其他
    字段派生出来, 它要求字段没有冗余。
  20. 什么情况下设置了索引但无法使用
    以“%”开头的 LIKE 语句,模糊匹配
    OR 语句前后没有同时使用索引
    数据类型出现隐式转化(如 varchar 不加单引号的话可能会自动转换为 int 型)

八、常用框架【19道】

  1. MyBatis$和#的区别
    #{ }可以防止Sql 注入,它会将所有传入的参数作为一个字符串来处理。
    Mybatis在处理#{}时,会将SQL语句中的#{}替换为?号,调用PrepaerdStatement的set方法来赋值。
    $ {} 则将传入的参数拼接到Sql上去执行,一般用于表名和字段名参数,$ 所对应的参数应该由服务器端
    提供,前端可以用参数进行选择,避免 Sql 注入的风险
    Mybatis在处理 时,就是把 {}时,就是把 时,就是把{}换成变量的值。
  2. MyBatis中一级缓存和二级缓存的区别
    一级缓存 :
    一级缓存是基于sqlsession默认开启的,在操作数据库时需要构造SqlSession对象,在对象中有一
    个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。
    一级缓存作用是sqlsession范围的,在同一个sqlsession中执行两次相同的sql时,第一次得到的数
    据会缓存放在内存中,第二次不再去数据库获取,而是直接在缓存中获取,提高效率。
    如果执行了增删改并提交到数据库,mybatis是会把sqlsession中的一级缓存清空的,这样是为了
    数据的准确性,避免脏读现象。
    二级缓存 :
    二级缓存是基于mapper的namespace作用域,但多个sqlsession操作同一个namespace下的sql
    时,并且传入的参数也相同,执行相同的sql语句,第一次执行完毕后会将数据缓存,这就是二级
    缓存。
    二级缓存同样是使用HashMap进行数据存储。相比一级缓存SqlSession,二级缓存的范围更大,
    多个Sqlsession可以共用二级缓存,二级缓存它是可以跨越多个sqlsession的。
  3. MyBatis和数据库交互的原理
    详细流程如下:
    加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文
    件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射
    配置),其对应着