- 什么样的对象可以当作gcroots
在Java语言里,可作为GC Roots对象的包括如下几种:
a.虚拟机栈(栈桢中的本地变量表)中的引用的对象
b.方法区中的类静态属性引用的对象
c.方法区中的常量引用的对象
d.本地方法栈中JNI的引用的对象
https://blog.csdn.net/bolg_hero/article/details/79344745
- switch能否用string做参数
- 在jdk 7 之前,switch 只能支持 byte、short、char、int 这几个基本数据类型以及由于1.5之后的自动拆箱,这四种基础类型对应的封装类也同样支持Byte,Short,Character,Integer。
Integer num = new Integer(1);
switch (num){
case 1:
System.out.println("1");break;
case 2:
System.out.println("2");break;
}
当Integer与int比较时,会自动拆箱,转成int类型进行比较。
但如果两个都是Integer类型,就不会自动拆箱成int类型,直接是以Integer类型比较,所以这里有个难理解的点就是,
Integer num1 = 1;
Integer num2 = 1;
if (num1 == num2){
System.out.println(true);
}else{
System.out.println(false);
}
Integer num3 = 166;
Integer num4 = 166;
if (num3 == num4){
System.out.println(true);
}else{
System.out.println(false);
}
输出结果
true
false
结果是不是意料之外?为什么num1 == num2,但num3 != num4 ?
因为,在自动装箱时,对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象;而如果超过了从–128到127之间的值,被装箱后的Integer对象并不会被重用,即相当于每次装箱时都新建一个 Integer对象;
- 在jdk1.7后,整形,枚举类型,boolean,字符串都可以
-
SpringMVC执行流程
第一步
用户发起请求到前端控制器DispatcherServlet
第二步
DispatcherServlet请求处理器映射器HandlerMapping查找Handler
可以是根据xml查找,也可以是根据注解查找
第三步
HandlerMapping向DispatcherServlet返回Handler
第四步
HandlerMapping调用处理器适配器HandlerAdapter去执行Handler
第五步
处理器适配器去执行Handler
第六步
Handler执行完给处理器适配器返回ModelAndView
ModelAndView是SpringMVC框架的一个底层对象,包括Model和View
第七步
处理器适配器给DispatcherServlet返回ModelAndView
第八步
DispatcherServlet请求视图解析器View resolver进行视图解析
根据逻辑视图解析成真正的物理视图(jsp等)
第九步
视图解析器向DispatcherServlet返回view
第十步
DispatcherServlet进行视图渲染
第十一步
DispatcherServlet向用户响应结果
原文:https://blog.csdn.net/leisure_life/article/details/72793738 -
浅谈Session与Cookie的区别与联系
https://blog.csdn.net/bwh0520/article/details/78808181 -
hashmap相关的问题
- 5.1 hashmap源码 https://blog.csdn.net/ooo123lll/article/details/81103234#335-get
- 5.1 hashmap扩容、为什么是2n(2的n次方)扩容 https://www.cnblogs.com/williamjie/p/9358291.html
扩容或者初始化容量时,都是以2的n次方扩容,这主要原因是,hashmap定位key的下标的机制决定的。根据源码:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
// 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 若“该key”对应的键值对不存在,则将“key-value”添加到table中
modCount++;
addEntry(hash, key, value, i);
return null;
}
// 返回索引值
// h & (length-1)保证返回值的小于length
static int indexFor(int h, int length) {
return h & (length-1);
}
我们可以看到当put或者get方法时,会先对key进行hashcode获得hash值,然后用hash值和(table长度-1)进行与运算(indexFor方法),从而得到key的table数组的下标,其实除了与运算,还可以直接使用mod取余运算(hash值%(table长度-1))来获得下标位置,但由于在计算机中取余运算很耗时(计组中有学二进制的除法运算会比二进制的直接与运算复杂很多),所以hashmap采用的是运算来定位key的下标。
如果table长度是2的n次方,那么table.length-1的二进制一定是都是1,如length=16,则length-1=15 -> 1111
那么任何一个小于table.length-1的数字跟length-1进行与运算都是它本身,如9->1001,1001 & 1111 = 1001;这样能保证key的分步均匀,减少key的冲突。
但如果不是2的n次方,假设table.length=15,则table.length-1=14 -> 1110,key的hash值认为9,即1001 & 1110 = 1000 ,发现了什么?只要key的hash值的二进制位1的都不能 用了,这样会跟原本应该在***0的位置的key产生冲突,然后hashmap使用的是拉链法解决冲突,所以冲突的key会放到链表中,无冲突的hashmap取值的时间复杂度为O(1),如果冲突,则在链表中查询当前值的时间复杂度为O(n),这严重降低了hashmap的查询料率。
所以不管是在初始化容量时还是在扩容时,jdk会自动帮你选择以2den次方进行扩容。
-
5.2 负载因子为什么是0.75 https://www.cnblogs.com/peizhe123/p/5790252.html
当创建 HashMap 时,有一个默认的负载因子(load factor),其默认值为 0.75,这是时间和空间成本上一种折衷:增大负载因子可以减少 Hash 表(就是那个 Entry 数组)所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的的操作(HashMap 的 get() 与 put() 方法都要用到查询);减小负载因子会提高数据查询的性能,但会增加 Hash 表所占用的内存空间。 -
5.3 hashMap为什么线程不安全(hash碰撞与扩容导致)https://www.cnblogs.com/qiumingcheng/p/5259892.html
-
5.4 hashmap冲突的解决方法以及原理分析:https://www.cnblogs.com/peizhe123/p/5790252.html