Java面试

目录

1.红黑树知道吗?介绍一下?

2.默认加载因子是多少?为什么不是 0.6 或者 0.8 ?

3.HashMap底层存储结构了解吗?

4.为什么达到8才转为红黑树,而不是直接就用红黑树?

5.红黑树后还会退化为链表吗?什么时候退化?

6. JDK1.8  HashMap 的插入流程?

7. ConturrentHashMap JDK1.8 插入过程?

8. JDK1.8 HashMap扩容机制?

9. 1.7 1.8介绍ConcurrentHashMap实现?

10. Map有哪些?区别?

11. HashMap 和 TreeMap 区别?

12.ArrayList用过吗,介绍一下?

13.LinkedList了解吗?介绍一下

14.LinkedList和ArrayList区别?

15.说一下乐观锁和CAS

16.String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

17.抽象类和接口

18.AOP概念?

19.AOP原理?

20.Spring IOC和DI?

21.慢SQL,慢SQL解决?

1、什么是慢SQL?

2、慢SQL解决?

1、索引设置不合理

2.SQL语句的优化

22.面向对象的三大特性


1.红黑树知道吗?介绍一下?

为了提高二叉树的查找效率,引入了二叉查找树,但是二叉查找树可能会退化成单链表,时间复杂度为 n。因此引入了平衡二叉树,平衡二叉树的左右子树深度之差不会超过1,但是维护二叉树平衡的代价太大,因此又出现了红黑树。红黑树是一种平衡的二叉查找树

红黑树特点:

  1. 节点是红色或者是黑色
  2. 根 是黑色的
  3. 叶子 是黑色的。
  4. 红色节点的子节点是黑色的,所以不存在父子两个节点全是红色
  5. 从任意节点到其每个叶子节点,所有简单路径上黑色节点的数量是相同的。

2.默认加载因子是多少?为什么不是 0.6 或者 0.8 ?

默认的加载因子是0.75,0.75是对空间和时间效率的一个平衡选择。较高的值空间开销小,哈希冲突大,查找效率低;较低的值,空间开销大,哈希冲突小,查找效率高。

如果内存很多,对时间效率要求很高,可以降低负载因子的值。

如果内存少,对时间效率要求不高,可以增加负载因子的值。

3.HashMap底层存储结构了解吗?

在 JDK1.7 中,是“数组+链表”,在 JDK1.8 中,是“数组+链表+红黑树”。数组是主体,数组中的节点存的是 KeyValue,Hash值next指针

HashMap使用链表解决哈希冲突,当链表过长,则会严重影响 HashMap 的性能,红黑树搜索时间复杂度是 logn,而链表是 n。因此,JDK1.8 ,引入了红黑树

1)当链表长度达到 8 且数组长度大于等于 64 ,会转成红黑树。

2)如果数组的长度小于 64,就先进行扩容,而不是转换为红黑树。

4.为什么达到8才转为红黑树,而不是直接就用红黑树

因为元素个数小于 8 个的时候,做查询操作,链表结构是可以保证查询性能的。但如果一开始就用红黑树,在节点很少时,红黑树的查找性能优势并不明显,还要付出 2 倍空间的代价。

选择 8的原因:根据泊松分布,链表中元素个数为 8 时的概率非常小,再多的就更少了,所以在选择 8,是根据概率统计而定的,是在时间和空间上权衡的结果。

5.红黑树后还会退化为链表吗?什么时候退化?

小于等于6的时候退转为链表。中间有个差值7可以防止链表和树之间频繁的转换。如果设计成超过8 变成红黑树。小于8转成链表当节点个数在 8 徘徊时,如果HashMap不停的插入、删除元素,就会频繁进行红黑树和链表的转换,造成性能的损耗。

6. JDK1.8  HashMap 的插入流程?

1、根据key计算Hash值。

2、判断数组的或长度0,那就 resize 初始化。

3、通过位运算计算索引位置。

  如果当前位置为空则构造新结点插入数组,否则

  如果当前是红黑树,就插入到树中

  如果是链表,则遍历链表,在遍历过程中若遇到keyhash都相等的元素,而且其value为空或onlyIfAbsentfalse就用新值覆盖旧值,否则尾插入链表; 如果链表长度大于8并且数组的长度大于等于64,则将这个结构转换为红黑树;

5、最后对变量modCount加1,表示结构性修改。如果实际大小大于阈值扩容

7. ConturrentHashMap JDK1.8 插入过程?

1、根据 key 计算出 hash 值。
2、判断数组为空或者长度为0,就初始化(自旋+ CAS)。
3、通过位运算计算索引位置,定位到要插入的Hash桶,拿到首节点 f并判断:
1)如果为 null ,则通过 CAS 的方式尝试添加,失败则自旋保证成功
2)如果数组正在扩容,则当前线程先帮助扩容,再插入数据;
3)如果都不满足 ,synchronized 锁住 f 节点,判断是链表还是红黑树,若是链表结点,则遍历链表,在遍历过程中若遇到key和hash都相同的节点,而且onlyIfAbsent为false,就用新值覆盖旧值,否则尾插入链表;若是红黑树则插入树中
3、当在链表长度达到 8 的时候,数组扩容或者将链表转换为红黑树。

8. JDK1.8 HashMap扩容机制

三个条件:

1. 空数组,首次插入节点,会触发扩容。

2. 将元素尾插链表后,如果链表长度达到8,并且数组长度小于64,则扩容。

3. 添加后,如果数组中元素超过阈值,(默认为0.75),则扩容。

扩容过程是:

1.首先获取老数组的旧容量和旧阈值,初始化新容量和新阈值为0。

2.如果老数组的旧容量大于0,判断旧容量是否超过最大容量,如果是就将阈值设置为MAX_VALUE,return旧数组;否则就设置新容量为旧组容量的2倍【如果扩容之后的新容量小于最大容量并且旧容量大于等于默认初始化容量(16),那么新阀值设置为老阀值的2倍;】

如果旧容量不大于0但是旧阈值大于0那么设置新容量为该阀值

如果旧容量和旧阈值都不大于0,说明是无参构造的map,并且是第一次添加元素,直接设置新容量为默认容量(16),新阈值(16*0.75=12);

4.根据新容量,创建新数组,若老数组不空,遍历数组中每个不为空的哈希桶

  1. 如果哈希桶只有一个节点则直接重新分配新位置。
  2. 如果是红黑树则调用split拆分树
  3. 否则是一个链表,就看哪些索引位置不变,哪些分到新的索引位置。

依据是:判断(e.hash & oldCap) 是否为0是0的话,则索引不变,是1的话索引变成“原索引 + oldCap ,尾插到对应的哈希桶链表

5.最后返回新数组

9. 1.7 1.8介绍ConcurrentHashMap实现?

JDK1.7 中的 ConcurrentHashMap 是由 Segment 数组和 HashEntry 数组组成,即把 哈希桶数组切分成Segment数组,从而将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问,从而实现了并发访问。Segment 是 ConcurrentHashMap 的一个内部类,继承了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。Segment 默认为 16,也就是并发度为 16,它使用HashEntry数组存放数据。HashEntry,也是一个静态内部类,其中,用 volatile 修饰了节点中的 value 和 next值,保证了多线程环境下数据获取时的可见性

JDK1.8   是 数组+链表+红黑树结构;采用CAS 。锁的是头或者根,只要锁住这个链表头节点(红黑树的根节点),就不会影响其他的哈希桶数组元素的读写,大大提高了并发度。

插入数据、扩容的时候均加锁,查找的时候不加锁

10. Map有哪些?区别?

HashMap, HashTable,TreeMap,

1、HashMap,允许键为和值为空,线程不安全。

2、Hashtable不允许 键或者值为空;它通过synchronized保证了线程安全。

3、TreeMap能够把记录 按键排序,默认升序排序。也可以自定义排序比较器。遍历TreeMap时,得到的记录是排过序的。

11. HashMap 和 TreeMap 区别?

TreeMap 和HashMap 都继承自AbstractMap ,但TreeMap它还实现了NavigableMap接口和SortedMap 接口。所以TreeMap 有了对集合内元素的搜索的能力,和根据键排序的能力。默认是按 key 的升序排序,也可以自定义排序的比较器。

12.ArrayList用过吗,介绍一下?

ArrayList实现了List接口,存放的数据是有序可重复的,允许放入null元素,底层是Object数组,默认是10,扩容是1.5倍。

还实现了RandomAccess 接口,所以支持快速随机访问。但是没有实现synchronized 同步,所以是线程不安全的。

13.LinkedList了解吗?介绍一下

LinkedList同时实现了List接口和Deque接口,所以它可以看作一个顺序容器,或者是队列,或者是

它基于双向链表实现,通过firstlast引用分别指向链表的第一个和最后一个节点每一个节点包含三部分(数据,前向指针,后向指针),它不要求空间是连续的。

14.LinkedList和ArrayList区别?

1.底层数据结构: 

Arraylist 是 Object 数组;LinkedList 是 双向链表 

2.插入和删除:

ArrayList 不适合插入删除元素,LinkedList 适合插入删除元素

3.是否支持快速随机访问:

ArrayList 支持快速随机访问,LinkedList  不支持。

4. 内存空间要求及占用: 

ArrayList 在 list 列表的结尾会预留一定的存储空间,需要连续的内存空间;而 LinkedList 的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放前驱、后继以及数据),但它不要求内存空间连续,也不需要预留空间。

15.说一下乐观锁和CAS

乐观锁一般采用CAS算法

CAS算法思路:

1.该算法认为不同线程对变量操作时,产生的竞争的情况比较少

2.算法核心时对当前读取的变量值和内存中的旧值进行比较

3.相等,代表没有其他线程对这个变量做修改,所以将变量值设为新值。

4.否则,认为有其他线程对变量进行了修改,不进行任何操作。

16.String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

String 字符数组保存字符串,java 9 之后,用byte数组保存字符串。StringBuffer StringBuilder继承于AbstractStringBuilder类, 字符数组保存字符串。

String 用final修饰,所以不可变。StringBuffer StringBuilder 没有用 final 修饰,所以可变。

String 因为对象不可变,所以线程安全。StringBuffer 加了同步锁,所以线程安全,StringBuilder 没有加同步锁,所以线程不安全。

少量数据,用String。单线程,大量数据,用StringBuilder。多线程,大量数据,用StringBuffer。

17.抽象类和接口

1.abstract;interface

2.extends继承;implements实现

3.可以有构造方法;不能有构造方法

4.方法的访问控制修饰符:任意;默认是public,不允许private、protected,java9开始,允许private

5.只能继承一个抽象类;可以实现多个接口

6.字段声明任意;字段声明默认static、final

18.AOP概念?

AOP,即面向切面编程能够将那些与业务无关,却为多个业务模块所共同调用的逻辑封装起来,例如事务处理、日志管理、权限控制等实现在不修改源代码的情况下给程序动态添加功能。从而减少系统的重复代码降低模块间的耦合度。

Spring AOP就是基于动态代理,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理。

19.AOP原理?

在spring生命周期中,有一个初始化bean方法initializeBean(),在内置的后置处理器中,有一个后置处理器叫做AnnotationAwareAspectJAutoProxyCreator,其后置处理器逻辑中调用了wrapIfNecessary()方法,在wrapIfNecessary方法中,首先判断这个bean是否有切点,没有则不需要代理,直接返回Bean,如果有切点则调用createProxy方法,在其最后一步有一个getProxy方法来获取代理。getProxy方法可以看出,其有两个实现类 一个就是JDK方式,一个就是Cglib方式,以JDK方式最后调用的是JDK的Proxy.newProxyInstance方法来生成动态代理,CGLIB则是调用ASM来实现动态代理的。

20.Spring IOC和DI?

IOC就是控制翻转,是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架,对象的生命周期,对象之间的相互依赖关系都由 IoC 容器来负责管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好注解即可,完全不用考虑对象是如何被创建出来的。

DI 指依赖注入,注入某个对象所需要的外部资源(包括对象,数据等)

IoC 是设计思想,DI 是具体的实现方式;

21.慢SQL,慢SQL解决?

1、什么是慢SQL?

就是跑的很慢的SQL,会占用资源,阻塞其他正常的SQL

2、慢SQL解决?

两个方面:索引设置不合理、SQL语句有问题

1、索引设置不合理

1.区分度低

2.创建过多索引

3.常用查询字段建索引

4.常排序、分组、去重字段建索引 ,对于经常使用order by、group by、distinct 和union 等操作的字段建立索引,可以有效借助B+树来加速执行

2.SQL语句的优化

会导致索引失效的情况

1.使用函数、表达式运算 where id + 1 = 10、隐式类型转换 where name = 100

2.使用not in 、not exist、!= (比如where name != ‘欣欣’)

3.or连接,一旦有or连接的字段没有设置索引,就会全表扫描

4.联合索引不遵循最左匹配原则

建议

1.使用联合索引,而不是多个单独的索引、遵循最左匹配

2.使用覆盖索引,减少回表

3.使用连接代替子查询、关联查询的时候,小表在前,大表在后(因为第一张表涉及全表扫描,所以先扫小表,效率高)

4.调整where子句中的连接顺序,把过滤数据多的条件放前面

5.开启慢日志,设置慢SQL的执行时间阈值、用explain分析SQL语句

22.面向对象的三大特性

封装、继承、多态

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值