java基础(持续更新)
2022-03-07 18:06:32 0 举报
AI智能生成
点我主页,涵盖 Java 后面面试所有知识点,本人持续整理,目前已帮助20+人拿到 大厂Offer
作者其他创作
大纲/内容
java基础
集合框架:Collection 和 Map
Collection 大家族
阻塞队列:BLockingQueue
阻塞添加
所谓的阻塞添加是指当阻塞队列元素已满时,队列会阻塞加入元素的线程,直到队列元素不满时才重新唤醒线程执行元素加入操作。
阻塞删除
阻塞删除是指在队列元素为空时,删除队列元素的线程将被阻塞,直到队列不为空再执行删除操作(一般都会返回被删除的元素)
所谓的阻塞添加是指当阻塞队列元素已满时,队列会阻塞加入元素的线程,直到队列元素不满时才重新唤醒线程执行元素加入操作。
阻塞删除
阻塞删除是指在队列元素为空时,删除队列元素的线程将被阻塞,直到队列不为空再执行删除操作(一般都会返回被删除的元素)
LinkedList - 链表实现
常见面试题
ArrayList - 数组实现
常见面试题
List 扩容?
最小初始容量 11
超过 11 使用 System.arrayCopy 一个新数组,进行 1.5 倍扩容 -- oldCapacity + (oldCapacity >> 1)
ArrayList 线程安全问题?
list.add() 操作遇见扩容,可能会丢失数据
如何解决?
CopyOnWriteArrsyList
高并发写时,CopyOnWriteArrayList为何这么慢呢?因为其每次add时,都用Arrays.copyOf创建新数组,频繁add时内存申请释放性能消耗大。
hashSet - hashmap实现,
Map 大家族
文章推荐:(先看文章,再看总结)
哈希表哪家强?几大编程语言吵起来了!
hash 冲突的几种解决方式;
拉链法
双重散列法
内置多个 hash 算法,一个冲突就用另一个
开方寻址法
如果发现了冲突,就按照制定的策略从这个位置往后找,直到找到有空的位置存储
HashMap
Map家族中最常使用的一个。
Map 遍历
map.entrySet()
推荐这一种嗷
map.keySet()
底层实现: 数组+链表+红黑树。
1. 确定哈希桶数组索引位置
HashMap 定位数组索引位置:
这里的Hash算法本质上就是三步:取key的hashCode值、高位运算。
这里的Hash算法本质上就是三步:取key的hashCode值、高位运算。
问:为什么计算出hascode 后不直接与数组长度取模?
模运算的消耗还是比较大的!
模运算的消耗还是比较大的!
2. 分析HashMap的put方法
时序图分析
3. 扩容机制- put 方法(得物)
为什么需要扩容?
扩容的负载因子是 0.75,即当数组容量cap大小达到 cap x 0.75 就会扩容 2 倍
据我的经验,日常使用中,大部分使用到 HashMap 的情况,大小都不会超过 1000
Hash 环问题
java7 中put 操作 的 resize 容易出现 死循环,
会在数组上形成一个环形链表。
会在数组上形成一个环形链表。
因为 jdk7 在扩容的时候认为新插入的元素访问概率更高,
所以在扩容时,会把链表翻转。所以在多线程下就会形成hash环
所以在扩容时,会把链表翻转。所以在多线程下就会形成hash环
单链表如何翻转?(不借助其它数据结构)
java8 中是如何解决 hash 环问题的?
jdk 7 头插法
jdk 7 头插法为什么会产生hash 环?
jdk 8 尾插法 解决hash 环
线程安全的HashMap(并发包提供的,不属于Map体系)
ConcurrentHashMap
并发安全问题分析:
多线程
共享资源
java1.7 和 java1.8实现不同
java1.7
底层实现
采用分段锁,每一段数据分配一个Segment(锁),
当线程占用其中一个Segment时,其他线程可正常访问其他段数据
Segment 继承了ReentrantLock,是一个AQS对象
当线程占用其中一个Segment时,其他线程可正常访问其他段数据
Segment 继承了ReentrantLock,是一个AQS对象
虽然已经解决了HashMap并发安全问题,但是查询遍历链表效率太低。
java 1.8
底层实现
抛弃了原有的 Segment 分段锁,而采用了 CAS + volatile 来保证并发安全性。
8中如何解决并发的
总结:
HashMap 什么情况下会发生 OOM?
一直 put
扩容时
HashMap 为什么在链表长度为 8 的时候转红黑树,为啥不能是 9 是 10?
源码中有一段注释, 说的很明白:
hashcode碰撞次数的泊松分布有关,
在负载因子0.75(HashMap默认)的情况下,单个hash槽内元素个数为8的概率小于百万分之一
hashcode碰撞次数的泊松分布有关,
在负载因子0.75(HashMap默认)的情况下,单个hash槽内元素个数为8的概率小于百万分之一
- 1、table 的初始化时机是什么时候,初始化的 table.length 是多少、阀值(threshold)是多少,实际能容下多少元素
- 2、什么时候触发扩容,扩容之后的 table.length、阀值各是多少?
- 3、table 的 length 为什么是 2 的 n 次幂?为什么为什么要先高16位异或低16位再取模运算?
- 4、求索引的时候为什么是:h&(length-1),而不是 h&length,更不是 h%length
- 5、 Map map = new HashMap(1000); 当我们存入多少个元素时会触发map的扩容; Map map1 = new HashMap(10000); 我们存入第 10001个元素时会触发 map1 扩容吗? 会的,动态扩容! 最大容量为: 1024*0.75 。
- 6、为什么加载因子的默认值是 0.75,并且不推荐我们修改
7. HashMap 多线程会有什么问题?
8. 链表转红黑树的阈值为什么选8?为什么用红黑树做优化?
1.
table 的初始化时机是什么时候
一般情况下,在第一次 put 的时候,调用 resize 方法进行 table 的初始化(懒初始化,懒加载思想在很多框架中都有应用!)
初始化的 table.length 是多少、阀值(threshold)是多少,实际能容下多少元素
默认情况下,table.length = 16;
默认情况下,threshold = 12;
默认情况下,能存放 12 个元素,当存放第 13 个元素后进行扩容
table 的初始化时机是什么时候
一般情况下,在第一次 put 的时候,调用 resize 方法进行 table 的初始化(懒初始化,懒加载思想在很多框架中都有应用!)
初始化的 table.length 是多少、阀值(threshold)是多少,实际能容下多少元素
默认情况下,table.length = 16;
默认情况下,threshold = 12;
默认情况下,能存放 12 个元素,当存放第 13 个元素后进行扩容
2.
什么时候触发扩容,扩容之后的 table.length、阀值各是多少
当 size > threshold 的时候进行扩容
扩容之后的 table.length = 旧 table.length * 2,
扩容之后的 threshold = 旧 threshold * 2
什么时候触发扩容,扩容之后的 table.length、阀值各是多少
当 size > threshold 的时候进行扩容
扩容之后的 table.length = 旧 table.length * 2,
扩容之后的 threshold = 旧 threshold * 2
// oldCap左移一位; newCap = 16 << 1 = 32;
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && // oldCap左移一位; newCap = 16 << 1 = 32;
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold // newThr = 12 << 1 = 24;
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold // newThr = 12 << 1 = 24;
3.
table 的 length 为什么是 2 的 n 次幂?
为了利用位运算 & 求 key 的下标(我们可以发现,只有 & 数是1时,& 运算的结果与被 & 数一致)
1&1=1;
0&1=0;
1&0=0;
0&0=0;
为什么为什么要先高16位异或低16位再取模运算?
右移·16 位 再异或运算,就能保证高低位都参与到取模运算中;
而取模运算 是通过 hash&(length-1)实现的!
只是为了降低hash冲突的几率(看图)
table 的 length 为什么是 2 的 n 次幂?
为了利用位运算 & 求 key 的下标(我们可以发现,只有 & 数是1时,& 运算的结果与被 & 数一致)
1&1=1;
0&1=0;
1&0=0;
0&0=0;
为什么为什么要先高16位异或低16位再取模运算?
右移·16 位 再异或运算,就能保证高低位都参与到取模运算中;
而取模运算 是通过 hash&(length-1)实现的!
只是为了降低hash冲突的几率(看图)
4.
求索引的时候为什么是:hash&(length-1),而不是 hash&length,更不是 hash%length
hash%length 效率不如位运算快
hash%length 会提高碰撞几率,导致 table 的空间得不到更充分的利用、降低 table 的操作效率
原理:
为了利用位运算 & 求 key 的下标(我们可以发现,只有 & 数是1时,& 运算的结果与被 & 数一致)
1&1=1;
0&1=0;
1&0=0;
0&0=0;
求索引的时候为什么是:hash&(length-1),而不是 hash&length,更不是 hash%length
hash%length 效率不如位运算快
hash%length 会提高碰撞几率,导致 table 的空间得不到更充分的利用、降低 table 的操作效率
原理:
为了利用位运算 & 求 key 的下标(我们可以发现,只有 & 数是1时,& 运算的结果与被 & 数一致)
1&1=1;
0&1=0;
1&0=0;
0&0=0;
5.
数组大小需要保证是 2 的 N 次幂!!!
从构造方法的逻辑可以看出,HashMap 并不是直接使用外部传递进来的 initialCapacity,而是经过了 tableSizeFor() 方法的处理,再赋值到 threshole 上。
在 tableSizeFor() 方法中,通过逐步位运算,就可以让返回值,保持在 2 的 N 次幂。以方便在扩容的时候,快速计算数据在扩容后的新表中的位置。
那么当我们从外部传递进来 1w 时,实际上经过 tableSizeFor() 方法处理之后,就会变成 2 的 14 次幂 16384,再算上负载因子 0.75f,实际在不触发扩容的前提下,可存储的数据容量是 12288(16384 * 0.75f)。
数组大小需要保证是 2 的 N 次幂!!!
从构造方法的逻辑可以看出,HashMap 并不是直接使用外部传递进来的 initialCapacity,而是经过了 tableSizeFor() 方法的处理,再赋值到 threshole 上。
在 tableSizeFor() 方法中,通过逐步位运算,就可以让返回值,保持在 2 的 N 次幂。以方便在扩容的时候,快速计算数据在扩容后的新表中的位置。
那么当我们从外部传递进来 1w 时,实际上经过 tableSizeFor() 方法处理之后,就会变成 2 的 14 次幂 16384,再算上负载因子 0.75f,实际在不触发扩容的前提下,可存储的数据容量是 12288(16384 * 0.75f)。
6.
为什么加载因子的默认值是 0.75,并且不推荐我们修改
如果loadFactor太小,那么map中的table需要不断的扩容,扩容是个耗时的过程
如果loadFactor太大,那么map中table放满了也不不会扩容,导致冲突越来越多,解决冲突而起的链表越来越长,效率越来越低
而 0.75 这是一个折中的值,是一个比较理想的值
为什么加载因子的默认值是 0.75,并且不推荐我们修改
如果loadFactor太小,那么map中的table需要不断的扩容,扩容是个耗时的过程
如果loadFactor太大,那么map中table放满了也不不会扩容,导致冲突越来越多,解决冲突而起的链表越来越长,效率越来越低
而 0.75 这是一个折中的值,是一个比较理想的值
7.
HashMap在并发编程环境下有什么问题啊?
(1)多线程扩容,引起的死循环问题 --》 1.7 头插法
(2)多线程put的时候可能导致元素丢失
(3)put非null元素后get出来的却是null
HashMap在并发编程环境下有什么问题啊?
(1)多线程扩容,引起的死循环问题 --》 1.7 头插法
(2)多线程put的时候可能导致元素丢失
(3)put非null元素后get出来的却是null
8. 链表转红黑树的阈值为什么选8?为什么用红黑树做优化?
采用均匀 Hash 算法的话, 桶的长度超过8的概率非常非常小
通过源码我们得知HashMap源码作者通过泊松分布算出,当桶中结点个数为8时,出现的几率是亿分之6的,因此常见的情况是桶中个数小于8的情况,此时链表的查询性能和红黑树相差不多,因为转化为树还需要时间和空间,所以此时没有转化成树的必要。
既然个数为8时发生的几率这么低,我们为什么还要当链表个数大于8时来树化来优化这几乎不会发生的场景呢?
首先我们要知道亿分之6这个几乎不可能的概率是建立在什么情况下的 答案是:建立在良好的hash算法情况下,例如String,Integer等包装类的hash算法、如果一旦发生桶中元素大于8,说明是不正常情况,可能采用了冲突较大的hash算法,此时桶中个数出现超过8的概率是非常大的,可能有n个key冲突在同一个桶中,此时再看链表的平均查询复杂度和红黑树的时间复杂度,就知道为什么要引入红黑树了,
举个例子,若hash算法写的不好,一个桶中冲突1024个key,使用链表平均需要查询512次,但是红黑树仅仅10次,红黑树的引入保证了在大量hash冲突的情况下,HashMap还具有良好的查询性能
采用均匀 Hash 算法的话, 桶的长度超过8的概率非常非常小
通过源码我们得知HashMap源码作者通过泊松分布算出,当桶中结点个数为8时,出现的几率是亿分之6的,因此常见的情况是桶中个数小于8的情况,此时链表的查询性能和红黑树相差不多,因为转化为树还需要时间和空间,所以此时没有转化成树的必要。
既然个数为8时发生的几率这么低,我们为什么还要当链表个数大于8时来树化来优化这几乎不会发生的场景呢?
首先我们要知道亿分之6这个几乎不可能的概率是建立在什么情况下的 答案是:建立在良好的hash算法情况下,例如String,Integer等包装类的hash算法、如果一旦发生桶中元素大于8,说明是不正常情况,可能采用了冲突较大的hash算法,此时桶中个数出现超过8的概率是非常大的,可能有n个key冲突在同一个桶中,此时再看链表的平均查询复杂度和红黑树的时间复杂度,就知道为什么要引入红黑树了,
举个例子,若hash算法写的不好,一个桶中冲突1024个key,使用链表平均需要查询512次,但是红黑树仅仅10次,红黑树的引入保证了在大量hash冲突的情况下,HashMap还具有良好的查询性能
ConcurrentHashMap:
0. HashMap 并发情况下会有什么线程安全问题?
1. ConcurrentHashMap的实现原理
2. ConcurrentHashMap1.7和1.8的区别?
3. ConcurrentHashMap使用什么技术来保证线程安全
4. ConcurrentHashMap的put()方法
5. ConcurrentHashmap 不支持 key 或者 value 为 null 的原因?
6. put()方法如何实现线程安全呢?
7. ConcurrentHashMap扩容机制
8. ConcurrentHashMap的get方法是否要加锁,为什么?
9. 为什么使用ConcurrentHashMap
10. ConcurrentHashMap迭代器是强一致性还是弱一致性?HashMap呢?
11. 经典面试题:为什么 ConcurrentHashMap 的读操作不需要加锁?
0. HashMap 并发情况下会有什么线程安全问题?
1. ConcurrentHashMap的实现原理
2. ConcurrentHashMap1.7和1.8的区别?
3. ConcurrentHashMap使用什么技术来保证线程安全
4. ConcurrentHashMap的put()方法
5. ConcurrentHashmap 不支持 key 或者 value 为 null 的原因?
6. put()方法如何实现线程安全呢?
7. ConcurrentHashMap扩容机制
8. ConcurrentHashMap的get方法是否要加锁,为什么?
9. 为什么使用ConcurrentHashMap
10. ConcurrentHashMap迭代器是强一致性还是弱一致性?HashMap呢?
11. 经典面试题:为什么 ConcurrentHashMap 的读操作不需要加锁?
0. HashMap 并发情况下会有什么线程安全问题?
JDK7: 死循环-> 头插法
put 时 如果产生扩容, 并发可能会丢失数据.
1. ConcurrentHashMap的实现原理:
JDK 7:Segment(分段锁)+HashEntry
使用分段锁,将数据分成许多个 Segment, 每个 Segment 单独加锁, 每个 Segment 里面都对应一个链表
好处:
对一个 Segment 加锁不会影响到的其它的 Segment 的读写, 理论上最大可以支持 Segment 数量的并发
坏处:
1. 要经过两次 Hash ,第一次找到对应的 Segment,进行加锁,第二次 Hash 定位到元素所在链表
2. 查询遍历链表效率太低
2. 查询遍历链表效率太低
JDK8: CAS+volatile +sychronized
JDK8 中的 ConcurrentHashMap 参考了 HashMap 的实现,基本实现了无锁操作,大量运用的 CAS 和 voltile 实现并发读写
JDK8 中放弃了 Segment 采用了 Node 这个数据结构
每个键值对(key-value)都存储在一个Node中。同时Node也有一些子类,TreeNodes用于树结构中(当链表长度大于8时转化为红黑树);TreeBins用于维护TreeNodes。当链表转树时,用于封装TreeNode。也就是说,ConcurrentHashMap的红黑树存放的是TreeBin,而不是treeNode
————————————————
版权声明:本文为CSDN博主「Coding-ls」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/programerxiaoer/article/details/80040090
每个键值对(key-value)都存储在一个Node中。同时Node也有一些子类,TreeNodes用于树结构中(当链表长度大于8时转化为红黑树);TreeBins用于维护TreeNodes。当链表转树时,用于封装TreeNode。也就是说,ConcurrentHashMap的红黑树存放的是TreeBin,而不是treeNode
————————————————
版权声明:本文为CSDN博主「Coding-ls」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/programerxiaoer/article/details/80040090
4. ConcurrentHashMap的put()方法?
1:首先判断是否初始化,如果没有初始化则进入$3进行初始化工作
2:如果已经初始化了,进入无限循环,$4原子判断key对应的数组下标是否有值了(如果下标为null 则可以插入,这也是为什么put 不支持 key 或者 value 为null的原因)
3:如果key对应的下标没有值,则通过$5:CAS原理插入,插入成功则退出循环,插入失败则继续循环,所以这种无锁的机制都是利用无限循环+CAS操作。
4:如果key对应的下标已经存在值,判断此时hash==MOVED,则进入$6帮助扩容。
5:如果key对应的下标已经存在值,但是hash!=MOVED,则需要$7对数组的这个下标进行加锁了,以保证线程的安全。
6:如果数组的这个下标是一个链表,则对操作链表(判断链表用hash>=0)
7:如果数组的这个下标是一个红黑树,则操作红黑树。
8:插入成功后,如果链表的长度已经达到了红黑树的阀门8,则首先判断此时数组的长度是否大于64,如果小于64则进行扩容,如果大于等于64则链表变成红黑树
9:判断容器是否扩容
原文網址:https://kknews.cc/code/eymyxjn.html
2:如果已经初始化了,进入无限循环,$4原子判断key对应的数组下标是否有值了(如果下标为null 则可以插入,这也是为什么put 不支持 key 或者 value 为null的原因)
3:如果key对应的下标没有值,则通过$5:CAS原理插入,插入成功则退出循环,插入失败则继续循环,所以这种无锁的机制都是利用无限循环+CAS操作。
4:如果key对应的下标已经存在值,判断此时hash==MOVED,则进入$6帮助扩容。
5:如果key对应的下标已经存在值,但是hash!=MOVED,则需要$7对数组的这个下标进行加锁了,以保证线程的安全。
6:如果数组的这个下标是一个链表,则对操作链表(判断链表用hash>=0)
7:如果数组的这个下标是一个红黑树,则操作红黑树。
8:插入成功后,如果链表的长度已经达到了红黑树的阀门8,则首先判断此时数组的长度是否大于64,如果小于64则进行扩容,如果大于等于64则链表变成红黑树
9:判断容器是否扩容
原文網址:https://kknews.cc/code/eymyxjn.html
初始化数组
无限循环CAS 判断数组下标是否为null,为null就进行CAS 插入
如果数组下标有值,要么在链表加入,要么在红黑树加入,这个操作是 sychronized 来保证的
链表长度超过8,并且 数组长度大于 64 就会链表转变为红黑树
5.ConcurrentHashmap 不支持 key 或者 value 为 null 的原因?
因为concurrenthashmap它们是用于多线程的,并发的 ,如果map.get(key)得到了null,不能判断到底是映射的value是null,还是因为没有找到对应的key而为空,
而用于单线程状态的hashmap却可以用containKey(key) 去判断到底是否包含了这个null。
而用于单线程状态的hashmap却可以用containKey(key) 去判断到底是否包含了这个null。
11.经典面试题:为什么 ConcurrentHashMap 的读操作不需要加锁?
ConcurrentHashMap get 操作不加锁是它高效率的原因
为什么?
1. Node 的成员变量 val 是用 volatile 修饰的,可以保证在多个线程中的内存可见性
2. ConcurrentHashMap 中的数组也是 volatile 修饰的, 可以保证数组扩容时的可见性
为什么?
1. Node 的成员变量 val 是用 volatile 修饰的,可以保证在多个线程中的内存可见性
2. ConcurrentHashMap 中的数组也是 volatile 修饰的, 可以保证数组扩容时的可见性
引申 volatile 面试发问?
有序性
可见性
底层通过内存屏障来实现
内存屏障就是一组处理器指令,
用于实现对内存操作的顺序限制
用于实现对内存操作的顺序限制
happend-before 原则:
通俗的讲就是: a happen-before b,则a所做的任何操作对b是可见的。
volatile 总线风暴知道不?
Hashtable
线程安全。遗留类,不再建议使用。被下面两个代替。
线程安全的的情况下使用:HashMap替代
线程不安全的情况下使用: ConCurrentHashMap 代替
线程安全的的情况下使用:HashMap替代
线程不安全的情况下使用: ConCurrentHashMap 代替
HashMap线程不安全,HashTable虽然线程安全,但性能差,
那怎么破?使用ConcurrentHashMap类吧,既线程安全,还操作高效,谁用谁说好。
那怎么破?使用ConcurrentHashMap类吧,既线程安全,还操作高效,谁用谁说好。
LinkedHashMap
保证了元素插入的顺序。
TreeMap
SortedMap的子类,键支持排序。
System.out.println(3|9)输出什么?
与 或 非 异或 ?
Linux 基础命令
Object 类有哪些方法?
public final native Class<?> getClass()//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。
public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。
protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。
public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。
protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。
public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
hashcode()
hashCode()方法的作用?
主要是为了配合基于散列的集合一起使用。
“设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在将一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。
重写 equals 为甚要重写 hashcode 方法?
一图胜千言(图)
一图胜千言(图)
回答一:与集合有关,比如Map 的get 和 put 操作,都依赖与 hashCode方法
这个问题主要是针对映射相关的操作(Map接口)。学过数据结构的同学都知道Map接口的类会使用到键对象的哈希码,当我们调用put方法或者get方法对Map容器进行操作时,都是根据键对象的哈希码来计算存储位置的,因此如果我们对哈希码的获取没有相关保证,就可能会得不到预期的结果。
回答二:
因为如果只重写了 equals 方法,两个对象 equals 返回了true,但是如果没有重写 hashCode 方法,集合还是会插入元素。这样集合中就出现了重复元素了。
说白了就是 hashmap 在put 的时候 既用到了 hashcode 计算索引下标,又用到了 equlas 判断对象是否相等。
回答三:从equals 注释角度
其实 equals 的javadoc 注释上就已经说的很清楚了
我们看一下Object类中关于hashCode()方法的注释
1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
== 和equlas()有什么区别?
==
基本类型
比较值
引用类型
比较内存地址
回答: 默认情况下也就是从超类Object继承而来的equals方法与‘==’是完全等价的,
比较的都是对象的内存地址,但我们可以重写equals方法,使其按照我们的需求的方式进行比较,
如String类重写了equals方法,使其比较的是字符的序列,而不再是内存地址。
比较的都是对象的内存地址,但我们可以重写equals方法,使其按照我们的需求的方式进行比较,
如String类重写了equals方法,使其比较的是字符的序列,而不再是内存地址。
String
String 为什么设计为不可变的?
安全,安全,还是他妈的安全!
线程安全:String 使用太广泛了,与其费劲心思的给它加锁让他线程安全,不如直接不可变来的直接。
效率,效率,还是他妈的效率。
像下面这样字符串one和two都用字面量"something"赋值。它们其实都指向同一个内存地址。
StringBuffer
线程安全:底层 sychronized 修饰
StringBuilder
线程不安全: 底层是字符数据实现,在拷贝数组进行扩容的时候可能丢失数据,并引发数组越界异常
基本类型
long:32/64 位,8 个字节
int:32/64 位,4 个字节
byte:8 位,1 个字节
a=a+b 和 a+=b 的区别?
+= 操作会把运算结果,强制转换为持有结果的类型,即 把 a+b 的 结果强制转换为 a 类型
反射
如何通过反射创建对象?
方法一:通过类对象调用 其 newInstance() 方法,如:String.class.newInstance()
方法二:通过类对象的 getConstructor()方法 或者 getDeclaredConstractor() 获取构造器对象(Constractor),
并调用其 newInstace() 方法创建对象。如:String.class.getConstractor().newInstace("hello");
并调用其 newInstace() 方法创建对象。如:String.class.getConstractor().newInstace("hello");
如何通过反射获取和设置对象的私有字段?
首先通过对象类的 getDeclaredField(“name”) 方法获取 Field 对象;
再通过 调用 field.setAccessible(true) 把字段设置为可访问;
最后,通过 field 的 get、set 方法获取或者设置字段值。
再通过 调用 field.setAccessible(true) 把字段设置为可访问;
最后,通过 field 的 get、set 方法获取或者设置字段值。
java 中 weakReference 和 SoftReference 的区别?
两者的出现都是用来提高GC 效率的。
SoftReference 在内存不足要发生 GC 前时会被回收
WeakReference 一旦发生GC,无论内存是否足够,都会回收被 弱引用关联的对象;
SoftReference 在内存不足要发生 GC 前时会被回收
WeakReference 一旦发生GC,无论内存是否足够,都会回收被 弱引用关联的对象;
java 中的编译期常量指的是什么?
有什么要注意的地方?
有什么要注意的地方?
public static final 修饰的变量也就是我们所说的编译期常量,
这些变量在编译器就会被替换掉,因为编译器知道这些变量的值在运行时不会再改变了。
但是:当引用第三方 jar 包里的编译器常量版本改变时,确保你的项目要重新编译哦!
这些变量在编译器就会被替换掉,因为编译器知道这些变量的值在运行时不会再改变了。
但是:当引用第三方 jar 包里的编译器常量版本改变时,确保你的项目要重新编译哦!
如何在java 中获取线程堆栈?
1. Jstack [java pid]:在当前终端显示,也可以重定向到文件中。
2. JvisualVM:Thread Dump:图形化界面操作
设计模式
创建型
结构型
行为型
你在工作中使用到过什么设计模式?
结合病历模块化改造详细谈:策略+工厂模式
什么是模板方法模式?
举出一个如何开闭原则模式的例子?
构造器注入和 setter 注入优缺点?
基础算法
二分查找
网络
OSI
一次完整的网络请求流程?
从输入网址到浏览器呈现页面内容,中间发生了什么?
从输入网址到浏览器呈现页面内容,中间发生了什么?
www.baidu.com
DNS 域名解析,得到IP地址
进行 Http 请求
三次握手,成功建立连接
服务器收到请求
页面渲染,响应请求
关闭连接
TCP 四次挥手
TCP 握手和挥手?
三次握手
子主题
四次挥手
子主题
为什么需要四次挥手?
GET和POST两种基本请求方法有什么区别?
HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。
NIO
I/O 复用
Linux 提供了 I/O 复用函数 select/poll/epoll,进程将一个或多个读操作通过系统调用函数,阻塞在函数操作上。这样,系统内核就可以帮我们侦测多个读操作是否处于就绪状态
select: 1024 个文件描述符
pool 与 select 类似,只不过没有了数量限制,但是当 文件描述符太多时还是有性能问题
epool:用红黑树存储 文件描述符 存放问题
算法
树
前中后序遍历
树的发展史
平衡二叉树:平衡二叉树是基于二分法的策略提高数据的查找速度的二叉树的数据结构;
B树
B树和平衡二叉树稍有不同的是B树属于多叉树又名平衡多路查找树(查找路径不只两个),数据库索引技术里大量使用者B树和B+树的数据结构
B+树
B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。
为什么说B+树查找的效率要比B树更高、更稳定;
为什么说B+树查找的效率要比B树更高、更稳定;
红黑树(TreeMap , TreeSet)
本质上就是一个会自动调整树高度的 二叉查找树
链表
翻转
字符串
java 中 统计一个文件中出现频率最高的单词?
检查两个给定的字符串是反序的?
在不使用临时变量的情况下,如何交换两个整数变量的值?
加减法
a=a+b;
b=a-b;
a=a-b;
b=a-b;
a=a-b;
乘除法
a=a*b;
b=a/b;
a=a/b;
b=a/b;
a=a/b;
异或法
a=a^b;
b=a^b;
a=a^b;
b=a^b;
a=a^b;
0 条评论
下一页