Java
HashMap和HashTable
- jdk1.8中采用数组+链表+红黑树实现
- 首先会创建一个默认长度为16,默认加载因为0.75的table数组
- 根据hash值和数组的长度计算应存入的位置
- 判断当前位置是否为空,如果为空则直接存入
- 如果当前位置不为空,则调用equals方法比较属性值
- 如果一样则替换为新的,如果不一样则采用头插法插入
- 当节点数多于8个后,链表转为红黑树
- HashMap是线程不安全的,HashTable是线程安全的
TreeSet和HashSet
- HashSet采用hash表实现,其中的元素没有按顺序排列
- add(),remove()等方法的复杂度都为O(1)
- TreeSet采用红黑树实现,元素按顺序排列
- 但是add(),remove()等方法的复杂度都为O(logn)
- TreeSet还提供了一些方法如first(),headSet()等来处理排列的set
Stringbuffer和Stringbuilder
- 首先两者在功能上是完全等价的
- StringBuffer中的方法大都采用了synchronized关键字修饰,因此是线程安全的
- StringBuilder是线程不安全的
- 单线程使用StringBuilder效率更高
Final、Finally、Finalize
final
:修饰类时这个类不能被继承,修饰变量时变量必须赋初始值,并且在使用过程中不能被改变,修饰方法时只能使用,不能被重写finally
:通常放在try..catch后,程序是否发生异常,都会执行finally代码块中的内容,通常将释放外部资源的代码写在finally块中finalize
:由垃圾回收器在销毁对象之前创建,在垃圾回收器回收之前做一些清理工作,可以重写finalize()方法执行一些操作
== 和equals
==
:比较基本数据类型,比较的是变量的值,若比较的是引用数据类型,比较的是地址值equals
:没重写之前比较的是两个对象的地址值,重写之后往往比较的是属性的内容
JVM
类加载器的分类
- 引导类加载器
- 扩展类加载器
- 应用程序类加载器
- 自定义加载器
类加载的过程(双亲委托模式)
- 下一级的类加载器,如果接到任务时,会先搜索是否加载过,如果没有,会先把任务往上传,如果都没有加载过,一直到根加载器,如果根加载器在它负责的路径下没有找到,会往回传,如果一路回传到最后一级都没有找到,那么会报ClassNotFoundException或NoClassDefError,如果在某一级找到了,就直接返回Class对象
- 父类已经加载了该类时,子类没有必要再加载,避免了类的重复加载
- 防止java核心api中定义的类型不会被被随意篡改
垃圾收集的方法
复制算法
:年轻代Minor GC采用的是这种算法,效率高但比较占内存,用于占空间比较小,刷新次数多的新生区标记清除
:效率低,会产生碎片,用于老年代标记压缩
:效率低,需要移动对象,但不会产生碎片标记清除压缩
:用于占空间大,刷新次数少的养老区
Java内存回收策略
- 对象优先在Eden区分配
- 大的对象直接进入老年代
- 长期存活的对象直接进入老年代
- Eden区空间不够时,虚拟机在新生代的Eden区实行一次Minor GC
- Major GC(Full GC)发生在老年代
- 可以通过配置,在Full GC之前执行一次Minor GC
- 这样可以加快老年代的回收速度
JUC
Java中创建线程的几种方式
- 继承Thread类,重写run方法
- 实现Runnable接口
- 实现Callable接口
- 通过线程池启动多线程
run和start
- start()用于启动线程
- run()用于执行代码
- run()可以重复调用,start()只能调用一次
sleep和wait
- sleep()来自Thread,wait()来自Object
- sleep()不释放锁,wait()释放锁
- sleep()时间到会自动恢复,wait()可以用notify()或notifyAll()直接唤醒
Synchronized与Lock
- Sychronized能实现的功能Lock都能实现,而且Lock比Sychronized更灵活好用
- 但是Synchronized可以自动上锁和解锁,Lock需要手动上锁和解锁,如果使用不当会造成死锁
Runnable与Callable
- Runnable接口中的方法没有返回值,Callable接口中的方法有返回值
- Runnable接口中的方法没有抛出异常,Callable接口中的方法抛出了异常
- Runnable接口中的落地方法是call方法,Callable接口中的落地方法是run方法