java基础复习(四)
1.说一下HashMap、LinkedHashMap、concurrentHashMap、ArrayList、LinkedList的底层实现
1、HashMap是java数据结构中两大结构数组+链表+红黑树的组合。
HashMap底层数组,数组中的每一项又是一个链表。程序会先根据key的hashcode()方法返回值决定该Entry在数组中的
存储位置,如果该位置上没有元素,就会将元素放置在此位置上,如果两个Entry的key相同,会调用equals,返回值是true则覆盖原来的value值,返回false则会形成Entry链,位于头部。
2、ArrrayList的底层实现是数组,在执行add操作时,会先检查数组 大小是否可以容纳新的元素,如果不够就会进行扩容。然后会将原来的数据拷贝到新的数组中。
3、LinkedList底层是一个双向循环链表,其实现增删改查和数据结构中的操作完全相同,而且插入是有序的。
4、LinkedHashMap的底层结构式是双向链表,其他的逻辑处理与HashMap一致,同样没有锁保护,多线程使用时存在风险。
5、ConcurrentHashMap的底层数据结构:Synchronized + CAS +Node +红黑树.Node的val和next都用volatile保证,保证可见性,查找,替换,赋值操作都使用CAS(jdk1.8版本)
2.说一下 Callable 和 Runnable 的区别?
Runnable和Callable区别是否有返回值
Runnable接口不能获取线程的返回值,所以也就无法知道线程是否执行成功,而Callable接口获取到线程的返回值(Future对象),通过返回值可以判断线程是否执行成功或取消任务
callable可以抛出异常,runnable 方法不行
3.静态变量和实例变量的区别是什么?
区别一、定义不同
- 静态变量定义时候前面要加上static,实例变量不需要加。
区别二、初始化不同
- 静态变量随着类的加载而初始化,实例变量是new对象后才进行初始化。
区别三、内存位置不同(JDK1.8后都存在堆中了)
- 静态变量存储在静态变量区,实例变量存储在堆内存区
区别四、调用方式不同
- 静态变量通过类名调用,实例变量通过对象调用
区别五、生命周期不用
- 静态变量随着类的加载而加载,虚拟机停止运行时,静态变量周期结束。实例变量随着对象的产生
4.说一说 Redis 缓存穿透的解决方案?
了解下 缓存穿透:
key 对应的数据在数据源并不存在,每次针对此 key 的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户 id 获取用户信息,不论缓存还是数据库都没有,违法分子利用漏洞进行攻击可能压垮数据库
- 1.查缓存没有的数据 比如 id:-1 命中率降低
- 2.导致大量请求直接访问DB(数据库)压力过大
解决方案:
-
1.对空值缓存
- 如果一个查询返回的数据为空(不管是数据是否不存在),我们把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
-
2.设置可访问的名单(白名单)
- 使用 bitmaps 类型定义一个可以访问的名称, 名单 id 作为 bitaps 的偏移量,每次访问 和 bitmap 里面的 id 进行比较,如果访问 id 不在 bitmaps 里面,进行拦截,不允许访问
-
3.采用布隆过滤器
- 简介:(布隆过滤器(Bloom Filter)是 1970 年由布隆提出的它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希数))
- 有缺点 1.性能占用 2.存在误判 3.删除困难
- 最大数值非常大的时候,布隆过滤器需要的空间少的多
- 查到元素不存在容器中,hash得到k个位置上的值都是1,可以建立白名单存储可能会误判的元素
- 放入容器的元素映射到bit数组的k个位置上是1,删除时不能直接重置为0,可能会影响元素的判断 ,采用Counting Bloom Filter
-
4.进行实时监控
- 当发现 Redis 的命中率急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单
5.描述 Cookie 和 Session 的作用、区别和应用范围,Seesion 工作原理
Cookie 和 Seesion 的区别:
1.session存放在服务器,cookie存放在客户端
2.session会随着会话的结束而关闭,cookie则存放在客户端浏览器上长期有效
3.seesion 保存的是对象,cookie保存的是字符串
4.存放在cookie里的信息容易泄露,通常只保存重要的信息,重要的信息放在session中(如token)
6.ArrayList扩容时扩容多少?
在每次存入数据后会进行检查,如果超过容量最大值,则以原容量的1.5倍继续扩容
初始容量:10
扩容一次:10*1.5=15
扩容两次:(10*1.5)*1.5=22.5
7.java垃圾回收算法有哪些?
1.标记-清除算法
java堆内存中的对象实例是动态分配和回收的,java虚拟机提供了多种不同的内存回收算法来满足不同的内存管理需求
标记-清除算法是最基础的内存回收算法
主要流程:
- 标记所有活跃对象
- 清除所有未被标记的对象
缺点:会产生内存碎片,导致内存利用率降低
2.复制算法
复制算法是将内存分为两部分,每次只使用其中一部分。当这部分内存用完后,将其中的活跃对象复制到另一部分中,然后清除这部分内存
复制算法的优点是可以避免内存碎片,缺点是需要消耗额外的内存空间
复制算法主要用于新生代内存回收
3.标记-整理算法
标记-整理算法是将内存分为两部分,一部分存储活跃对象,另一部分为未使用的内存空间。当内存空间不足时,先进行标记活跃对象,然后将活跃对象整理到未使用的内存空间中,最后清除未使用的内存空间
标记-整理算法可以避免内存碎片,但其缺点是需要移动内存中的对象,因此效率较低
标记-整理算法主要用于老年代内存回收
4.分代回收算法
分代回收算法是根据对象的生命周期将内存分为不同的代,每个代使用不同的内存回收算法。
新生代中一般使用复制算法,由于新生代中大部分对象的生命周期很短,因此这种算法的效率较高。而老年代中一般使用标记-整理算法,由于老年代中存储的对象生命周期较长,因此算法能够有效地减少垃圾回收的次数,提高Java应用的性能
5.G1算法
G1算法是一种面向服务端应用的垃圾回收算法。它将堆内存划分为多个大小相等的区域(Region),每个区域既可以是新生代也可以是老年代。G1算法会根据应用程序的内存使用情况动态地调整各个区域的大小
在G1算法中,垃圾回收器不再按照新生代和老年代的划分进行垃圾回收,而是将整个堆空间一起考虑。G1算法使用了类似分代回收算法的思想,将堆空间分为多个区域,每个区域的回收策略可以不同,这样就能够更加灵活地进行内存回收
G1算法的优点是能够在保证垃圾回收效率的同时,避免了内存碎片的问题。同时,由于G1算法能够动态调整各个区域的大小,因此可以更好地适应不同的应用场景
8.JDK和JRE 有什么区别?
JDK是java开发工具包,里面包含了编译器、调试器和其他工具
提供了java开发环境和运行环境(JDK中也包含JRE,因为编写好的程序也要测试运行)
JRE是运行环境,里面包含jvm(Java虚拟机)和基础类库
9.Redis 缓存和 JVM 缓存有什么区别?
jvm缓存,
- 会有回收问题
- 每次启动时都需要灌入数据
redis缓存
- 有内存淘汰策略(8种)
- 通过RDB快照 快速加载
- redis和本地缓存之间还有堆外缓存
这三种缓存可以实现出一个多级缓存架构
10.为什么 Redis 使用单线程性能会优于多线程?
Redis 是单线程 + 多路 IO 复用技术
- 避免上下午切换和竞争条件
- 也不存在多线程切换而消耗cpu
- 不存在锁问题