3.1 Object包含哪些类
clone getClass toString finalize equals hashCode wait notify notifyAll
3.2 hashCode和equals的关系
重写hashcode方法一定要重写equals。
两个对象相等,hashcode一定相等
而hashCode不相等,两个对象一定相等。
hashCode相等,两个对象不一定相等
3.3 对于==和equals的区别
对于基本数据类型,== 比较的是他们的值,基本数据类型不能用equals
对于对象数据类型,==比较的是他们的内存地址
对于equals比较的是对象数据类型,底层有一个==,其实比较的也是他们的内存地址,但是大多数方法,都重写了hashCode方法,所以,现在默认比较的是他们的对象数据类型的值。
3.4 关于String的常用方法
splite,trim,charAt,tocharArray,indexOf(第一次出现的索引),subString,replace
3.5 String包含的正则表达式的方法
search检索与正则表达式相匹配的值
match找到一个或多个正则表达式的匹配
replace 替换与正则表达式匹配的字符串
splite 把字符串分割成字符串数组
3.6 String的”“和new String的区别
”“表示的是如果常量池中有这个字符串,那么直接引用,此时是不创建对象的
而new是一定会创建一个对象的,并且存在堆内存中
3.7 String字符串相加实现原理
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
3.7.1对于字符串常量相加:
String s1 = "foo";
String s2 = "bar";
String s3 = s1 + s2;
String s1 = "foo";
String s2 = "bar";
String s3 = (new StringBuilder()).append(s1).append(s2).toString();
concat() 源码可以看出,StringBuilder创建了更多的对象,而concat却没有,它使用的String类的内部实现。
3.8 异常的继承体系
3.9 异常的处理机制
异常处理机制为:抛出异常,捕获异常
抛出异常:当一个方法出现错误引发异常时,方法会创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行
捕获异常:在方法抛出异常后,运行时系统将转为寻找合适的异常处理器。运行时系统从发生异常的方法开始,一次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。
3.10 finnally和return的运行时顺序
首先明确一点,finnally是一定会执行的,先运行try里的内容信息,如果try里面有return,那么执行顺序是先运行try,然后运行return,但是不会直接返回,而是去循行finnally内的信息,当finnally的信息执行完成,返回之前的return信息
3.11 hashmap的特点
hashmap是key-value的结构,无序,允许为空的,初始化容量为16,负载因子为0.75
底层是:数组+链表+红黑树
扩容过程:
1.首先判断数组是否为空,如果为空,则初始化
2.如果不为空,则计算k的hash值,通过(n-1)& hash计算应当存放在数组中得下标index
3.查看table【index】是否存在数据,没有就构造一个node节点放在table【index】中
4.存在数据,证明发生了hash冲突,继续判断key是否相等,如果相等,用新得value替换原来得数据
5.如果不相等,判断当前节点是不是树形节点,如果是,创建树性节点插入
6.如果不是树型节点,创建普通得Node加入链表中,此时判断链表长度和数组长度,链表长度大于8并且数组长度大于64,则转化为红黑树
7.插入完成之后判断当前节点数是否大于阈值,如果大于得话,就扩容
3.12 hashmap的JDK1.7和jdk1.8有什么区别
jdk1.7采用头插法扩容,头插法会使链表发生反转,多线程环境下会差生环
jdk1.7先判断是否需要扩容,再插入。1.8先进行插入,再扩容
jdk1.8的hashmap存在线程不安全问题,没有加锁,简单的例子就是覆盖
线程1要执行插入,检测后并没有发生hash冲突,并且计算好了hash值,但是在插入值得时候时间片用完了,这是线程1被挂起,此时线程2执行插入,很巧的是他的hash与线程1的值相同,那么测试就会造成数据覆盖
3.13hashmap中的循环链表
A线程在插入节点B,B线程也在插入,遇到容量不够开始扩容,重新hash,放置元素,采用头插法,后遍历到的B节点放入了头部,这样形成了环。
3.14 hashmap如何解决hash冲突的
常用的两种方式:链表法和开放寻址法
链表法:
在哈希表中,每一个桶或者槽都会对应一条链表,所有哈希值相同的元素放到相同槽对应的链表中
在插入的时候,通过散列函数计算对应的散列槽位,将元素插入到对应的链表即可,时间复杂度O(1)
在查找删除的时候,通向先计算槽位,然后遍历链表删除,时间复杂度O(length)
开放寻址法:
如果出现散列冲突,重新探测一个空闲位置,将元素插入
往散列表插入,而存储位置已经被占用,那么从当前位置往后遍历,知道找到空闲位置为止
缺点:数据很大时,散列冲突发生的可能性会越来越大,空余位置会越少,最坏时间复杂度为O(n)。
3.15 ConcurrentHashMap:
ConcurrentHashMap使用分段锁,降低了锁粒度,让并发度大大提高。
ConcurrentHashMap的大部分操作和HashMap是相同的,例如初始化,扩容和链表向红黑树的转变等。
但是,在ConcurrentHashMap中,利用一个CAS算法实现无锁化的修改值的操作,降低了锁代理的性能消耗。使用CAS和synchronized保证并发。synchronized只锁住当前链表或红黑树的首节点,这样是要hash不冲突,就不会产生并发冲突。
这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。这一点与乐观锁,SVN的思想是比较类似的。