- 面试高频【1— 5】
- 1、HashMap底层原理,以及在jdk1.7和jdk1.8实现区别?
- 1、1 Java7 HashMap结构【数组+链表】
- 1、2 Java8 HashMap【数组+链表+红黑树】
- 2、高并发中的集合类
- 3、java8新特性(由于内容过多,此处不再赘述)
- 4、Java代理的几种实现方式
- 5、IOC容器的实现
1、1 Java7 HashMap结构【数组+链表】HashMap:根据键的hashCode存储数据,访问速度较快,但是遍历顺序不确定
①HashMap最多一个键为null【键不重复,null也算一种】,可有多个值为null
②非线程安全【变为安全:1、Collections的synchronizedMap 2、ConcurrentHashMap】
static class Entryimplements Map.Entry { final K key; V value; Entry next; int hash; }
总的来说,HashMap里面是一个数组,数组中每个元素是一个单向链表【上图中绿色的Entry】。
- capacity:当前数组容量,2^n,扩容缩容为原来的2倍【 DEFAULT_INITIAL_CAPACITY 默认容量:16】
- loadFactor:负载因子,默认0.75
- threshold:扩容阈值,等于capacity*loadFactor
注意:在HashMap添加数据时,如果hash值对应数组的位置已经有元素了,就会比较两个元素的值是否相同,如果相同就不添加,如果不相同就添加,新元素替换旧元素,然后由新元素指向旧元素,“七上八下”
在Java7 HashMap中,我们可以知道根据hash值我们可以快速定位到数组的具体下标,但是,找到下标之后,我们就需要顺着链表一个一个去比较,时间复杂度为O(n)。于是为了降低这部分开销,在Java8中,当链表的元素超过了8个以后,会将链表转为红黑树,以此来降低时间复杂度,为O(logN)
补充:
红黑树特性:
- 每个节点只能是红色或黑色
- 根节点必须是黑色
- 红色的节点,它的叶子节点只能是黑色
- 从任一节点到其他每个叶子的所有路径都包含相同数目的黑色节点【一条路径不能比其他任意一条路径的两倍还要长】
红黑树牺牲了查找性能,其本身不是完全平衡的二叉树。因此,插入、删除效率高于AVL树(平衡二叉树)。AVL树用自平衡的计算牺牲了插入、删除性能,但因为最多只有一层高度差,查询效率会更高。
2、高并发中的集合类- 第一代线程安全集合类 (Vector、Hashtable)
使用synchronized修饰所有方法,导致效率低下
- 第二代线程非安全集合类 (ArrayList、HashMap)
一、高并发场景很少见,为了弥补性能,但是线程不安全,用于代替Vector、Hashtable
二、Collections.synchronizedList(list);Collections.synchronizedMap(map);用来实现线程安全。【底层使用synchronized代码块锁,锁在方法里面,性能较之前有所提高】
- 第三代线程安全集合类(ConcurrentHashMap、CopyOnWriteArrayList)
3、java8新特性(由于内容过多,此处不再赘述)java.uitl.concurrent.* JUC包
【ConcurrentHashMap】
【CopyOnWriteArrayList】
底层大都使用Lock锁(1.8的ConcurrentHashMap不使用Lock锁),保证安全,同时提高了性能。
- 接口的默认方法
- Lambda表达式(**)
- 函数式接口
- 方法与构造函数引用
- Lambda作用域
- 访问局部变量
- 访问对象字段与静态变量
- 访问接口的默认方法
- Date API(*)
- Annotation注解
第一种:静态代理,只能静态的代理某些类或者某些方法,功能弱,编码简单(不推荐)
第二种:动态代理,包含Proxy和CGLIB动态代理
-
Proxy代理(JDK内置动态代理,面向接口)
【特点】:面向接口的,不需要导入第三方依赖的动态代理,可以对多个不同的接口进行增强,通过反射读取注解时,只能读取到接口上的注解。
【原理】:面向接口,只能对实现类在接口种定义的方法进行增强 -
CGLIB动态代理【继承,面向父类】
【特点】:面向父类的动态代理,需要导入第三方依赖
【原理】:面向父类,底层通过子类继承父类并重写方法实现增强
5、IOC容器的实现拓展:Proxy和CGLIB是很重要的代理模式,是SpringAOP底层实现的两种方式
1.Spring5.x中AOP默认采用JDK动态代理(Proxy)
2.SpringBoot2.x开始,为了解决JDK动态代理而导致的类型转换异常而默认使用CGLIB。如果需要默认使用JDK动态代理,可以通过配置项spring.aop.proxy-target-class进行修改
IOC(Inversion of Control),控制反转,是一种设计思想,意味着将设计好的对象交给容器控制,而不是传统的在对象内部直接控制。
在传统的程序中,我们是在对象中主动控制去直接获取依赖对象,这叫正转;反转是由容器来帮忙创建及注入依赖对象,在这个过程中,由容器帮我们查找及注入依赖对象。
- 先准备一个基本容器对象,包含map结构
- 进行配置文件读取或注解解析,将需要创建的bean对象都封装成BeanDefinition对象,存储在容器中
- 容器将封装好的BeanDefinition对象通过反射的方式进行实例化,完成对象的实例化工作
- 进行对象的初始化操作(给类中对应属性赋值),也就是依赖注入,完成整个对象的创建,变成一个完整的bean对象,存储在容器的某个map结构中
- 通过容器获取对象,进行对象的获取和逻辑处理操作
- 提供销毁操作,对象不用时、容器关闭时,进行对象销毁操作
扩展:
Spring中BeanDefinition接口(对bean的一些配置信息)部分源码:
@Nullable String getParentName(); void setBeanClassName(@Nullable String beanClassName); void setScope(@Nullable String scope); void setLazyInit(boolean lazyInit); void setDependsOn(@Nullable String... dependsOn); void setAutowireCandidate(boolean autowireCandidate); void setPrimary(boolean primary); void setFactoryBeanName(@Nullable String factoryBeanName); void setFactoryMethodName(@Nullable String factoryMethodName); ConstructorArgumentValues getConstructorArgumentValues(); MutablePropertyValues getPropertyValues(); void setInitMethodName(@Nullable String initMethodName); void setDestroyMethodName(@Nullable String destroyMethodName); void setRole(int role); void setDescription(@Nullable String description); boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); String getResourceDescription(); BeanDefinition getOriginatingBeanDefinition();



