java面试题

文章目录

  • Java面试题
    • 1. spring篇
      • 1.1 对spring的理解?
      • 1.2 springboot 与 spring区别?
      • 1.3 Spring bean的作用域?
      • 1.4 Autowired和Resource关键字的区别?
      • 1.5 谈谈对Spring AOP的理解?
      • 1.6 Spring Bean生命周期?
      • 1.7 Spring是如何解决循环依赖?
      • 1.8 Spring 中的bean三级缓存是什么?三级缓存作用?
      • 1.9 Spring 中哪些情况下,不能解决循环依赖问题?
      • 1.10 谈谈对Spring MVC理解?
      • 1.11 Spring MVC 工作流程?
      • 1.12 Spring 事物中的隔离级别?
    • 2 redis篇
      • 2.1 redis 常⽤的数据结构有哪些?
      • 2.2 redis String 还是 Hash 存储对象数据更好呢?
      • 2.3 redis 是多线程还是单线程的?
      • 2.4 redis 为内存设置过期时间的作用?
      • 2.5 redis 是如何判断数据是否过期的?
      • 2.6 redis数据过期删除策略?
      • 2.7 redis内存淘汰策略?
      • 2.8 redis持久化机制?
      • 2.9 哨兵机制与集群cluster模式有什么区别?
    • 3. java
      • 3.1 对threadLocal的理解?
      • 3.2 threadLocal key为什么是弱引用(解决内存泄露)?
      • 3.3 threadLocal value为什么不使用弱引用?
      • 3.5 介绍下强引用,软引用、弱引用、虚引用?
      • 3.6 介绍下hashMap?
      • 3.7 hashMap 哈希冲突是如何解决的?
      • 3.8 HaseSet与HashMap区别?
      • 3.9 ArrayList 与 LinkList区别?
      • 3.10 ⽐较 HashSet、LinkedHashSet 和 TreeSet 三者的区别?
      • 3.11 介绍下hashTable?
      • 3.12 介绍下ConcurrentHashMap?
      • 3.13 什么是反射(包括优点与缺点)?
      • 3.14 什么是动态代理?
      • 3.15 什么是静态代理?
      • 3.16 动态代理与静态代理区别?
      • 3.16 什么是注解?
      • 3.17 什么是泛型?
      • 3.18 JDK代理与CGLIB代理的区别?
      • 3.19 fail-safe 机制与 fail-fast 机制分别有什么作用?
      • 3.20 什么叫阻塞队列的有界和无界?
      • 3.21 谈谈CAS机制?
      • 3.22 String,StringBuffer,StringBuilder区别?
      • 3.23 java的SPI?
      • 3.24 为什么重写equal,就一定要重写hashcode?
      • 3.25 hashMap 与 hashTable区别?
      • 3.26 存储MD5的值是用varchar还是char?
    • 4 锁
      • 4.1 什么是乐观锁?
      • 4.2 什么是悲观锁?
      • 4.3 什么是自旋锁?
      • 4.4 Synchronized 与 ReentrantLock 区别?
      • 4.5 谈谈你对AQS的理解?
      • 4.6 谈谈你对分布式锁的理解?
      • 4.7 分布式锁实现方式?
    • 5. 多线程
      • 5.1 线程有几种状态?
      • 5.2 java 创建线程的方式
      • 5.3 介绍下线程池?
      • 5.4 谈谈CompletableFuture?
    • 6. JVM
      • 6.1 谈谈JVM的内存区域?
      • 6.2 gc主要做的事情?
      • 6.3 JVM如何判断哪些对象是垃圾(死亡)?
      • 6.4 垃圾收集算法有哪些?
      • 6.5 垃圾收集器有哪些?
      • 6.6 介绍下G1垃圾收集器?
      • 6.8 JVM类加载过程?
      • 6.9 类加载器?
      • 6.10 双亲委派原则?
    • 7 kafka篇
      • 7.1 谈谈你对kafka的理解?
      • 7.2 kafka消费模式?
      • 7.3 kafka 存储机制?
      • 7.4 kafka分区策略?
      • 7.5 kafka是如何保证数据的可靠性?
      • 7.6 什么是ISR?
      • 7.7 什么是LEO?
      • 7.8 什么是HW?
      • 7.9 follower故障恢复过程?
      • 7.10 leader故障是如何保证数据的一致性?
      • 7.11 kafka如何保成Exactly Once?
      • 7.12 kafka是如何高效读写数据的?
    • 8 MySql 篇
      • 8.1 mysql 引擎有哪几种?
      • 8.2 InnDb与MylSAM的区别?
      • 8.3 介绍下索引?
      • 8.3 什么情况会造成索引失效?
      • 8.4 什么是最左前缀原则?
      • 8.5 项目中如何优化索引?
      • 8.6 谈谈你对mysql性能优化的理解?
      • 8.7 如何保证mysql与redis一致性?
      • 8.8 mysql索引的优点与缺点?
      • 8.9 mysql 事务的隔离级别?
      • 8.10 mysql为什么使用B+树作为索引结构?

Java面试题

1. spring篇

1.1 对spring的理解?

spring是基于Java的企业级开源开发框。它为开发者提供了一些很好的特性,比如轻量、IOC容器、控制反转、AOP面向切面编程、MVC架构、支持事务、全局异常处理等特性。基于以上特性可以让开发者将更多的工精力聚焦与业务开发。

1.2 springboot 与 spring区别?

Spring Boot 是一款敏捷开源开发框架,它是 Spring 框架的一个扩展,比较大差异是去xml的配置化。
Spring Boot 使用了约定大于配置的理念,提供了一些自动配置和默认配置,减少了开发人员在项目配置上的工作量,让开发者将更多的工作精力聚焦与业务开发。

1.3 Spring bean的作用域?

  1. singleton,单例模式,Spring idc容器中只有一个实例
  2. protoType,多例模式,每次获取bean,都会返回一个新的实例
  3. request,每一次http请求都会创建一个bean
  4. session,同一个http session共享一个bean
  5. globalSession,很少用

1.4 Autowired和Resource关键字的区别?

@Resource和@Autowired都是做bean的注入时使用,@Autowired是spring提供的,@Resource是J2EE提供的,需要导入包javax.annotation.Resource。
不同点:
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。
@Resource有两个重要的属性:name和type,如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

1.5 谈谈对Spring AOP的理解?

spring aop是动态代理思想,在不修改源代码的情况下,对原有的功能增强。
在代码的使用上,需要先定义切入点,对切入点进行after,before,around,增强代码的编写。
aop还将代码抽象模块化,提高代码的灵活性和复用性。
常见的使用场景:
记录日志,事务管理,权限控制等等

1.6 Spring Bean生命周期?

五个阶段

  1. 创建前准备,上下文和相关配置加载与解析(init-method)
  2. 创建实例阶段,通过反射来创建 Bean 的实例对象
  3. 依赖注入阶段
  4. 容器缓存阶段,将实例交由IOC容器管理,以及 Spring 的缓存中
  5. 销毁实例阶段

1.7 Spring是如何解决循环依赖?

什么是循环依赖? 两个或多个 Bean 之间互相持有对方的引用就会发生循环依赖,循环的依赖会导致注入死循环。
如何解决?
在Spring 中,设计了三级缓存来解决循环依赖问题。当我们去调用 getBean()方法的时候,Spring 会先从一级缓存中去找到目标 Bean,如果发现一级缓存中没有便会去二级缓存中去找,而如果一、二级缓存中都没有找到,意味着该目标 Bean还没有实例化,于是Spring 容器会实例化目标 Bean。

1.8 Spring 中的bean三级缓存是什么?三级缓存作用?

三级缓存

  1. 一级缓存:所有成熟的bean
  2. 二级缓存:所有早期的bean(刚初始化的 Bean称为早期 Bean)
  3. 三级缓存:代理的bean

第三级缓存的作用?
第三级缓存是用来存储代理 Bean,当调用 getBean()方法时,发现目标 Bean 需要通过代理工厂来创建,此时会将创建好的实例保存到三级缓存,最终也会将创建好的 Bean 同步到一级缓存中

1.9 Spring 中哪些情况下,不能解决循环依赖问题?

  1. 多例 Bean 通过 setter 注入的情况,不能解决循环依赖问题
  2. 构造器注入的 Bean 的情况,不能解决循环依赖问题
  3. DependsOn 的循环依赖,不能解决循环依赖问题
  4. 单利模式的setter注入,可以解决循环依赖问题

1.10 谈谈对Spring MVC理解?

它是一种MVC设计模式,包括Model 模型,VIew 视图,Controller 控制器三层。Spring MVC将复杂的web开发简单化,为开发者提供了功能强大、灵活、简单的 Web 框架。

1.11 Spring MVC 工作流程?

java面试题_第1张图片

  1. 客户端请求url提交到 DispatcherServlet。
  2. 由 DispatcherServlet 控制器通过url映射到对的 HandlerMapping,找到处理请求的Controller。
  3. 调用处理器 Controller
  4. Controller 调用业务逻辑处理后,返回视图ModelAndView
  5. DispatcherServlet通过(ViewResoler)视图解析器,将ModelAndView 解析为视图
  6. 将视图返回给客户(浏览器)

1.12 Spring 事物中的隔离级别?

  1. TransactionDefinition.ISOLATION_DEFAULT(default): Mysql 默采用的REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
  2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED(read_uncommitted):未提交读,可能会导致脏读、幻读或不可重复读。
  3. TransactionDefinition.ISOLATION_READ_COMMITTED(read_committed):可以阻止脏
    读,会出现幻读或不可重复读。
  4. TransactionDefinition.ISOLATION_REPEATABLE_READ (可重复读):可以阻止脏读和不可重复读,还是会出现幻读。
  5. TransactionDefinition.ISOLATION_SERIALIZABLE(SERIALIZABLE):可以防止脏读、不可重复读以及幻读。

2 redis篇

2.1 redis 常⽤的数据结构有哪些?

String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)

2.2 redis String 还是 Hash 存储对象数据更好呢?

  • String 存储的是序列化后的对象数据,存放的是整个对象。Hash 是对对象的每个字段单独存
    储,可以获取部分字段的信息,也可以修改或者添加部分字段,节省⽹络流量。如果对象中某些
    字段需要经常变动或者经常需要单独查询对象中的个别字段信息,Hash 就⾮常适合。
  • String 存储相对来说更加节省内存,缓存相同数量的对象数据,String 消耗的内存约是 Hash 的
    ⼀半。并且,存储具有多层嵌套的对象时也⽅便很多。如果系统对性能和资源消耗⾮常敏感的
    话,String 就⾮常适合。

2.3 redis 是多线程还是单线程的?

  • 对于客户端线程的请求,redis采用单线程,单线程保证的高并发数据的安全性。Redis底层采用的 IO 多路复⽤来监听客户端的⼤量请求,降低了资源的消耗。
  • redis 6.0 版本引入了多线程,主要是为了提⾼⽹络 IO 读写性能。Redis 的多线程只是在⽹络数据的读写这类耗时操作上使⽤
    了,执⾏命令仍然是单线程顺序执⾏,所以没有线程安全性的问题。

2.4 redis 为内存设置过期时间的作用?

redis 是一款内存数据,同时它也支持对数据进行持久化。为key设置过期时间,主要是为了防止在有限的内存中,随着数据的增加造成服务的oom。

2.5 redis 是如何判断数据是否过期的?

redis 通过⼀个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间,value保存的是过期时间戳

2.6 redis数据过期删除策略?

  • 定期删除 : 每隔⼀段时间,会随机抽取⼀批过期 key 执⾏删除 操作。并且,Redis 底层会通过限制删除操作执⾏的时⻓和频率来减少删除操作对 CPU 时间的影响。

  • 惰性删除 :只会在取出 key 的时候才对数据进⾏过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。

  • 总结:定期删除对内存更加友好,惰性删除对 CPU 更加友好。所以 Redis 默认采⽤的是 定期删除+惰性/懒汉式删除 。

2.7 redis内存淘汰策略?

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使⽤的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru(least recently used):当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的 key(这个是最常⽤的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错。这个应该没⼈使⽤吧!

2.8 redis持久化机制?

Redis 的⼀种持久化⽅式叫快照(snapshotting,RDB),另⼀种⽅式是只追加⽂(append-only file, AOF)

2.9 哨兵机制与集群cluster模式有什么区别?

  1. Redis 哨兵集群是基于主从复制来实现的,所以它可以实现读写分离,分担 Redis 读操作的压力。
  2. Redis Cluster 集群的 Slave 节点只是实现冷备机制,它只有在 Master 宕机 之后才会工作。
  3. Redis 哨兵集群无法在线扩容,所以它的并发压力受限于单个服务器的资源配置。
  4. Redis Cluster 提供了基于 Slot 槽的数据分片机制,可以实现在线扩容提升写数 据的性能。
  5. 从集群架构上来说,Redis 哨兵集群是一主多从, 而 Redis Cluster 是多主多从。

3. java

3.1 对threadLocal的理解?

  • threadLocal是线程的一个私有变量,主要用于存储数据。ThreadLocal类内部存储对象是ThreadLocalMap,ThreadLocalMap本质是一个hash表。有一个特点是key(key是线程本身)是弱引用,value是强引用。用一句话总结:ThreadLocal主要用于保存线程私有的数据,避免多线程并发访问共享资源的竞争问题,从而提高程序的并发性能和可靠性
  • 常见的使用场景
  1. 线程上下文参数的传递
  2. jdbc连接池,用于事务管理等等。

3.2 threadLocal key为什么是弱引用(解决内存泄露)?

可以解决内存泄漏的问题。因为JVM GC过程中,ThreadLocal实例已经不存在引用了,那么对应的弱引用对象就会被回收。ThreadLocalMap调用get ()、set (T)、remove ()等方法的时候,会自动清理key为null的Entity。在使用ThreadLocal过程中,还是需要手动remove。毕竟ThreadLocal清理机制不是实时的。

3.3 threadLocal value为什么不使用弱引用?

因为value对象可以被其他对象引用。如果设置了弱引用,会导致其他对象在使用value对象的时候报空指针异常。

3.5 介绍下强引用,软引用、弱引用、虚引用?

  • 强引用:正常情况下,创建一个对象,并将该对象赋给一个引用变量,这个引用变量就是一个强引用。它处于可达状态,它是不可能被垃圾回收机制回收的。
  • 软引用:软引用需要用 SoftReference 类来实现,特点是当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。
  • 弱引用:弱引用需要用 WeakReference 类来实现,特點是只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。
  • 虛引用:虚引用需要 PhantomReference 类来实现,虚引用的主要作用是跟踪对象被垃圾回收的状态

3.6 介绍下hashMap?

  1. 从数据结构,jdk1.7 数据+链表;jdk1.8 数组+链表+红黑树,当链表的长度大于8,自动转化为红黑树,提高检索效率
  2. hashMap是线程不安全,所以在高并发的情况下,可能会出现循环链表,数据重复等问题;在开发中,线程安全可以使用Hashtable或者是ConcurrentHashMap
  3. hashMap,key与value是允许为null
  4. hashMap扩容机制,初始容量16,扩容因子0.75,扩容阈值=容量 * 扩容因子=16*0.75=12;当hashMap的容量达到扩容阈值,自动扩容2倍(扩容是过程:创建一个新的hashMap,进行对象及链表的复制)

3.7 hashMap 哈希冲突是如何解决的?

造成哈希冲突的原因:同一个hashcode存在对应多个不用的key值
常见的处理方式

  1. 再 hash 法,就是当通过某个 hash 函数计算的 key 存在冲突时,再用另外一个 hash 函数对这个 key 做 hash,一直运算直到不再产生冲突。这种方式会增加计 算时间,性能影响较大。
  2. JDK1.8 HashMap,通过链式寻址法 + 红黑树解决
    对于put过程,获取key对应的hashcode,根据hashcode定位到hash表存储的位置,如果存储节点下有数据,则轮询调用equal方法进行比较,true则替换value,当所有的对比结果都是false,则在链表的尾部插入
    对于get过程,获取key对应的hashcode,根据hashcode定位到hash表存储的位置,获取存储节点下的数据,同样是轮询调动equal,true则获取对应的value,当所有的对比结果都是false,则返回null

3.8 HaseSet与HashMap区别?

  1. hashSet实现了Set接口,hashMap实现了Map接口
  2. hashSet存储的是对象,hashMap存储key,value结构
  3. hashSet不能同时存储两个相同的对象,可以用于做去重的功能。

3.9 ArrayList 与 LinkList区别?

  1. ArrayList
    数据结构:数组;线程不安全的;优点是可以快速检索且遍历的效率快;缺点是增删下效率慢(原因是增删过程是复制的过程);当容量不够,默认当前容量*1.5+1
  2. LinkList
    数据结构:双向循环链表;线程不安全的;优点是增删快;缺点是查询效率慢

3.10 ⽐较 HashSet、LinkedHashSet 和 TreeSet 三者的区别?

  1. HashSet 、 LinkedHashSet 和 TreeSet 都是 Set 接⼝的实现类,都能保证元素唯⼀,并且都不是线程安全的
  2. 数据结构上的区别:hashSet数据结构是hash表,LinkedHashSet 数据结构是链表+hash表,treeSet数据结构是红黑树

3.11 介绍下hashTable?

  1. 数据结构:数据+链表(hash表)
  2. 线程安全的,使用的是可重入锁synchronized
  3. key、value不允许为null
  4. 缺点:高并发场景效率低

3.12 介绍下ConcurrentHashMap?

  1. 与hashMap对比,ConcurrentHashMap对打的特点是线程安全;
  2. jdk1.7与jdk1.8实现方式有所差异
  • jdk1.7 底层数据结构:分段数据+hash表,锁实现的方式是采用分段锁,锁住的对象是分段数组,默认情况分段数组16,这表明它最多支持16个并发写,效率比较低。
  • jdk1.8 底层数据结构:hash表,数据+链表+红黑树,锁实现的方式是颗粒锁,采用cas和synchronized锁住操作的节点对象。这使得jdk1.8的ConcurrentHashMap效率更高。

3.13 什么是反射(包括优点与缺点)?

  1. 在Java当中,对于任意一个类,都能够知道这个类的所有属性和方法等类信息;对于任意个对象,都能够调用它的任意一个方法。这种动态获取类信息以及动态调用对象的方法的功能就是反射机制。
  2. 优点:能够运行时动态获取类的实例,提高灵活性
  3. 缺点:使用反射性能较低,需要解析字节码,将内存中的对象进行解析。 解决方案: 通过setAccessible(true)
  4. 应用场景:通过反射创建对象,常见加载Mysql驱动类。

关闭JDK的安全检查来提升反射速度;

3.14 什么是动态代理?

  • 在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能增强的一种技术。简单理解就是在执行对象方法的时候,动态拦截,在拦截方法的前后执行增强功能操作(比如Spring AOP)。
  • 常见的动态代理有两,基于接口的JDK动态代理,基于CGLib的动态代理

3.15 什么是静态代理?

静态代理就是在程序运行前就已经存在代理类的字节码文件。优点简单、效率高,缺点代码冗余,灵活性差

3.16 动态代理与静态代理区别?

  1. 静态代理

3.16 什么是注解?

在java当中,注解可以为开发者提供元数据信息和方法,注解可以作用在类,成员变量,方法体,方法参数上,可以提高代码的可读性和灵活性。在代码的表示上,注解的定义,创建一个类并用@interface修饰。可以使用@Target(修饰的对象范围),@Retention(定义注解生命周期),@Documented(javadoc) ,@Inherited 修饰自定义注解类

3.17 什么是泛型?

在java当中,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,在代码中的表示为:T,U,?。比如我们用的非常多的ArrayList就是个泛型类,ArrayList作为集合可以存放各种元素,如Integer, String,自定义的各种参数类型。

3.18 JDK代理与CGLIB代理的区别?

  1. JDK动态代理实现接口,Cglib动态代理是继承思想
  2. JDK动态代理(目标对象存在接口时)执行效率高于Ciglib
  3. 如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理

3.19 fail-safe 机制与 fail-fast 机制分别有什么作用?

  1. fail-safe 和 fail-fast,是多线程并发操作集合时的一种失败处理机制。
  2. Fail-fast:表示快速失败,在集合遍历过程中,一旦发现容器中的数据被修改了, 会立刻抛出 ConcurrentModificationException 异常,从而导致遍历失败(定义一个 Map 集合,使用 Iterator 迭代器进行数据遍历,在遍历过程中,对集 合数据做变更时,就会发生 fail-fast。,常见的的使用 fail-fast 方式遍历的容 器有 HashMap 和 ArrayList 等)。
  3. Fail-safe,表示失败安全,也就是在这种机制下,出现集合元素的修改,不会抛出ConcurrentModificationException。 原因是采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的, 而是先复制原有集合内容(fail-safe 方 式 遍 历 的 容 器 有 ConcerrentHashMap 和 CopyOnWriteArrayList 等)。

3.20 什么叫阻塞队列的有界和无界?

阻塞队列,是一种特殊的队列。对于普通对了它有两种特殊功能。

  1. 当队列为空的时候,获取队列中元素的消费者线程会被阻塞,同时唤醒生产者线程。
  2. 当队列满了的时候,向队列中添加元素的生产者线程被阻塞,同时唤醒消费者线程。
    阻塞队列中能够容纳的元素个数,通常情况下是有界的,ArrayBlockingList为例,创建可以指定队列大小。无界队列,就是没有设置固定大小的队列,无界队列容易导致内存溢出的问题。

3.21 谈谈CAS机制?

  1. cas全称是 CompareAndSwap,比较并交换的意思。它的主要功能在多线程环境下,操作共享变量,保证其原子性。
  2. CAS操作过程,当前线程获取共享变量并存储到本地,在更新之前需要拿本地变量与共享变量进行比较,如果比较结果一致,进行update的操作。如果比较结果不一致,以上的过程重新来过。

3.22 String,StringBuffer,StringBuilder区别?

  1. 可变性,StringBuffer,StringBuilder是可变的。String是不可变的,原因是String存储对象(char[])使用final修饰。
  2. 线程安全性,String是不可变的,所以它是线程安全的;StringBuffer也是现成安全的,采用是synchronized锁;StringBuilder是现成不安全的。
  3. 性能方面,StringBuilder > StringBuffer > String。

3.23 java的SPI?

Java SPI,它是一种基于接口的动态扩展机制,相当于 Java 里面提供了一套接口。然后第三方可以实现这个接口来完成功能的扩展和实现。
应用案例:flink 自定义connector/sink,都是基于SPI实现的。
实现步骤

  1. 先定义一个接口(org.apache.flink.table.factories.Factory)
  2. resources下创建META-INF/service
  3. 创建文件,文件名称与定义的接口一致(org.apache.flink.table.factories.Factory),并在该文件具体配置扩展类
  4. SPI底层会加载META-INF文件信息,通过classLoader与动态代理获取到对应的扩展类
  5. SPI实例化会造成性能开销,并且加载一些不需要用到的实现类,会导致内存资源的浪费

3.24 为什么重写equal,就一定要重写hashcode?

  1. 如果只重写 equals 方法,不重写 hashCode 方法。就有可能导致 a.equals(b)这个表达式成立,但是 hashCode 却不同。
  2. 只重写了 equals 方法的对象,在使用散列集合(set,map)进行存储的时候就会出现问题。因为散列集合是使用 hashCode 来计算 key 的存储位置,没有重写hashcode会导致两个对象equal等式成立,但这两个对象储存在散列表中会出占用两个位置,造成大家约定俗成的规则,出现一些不可预料的错误。

3.25 hashMap 与 hashTable区别?

  1. 线程安全,hashTable 是线程安全的,而 HashMap不是。HashTable 采用了全局同步锁synchronized来保证安全性,对性能影响较大。所以在性能上hashmap会更好。
  2. 数据结构上,hashTable采用的是数组+链表,hashMap在JDK.8改成了数组+链表+红黑树。
  3. hashTable key不允许为null,hashTable允许key为null
  4. key 的散列算法不同,HashTable 直接是使用 key 的 hashcode 对数组长度做取模。而 HashMap 对 key 的 hashcode 做了二次散列,从而避免 key 的分布不均匀问题影响到查询性能。

3.26 存储MD5的值是用varchar还是char?

我认为是使用char类型,原因是char 类型是固定长度的字符串,varchar 是可变长度字符串。而 MD5 是一个固定长度的字符,不管数据怎么修改,长度不变,这个点很符合 char 类型。另外,由于是固定长度,所以在数据变更的时候,不需要去调整存储空间大小, 在效率上会比 varchar 好。

4 锁

4.1 什么是乐观锁?

乐观锁是一种乐观思想,它认为读多写少。每次有线程去读取数据,不会加锁。当要做更新的操作,才会加锁。采用的是CAS操作(compare ans set),CAS逻辑是这样子的:先读取版本号加载到本地变量,更新之前判断当前本地变量的版本号是否与操作资源的版本号一致,一致才有有权限操作,不一致则重新执行CAS逻辑。

4.2 什么是悲观锁?

悲观锁是就是悲观思想,即认为写多,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁。像Synchronized就是java中的悲观锁

4.3 什么是自旋锁?

自旋锁原理是这样的,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。 自旋锁的缺点:只适用于锁竞争比较小的场景。

4.4 Synchronized 与 ReentrantLock 区别?

  • Synchronized

    1. Synchronized 是基于JVM实现的
    2. Synchronized 是可重入锁,它是非公平锁
    3. 在使用上,Synchronized 作用于静态方法时锁的是class实例;Synchronized 作用于普通方法锁的是当前对象。
    4. 缺点,自动解锁不够灵活
  • ReentrantLock

    1. ReentrantLock 是基于JDK实现的
    2. ReentrantLock 也是可重入锁,它可以作为公平锁与非公平锁,由开发者定义的时候决定
    3. ReentrantLock 可以完成 synchronized 所能完成的所有工作
    4. ReentrantLock 提供了灵活的api,lock,unlock,Condition等等,对于开发者它更加灵活

4.5 谈谈你对AQS的理解?

  1. AS全称是AbstractQueuedSynchronizer,它是多线程同步器,是并发编程中比较核心的组件,如 Lock、countDownLatch等都用到了 AQS。
  2. AQS 提供了两种锁机制,分别是排它锁,和共享锁。
    排它锁,就是在多线程竞争同一共享资源时,同一时刻只允许一个线程获得锁资源。ReentrantLock 重入锁实现就是用到了 AQS 中的排它锁功能。
    共享锁也称为读锁。

4.6 谈谈你对分布式锁的理解?

分布式锁,是一种跨进程的,跨机器节点的互斥锁,它可以保证多机器节点对于共享资源访问的排他性。我觉得分布式锁和线程锁本质上是一样的,线程锁的生命周期是单进程多线程, 分布式锁的生命周期是多进程多机器节点。在实现方面,分布锁需要依赖三方组件,像redis,zookeeper。

4.7 分布式锁实现方式?

  • redis的实现方式:
  1. 使用setNx方法,该方法需要传递三个参数:key,sessionId,timeOut;
  2. key代表锁;sessionId用于保证只有持有锁的线程才可以手动释放锁;timeOut表示当持有锁的线程超过这个时间自动释放锁,解决死锁的问题
  3. redis的实现方式有一些缺点:需要客户端不断的轮询判断,对系统的消耗相对较大;并且它是非公平的分布式锁
  • zookeeper的实现
  1. 根据zoopkeeper临时有序节点的特性,使用发布订阅回调的方式实现分布式锁。
  2. zookeeper通过订阅回调的方式更好,没有redis轮询对系统带来额外的资源开销。

5. 多线程

5.1 线程有几种状态?

  1. 创建状态,该状态JVM 为线程分配内存,并初始化其成员变量
  2. 就绪状态,线程调用start方法后,进入就绪状态,抢占cpu执行
  3. 运行状态,就绪状态的线程抢到cpu,进入该运行
  4. 阻塞状态,线程调用sleep,或者是object.wait进入阻塞状态
  5. 死亡状态,run()或 call()方法执行完成,线程正常结束会进入死亡状态;抛出异常或者是调用stop方式也会使线程进入死亡状态

5.2 java 创建线程的方式

继承Thread,实现runnable,实现callable三种方式

5.3 介绍下线程池?

  • ExecutorService
  1. 线程池有四类
    SingleThreadPool: 单例的线程池,只有一个线程工作
    FixedThreadPool: 固定大小的线程池
    CachedThreadPool:缓存线程池,特点:创建过程需要指定核心线程数、最大线程数、及过期时间;当任务超过核心线程数时,会额外创建线程进行工作,总线程数不能大于最大线程数。当非核心线程超过过期时间没工作,会自动被销毁。
    ScheduledThreadPool:定时调度线程
  2. 在开发中创建线程池时建议不要使用Executors.new方式,因为默认情况线程的最大值以及队列的最大值为Integer.MAX,可能会导致OOM;正常都是通过new ThreadPoolExecutor方式,然后根据实际场景定义参数。
  3. 还有一个比较重要的是线程池提供的四种拒绝策略(当线程池中工作线程满了并且队列也满了,用户在新增任务的时候会执行)
    AbortPolicy :中止策略,该模式是默认的,特性是抛出 RejectedExecutionException 异常信息
    CallerRunsPolicy:调用者执行策略,特性是使用调用线程直接运行任务
    DiscardPolicy :直接丢弃策略,特性直接丢弃后面新增的任务
    DiscardOldestPolicy :丢弃最旧任务策略,特性是丢弃队列最旧的任务,将新任务新增到队列当中
    用户自定义拒绝策略,需要继承RejectedExecutionHandler 接口,重写rejectedExecution方法

5.4 谈谈CompletableFuture?

CompletableFuture 是 JDK1.8 里面引入的一个基于事件驱动的异步回调类。它提供了更灵活的方式来处理多个异步任务,可以更容易地编写并发程序。
CompletableFuture常见的使用场景有

  1. 异步计算,使用 supplyAsync()、runAsync() 等方法来启动异步任务
  2. 任务组合,调用allof组合任务,调用thenApply()、thenCombine() 进行转换与合并。
  3. 阻塞等待任务完成,使用 join() 或 get() 方法来阻塞当前线程,等待异步任务完成并获取其结果(get()和join()方法都可以用于等待异步任务的结果完成,并获取其结果。它们的主要区别在于get()方法会抛出受检异常,需要进行异常处理,而join()方法则不会抛出受检异常)。
  4. 异常处理,CompletableFuture提供了 exceptionally() 和 handle() 方法来处理异常。

6. JVM

6.1 谈谈JVM的内存区域?

  1. 程序计数器(线程私有),是当前线程所执行的字节码的指示器。
  2. 虚拟机栈(线程私有) ,是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量。表、操作数栈、动态链接、方法出口等信息。
  3. 本地方法区(线程私有) ,Native 方法相关的区域。
  4. 堆(Heap-线程共享),程序创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。
  5. 方法区(线程共享、永久代),存储了被 JVM 加载的类信息、常量、静变量、即时编译器编译后的代码等数据。

6.2 gc主要做的事情?

  1. 哪些对象是垃圾?
  2. 什么时候回收?
  3. 怎么回收?

6.3 JVM如何判断哪些对象是垃圾(死亡)?

使用到的算法有2种

  1. 引用计数算法
  2. 可达性分析算法,采用的是可达性分析算法
    可达性分析算法的根节点:线程中栈帧局部变量的引用,方法区中的引用,JNI中的引用;从以上区域开始搜索,不可达的对象为垃圾对象

6.4 垃圾收集算法有哪些?

  1. 标记清除算法,缺点:效率低,内存碎片化严重
  2. 复制算法,解决了内存碎片化的问题,缺点是:内存只能使用一半
  3. 标记整理算法,解决了复制算法的缺陷
  4. 分代收集算法,新生代使用复制算法,老生代使用标记整算法

6.5 垃圾收集器有哪些?

  1. 串行垃圾收集器,只有一个线程执行垃圾回收,GC过程对外不工作。
  2. 并发垃圾收集器,多个线程执行垃圾回收,且GC过程对外不工作
  3. 并发垃圾收集器,多个线程执行垃圾回收,GC过程对外工作
  4. G1垃圾收集器,默认使用该收集器进行垃圾回收

6.6 介绍下G1垃圾收集器?

  1. G1收集器是JVM默认的垃圾收集器,G1收集器是并行垃圾收集器。G1收集器可以与应用程序同时运行。
  2. G1收集器使用分代收集算法,并将整个堆空间划分为多个大小相等的区域,每块区域的垃圾回收都是独立的,并且在垃圾回收时优先回收垃圾最多的区域。
  3. G1是高性能垃圾收集器。

6.8 JVM类加载过程?

  1. 加载,加载.class文件
  2. 验证,验证.class文件是否符合虚拟机规范
  3. 准备,为变量分配内存空间
  4. 解析与初始化,创建对象
  5. 使用
  6. 卸载

6.9 类加载器?

  1. 启动类加载器(bootstrap classLoader),负责加载 JAVA_HOME\lib目录下的类
  2. 扩展类加载器(extension classLoader),负责加载JAVA_HOME\lib\ext 目录下的类
  3. 应用程序类加载器(application classLoader),负责加载用户路径(classpath)上的类
  4. 自定义类加载器

6.10 双亲委派原则?

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成;当父类加载器反馈自己无法完成这个请求的时候,子类加载器才会尝试自己去加载。双亲委派原则保证了使用不同的类加载器最终得到的都是同样一个 Object 对象。

7 kafka篇

7.1 谈谈你对kafka的理解?

kafka 是一种高吞吐量、分布式、基于发布/订阅的消息系统。
kafka包括以下概念

  1. broker:Kafka 服务器,负责消息存储和转发
  2. topic:消息类别,Kafka 中消息都是以 topic 进行分类的,生产者生产消息,消费者消费消息,都是面向 topic的。
  3. partition:topic分区,一个topic可以有多个partition
  4. offset:偏移量,可以用于快速定位消息存储位置
  5. producer:生产者
  6. consumer:消费者
  7. Replica:副本
  8. leader 与 follower

7.2 kafka消费模式?

  1. 点对点模式(一对一)
  2. 发布-订阅模式(一对多)

7.3 kafka 存储机制?

Kafka 采取了分片和索引机制,将每个 partition 分为多个 segment,每个segment对应两个文件.index索引文件,.log数据文件。索引文件包括offset和元数据,元数据指向对应数据文件中 的物理偏移地址。

7.4 kafka分区策略?

  1. 调用客户端api,可以指定partition。
  2. 没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值
  3. 既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与 topic 的 partition 总数取余得到 partition 值

7.5 kafka是如何保证数据的可靠性?

  1. ack应答机制,kafka提供三种ack应答机制
    0: producer 不等待 broker 的 ack,broker 接收到还没有写入磁盘就已经返回,当 broker 故障时有可能丢失数据;
    1:producer 等待 broker 的 ack,partition 的 leader 落盘成功后返回 ack,如果在 follower同步成功之前 leader 故障,那么将会丢失数据;
    -1:producer 等待 broker 的 ack,partition 的 leader 和 follower 全部落盘成功后才返回 ack。但是如果在 follower 同步完成后,broker 发送 ack 之前,leader 发生故障,那么会造成数据重复。
  2. 副本数据同步策略:
    1:半数以上完成同步,就发送 ack,优点延迟低。
    2:全部完成同步,才发送ack(默认)

7.6 什么是ISR?

ISR表示与leader保持心跳、同步的follower集合。如果 follower长时间未向leader同步数据,则该follower将被踢出ISR。Leader 发生故障之后,就会从 ISR 中选举新的 leader。

7.7 什么是LEO?

指的是每个副本最大的 offset

7.8 什么是HW?

消费者能见到的最大的 offset,ISR 集合中最小的 LEO

7.9 follower故障恢复过程?

当follower 恢复后,follower 会读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步。

7.10 leader故障是如何保证数据的一致性?

leader 发生故障之后,会从 ISR 中选出一个新的 leader,为保证多个副本之间的数据一致性,其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader同步数据。

7.11 kafka如何保成Exactly Once?

  1. ACK 级别设置为-1,可以保证 Producer 到 Server 之间不会丢失数据。
  2. 0.11版本之后的Kafka,引入了幂等性(启动参数配置 enable.idompotence),它保证Producer 不论向 Server 发送多少次重复数据,Server 端都只会持久化一条。
  3. 开启幂等性的 Producer 在初始化的时候会被分配一个 PID,发往同一 Partition 的消息会附带 Sequence Number。而Broker 会对收到的消息<以PID, Partition, SeqNumber>作为key做缓存,当具有相同主键的消息提交时,Broker 只会持久化一条。

7.12 kafka是如何高效读写数据的?

  1. 顺序写磁盘,producer 生产数据,追加到log文件末端。顺序写之所以快,是因为它省去了大量磁头寻址的时间。
  2. 零复制技术

8 MySql 篇

8.1 mysql 引擎有哪几种?

  1. InnoDB,大部分开发者使用的引擎,底层存储结构是B+树。优点支持事物,支持外键,MVCC架构模式,发生灾难可以恢复等等。
  2. MyISAM,缺点是它不支持事务,不支持行级锁和外键,优点是执行读取操作的速度很快。
  3. Memory,使用存在内存中的内容来创建表,因为它的数据是放在内存中的,MEMORY 类型的表访问非常得快,但是一旦服务关闭,表中的数据就会丢失掉。

8.2 InnDb与MylSAM的区别?

  1. InnDb支持事务,MyISAM不支持事务
  2. InnDb支持表锁,行锁,间隙锁,MyISAM只支持表锁
  3. InnDb不支持全文索引,MyISAM支持全文索引
  4. InnDb支持灾难恢复,MyISAM不支持

8.3 介绍下索引?

  1. 索引是帮助 MySQL 高效获取数据的数据结构。
  2. 索引类型:普通索引,唯一索引,全文索引,组合索引,单列索引,多列索引

8.3 什么情况会造成索引失效?

  1. 组合索引不满足最左匹配原则
  2. 使用了select *
  3. 索引列参与了计算或者是索引列使用了函数
  4. 使用like语句,占位符%在左边索引会失效
  5. 类型隐式转换转换,比如所以是varchar类型,查询语句使用int,会造成索引失效
  6. or 或者in 的过滤字段太多,会造成索引失效
  7. is not null,查询条件使用is null时正常走索引,使用is not null时,不走索引
    等等

8.4 什么是最左前缀原则?

在组合索引中,检索数据时从组合索引的最左边开始匹配,如果中间存在间断,会导致索引失效。

8.5 项目中如何优化索引?

  1. 表结构上,合理的设计索引,防止索引过多影响检索效率。
  2. 优化sql,禁止使用select *,明确指定需要的列,防止索引失效;join关联查询尽可能的使用索引;关联查询控制在3张表以内,防止多表的join影响检索效率
  3. 优化sql之后,使用explain检查索引是否失效
  4. 测试过程中,如果检索效率很慢,可以从慢查询日志去分析
  5. 使用 show profile Mysql工具分析
  6. 等等

8.6 谈谈你对mysql性能优化的理解?

  1. 硬件及操作系统层面的优化
    硬件方面,CPU、可用内存大小、磁盘读写速度、网络带宽会影响mysql性能
  2. 架构设计层面的优化
    包括搭建 Mysql 主从集群,读写分离设计,分库分表,对热点数据做缓存都可以提高mysql性能
  3. MySQL 程序配置优化
    对my.cnf参数优化,比如最大连接池的配置,实际操作中可以参考官方文档或一些资料。
  4. SQL 语句的优化,参考8.5
  5. 等等

8.7 如何保证mysql与redis一致性?

一般情况下,Redis 用于做缓存层,主要目的是减少Mysql IO,还可以提升Mysql的 IO 性能

  1. 正常的使用场景,应用程序获取数据优先从redis获取,没命中则会去查mysql。当mysql发生变更,redis没有及时变更会存在数据不一致的现象。
  2. 常规的方案有
    先更新数据库,在更新缓存;或是先删除缓存,再更新数据库。以上这两种方案,还是无法保证mysql与redis数据的一致性,但对于大部分业务都可满足。
  3. 如果业务必须保证数据一致性,那么可以使用cancel组件监听mysql binlog日志把更新后的数据同步到redis。

8.8 mysql索引的优点与缺点?

  1. 索引,它可以高效从磁盘上检索数据的一种数据结构。在mysql,innodb引擎中,采用了 B+树的数据结构来实现索引和数据的存储。
  2. 索引的优点有很多:B+树的结构来存储数据,可以大大减少数据检索时的磁盘 IO 次数,从而提升数据查询的性能;通知唯一索引约束,可以保证数据表中每一行数据的唯一性。
  3. 当索引使用不合理,也会存在一些缺点:数据的增加、修改、删除,需要涉及到索引的维护,当数据量较大的情况下,会有不小的开销;一张表索引不能创建太多,否则造成的索引维护成本过高;

8.9 mysql 事务的隔离级别?

四种隔离级别

  1. 读未提交,可能会产生脏读、不可重复读、幻读。
  2. 读已提交,可能会产生不可重复读和幻读。
  3. 可重复读,可能会产生幻读。
  4. 串行化,不会产生安全性问题。
    mysql 默认事务隔离级别是 可重复读

8.10 mysql为什么使用B+树作为索引结构?

  1. 常规的数据库存储引擎,一般都是采用 B 树或者 B+树来实现索引的存储。
  2. B 树是一种多路平衡树,它的整个高度会相比二叉树来说,会矮很多。树的高度能够决定磁盘 IO 的次数,高度越低磁盘IO次数越少,查询效率越高。
  3. B+树是B树的增强版,做了一些优化。
    B+树的所有数据都存储在叶子节点,非叶子节点只存储索引。B+树非叶子节点不存储数据,所以每一层能够存储的索引数量会增加,所以在层高相同的情况下B+树存储的数据量要比 B 树要多,使得磁盘 IO 次数更少。
    使用B+树最终的目标是提高mysql的检索效率

你可能感兴趣的:(java)