Java常用类库、技巧
- 1、Java异常体系
java异常机制主要回答了三个问题
what:异常类型回答了什么被抛出
where:异常堆栈跟踪回答了在哪抛出
why:异常信息回答了为什么被抛出
RuntimeException:不可预知的异常,程序应当自行避免,是程序应该承担的责任
非RuntimeException:可预知的异常,从编译器可以校验的异常,是Java编译器应该承担的责任
Checked Exception可检查异常是Java编译器应该负担的责任
- 2、error和exceptin的区别
error:程序无法处理的系统错误,编译器不做检查(Error是JVM应当承担的责任)
exception:程序可以处理的异常,捕获后可能恢复
总结:前者是程序无法处理的错误,后者是可以处理的异常
- 3、常见Error以及Exception
RuntimeException
NullPointerException-空指针引用异常
ClassCastException -类型强制转换异常
IllegalArgumentException-传递非法参数异常
IndexOutOfBoundsException-下标越界异常
NumberFormatException-数字格式异常
非RuntimeException
ClassNotFoundException-找不到指定class的异常
IOException-IO操作异常
Error
NoClassDefFoundError-找不到class定义的异常
*Error-深递归导致栈被耗尽而抛出的异常
OutOfMemoryError-内存溢出异常
- 4、Java的异常处理机制
抛出异常:创建异常对象,交由运行时系统处理
捕获异常:寻找合适的异常处理器处理异常,否则终止运行
处理原则:
具体明确:抛出的异常应能通过异常类名和message准确说明异常的类型和产生异常的原因;
提早抛出:应尽可能早的发现并抛出异常,便于精确定位问题
延迟捕获:异常的捕获和处理应尽可能延迟,让掌握更多信息的作用域来处理异常。
Java异常处理消耗性能的地方
try-catch影响JVM性能的优化
异常对象实例需要保存栈快照等信息,开销较大。
- 5、数据结构、算法考点
数组和链表的区别
链表的操作,如反转、链表环路检测、双向链表、循环链表相关操作
队列、栈的应用
二叉树的遍历方式及其递归和非递归的实现
红黑树的旋转
内部排序:如递归排序、交换排序(冒泡、快排)、选择排序、插入排序
外部排序:应掌握如何利用有限的内存配合海量的外部存储来处理超大的数据集,写不出来也要写出相关思路
那些排序是不稳定的,稳定意味着什么?(快排、堆排序是不稳定的)
不同数据集,各种排序的最好最差情况
如何优化算法
-6、 集合总体结构图
- 7、集合之List和Set
- 8、HashMap、HashTable、ConcurrentHashMap
1)、HashMap(Java1.8以前):数组加链表
HashMap(Java1.8及以后):数组加链表加红黑树
HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。容量 是哈希表中桶的数量,初始容量(默认值为16)只是哈希表在创建时的容量。加载因子(默认0.75)是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将扩大到具有大约两倍的桶数。
HashMap在Java1.8以前,key在产生碰撞后会自动将键值对连接在碰撞键值对的后面组成链表结构,但是在链表长度太长之后,会影响HashMap的查询性能,时间复杂度可能会从原来的O(1)变为O(n)。在Java1.8及以后,HashMap使用数组加链表加红黑树的结构,HashMap中提供一个为TREEIFY_THRESHOLD的变量,当链表长度达到8的时候,自动将链表转化为红黑树进行存储,当键值对个数少于6的时候,自动将红黑树转化为链表存储。
HashMap的put方法的逻辑:
如果HashMap未被初始化过,则初始化
对Key求Hash值,然后再计算下标
如果没有碰撞,直接放入桶中;如果碰撞则以链表的形式存放在后面
如果链表长度超过阈值8,就把链表转化为红黑树;如果链表长度低于6,则把红黑树转回链表
如果节点已经存在,则替换旧值
如果桶满了(容量16*加载因子0.75),就需要resize(扩容2倍后重排)
HashMap如何减少碰撞:
扰动函数:让元素位置分布均匀,减少碰撞
使用final对象,并采用合适的equals()和hashcode()方法,例如用String和Integer这样类型的变量作为key
HashMap扩容可能产生的问题:
多线程环境下,多个线程同时调整同一个HashMap会存在条件竞争,容易造成死锁
rehashing是一个比较耗费时间的过程
2)、Hashtable
线程安全:涉及到修改Hashtable的方法,都使用了synchronized去修饰
串行化的方式运行,性能较差
3)、ConcurrentHashMap
早期ConcurrentHashMap:通过分段锁Segment来实现
当前ConcurrentHashMap:CAS+synchronized使锁更细化
ConcurrentHashMap:put方法逻辑
判断Node[]数组是否初始化;没有则进行初始化操作
通过hash定位数组的索引坐标,是否有Node节点,如果没有则使用CAS进行添加(链表的头节点),添加失败则进入下一次循环
检查到内部正在扩容,就帮助他一起扩容
如果头节点!=null,则使用synchronized锁住头节点,再对链表或者红黑树进行添加元素操作
判断链表长度是否达到临界值8,如果节点数超过临界值,则将链表转化为红黑树,小于6则将红黑树转化为链表
ConcurrentHashMap总结:比起Segment,锁拆的更细
首先使用无锁操作CAS插入头节点,失败则循环重试
若头结点已存在,则尝试获取头结点的同步锁,在进行操作
HashMap、Hashtable、ConcurrenHashMap三者区别
HashMap线程不安全,数组+链表+红黑树
Hashtable线程安全,锁住整个对象,运行效率低,数组+链表
ConcurrentHashMap线程安全,CAS+同步锁,数组+链表+红黑树
HashMap的key、value均可以为null,其他两个不支持
- 9、BIO、NIO、AIO
Block-IO:基于字节流的InputStream和OutputStream,基于字符流的Read和Writer
NonBolck-IO(非阻塞IO):构造多路复用、同步非阻塞的IO操作