浮点型范围

类型 符号位 指数位 尾数位
float 1bit 8bits 23bits
double 1bit 11bits 52bits

float因为指数为 0 和 255 代表特殊值。实际浮点数的指数表示范围是1-254,减去偏阶127后得到实际表示范围:-126~+127。

图片描述

精度丢失

由于我们代码在程序里写的十进制小数,而计算机内部只能用二进制的小数, 所以无法精确的表达。对于二进制小数,小数点右边能表达的值是 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128 … 1/(2^n)。所有的十进制小数都是这些一点一点的拼凑出来的一个近似的数值, 如1.2(10进制) ≈1.00110011(2进制) = 1 + 1/8+1/16+1/128+1/256 = 1 + 0.19921875。

ArrayList

底层是Object[] elementData数组,默认大小DEFAULT_CAPACITY=10,使用空参构造时只是将默认的空数组赋值给elementData。在添加数据时先执行ensureCapacityInternal(size+1)方法判断容量是否够,先判断elementData是否等于默认空数组,若是,则容量初始化为Math.max(DEFAULT_CAPACITY【默认大小10】, minCapacity【当前所需容量最小大小】);否则若minCapacity大于当前容量,则调用grow方法以1.5倍扩容。

链接

LinkedList

底层是使用静态内部类Node实现的双向链表。

HashMap

底层使用数组+链表/红黑二叉树。桶数组默认长度DEFAULT_INITIAL_CAPACITY是16,负载因子loadFactor默认是0.75,数组长度大于阈值会以两倍形式增长。添加数据哈希冲突时采用尾插法(注:jdk1.7及以前采用头插法,在多线程下可能导致死循环)。当链表的长度大于8,同时桶数组长度大于等于64时,将链表转为红黑二叉树。当桶中元素小于等于6时转为链表。

哈希值计算

1
2
3
4
5
6
7
8
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//用高16位参与异或运算,为了降低 hash 冲突的概率。
}

// 将(tab.length - 1) 与 hash值进行&运算定位桶数组索引
// 注:对于2的n次方的求余,与2的n次方-1进行与运算结果相等,位运算效率更快
int index = (n - 1) & hash;

Set

底层直接使用HashMap的key存值。map的value统一存入一个不可变的object对象。

LinkedHashMap

继承HashMap,每个结点在其基础上多了before, after。保证元素的有序性。还可以通过设置accessOrder为true将最近获取的元素移动至链表尾部。可用作简单缓存。也可通过继承自定义removeEldestEntry()方法实现LRU算法实现新增数据时移除最久未使用元素。

HashTable

线程安全。底层使用数组+链表(头插法)。使用synchronized修饰方法。默认容量为11,rehash方法以2倍加1形式扩容。key/value也无法为null。

1
2
3
4
5
//hash值直接使用key的hash值
int hash = key.hashCode();

int index = (hash & 0x7FFFFFFF) % tab.length;
//&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。

参考链接

TreeMap

底层使用红黑二叉树。初始化时可指定排序规则。

ConcurrentHashMap

key/value都不允许为空。

1.5~1.7前使用分段锁,把Map分成了N个Segment(相当于把Map拆分成N个小的HashTable),put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中,操作不同Segment不会锁冲突。扩容时不会对Segmengt数组增大(默认长度16),只会增加Segmengt 后面的链表容量的大小。即对每个Segmengt 的元素进行的ReHash操作。

1.8后用cas+synchronized。

图解扩容

参考链接

参考链接

sizeCtl 定义理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
private transient volatile int sizeCtl;
源码注释翻译
sizeCtl :默认为0,用来控制table的初始化和扩容操作
-1 代表table正在初始化
-N 表示有N-1个线程正在进行扩容操作(注:这里理解有问题)
其余情况:
1、如果table未初始化,表示table需要初始化的大小。
2、如果table初始化完成,表示table的容量,默认是table大小的0.75


//ConcurrentHashMap中有个大写的sizeCtl
//这个大写的sizectl存的是sizeCtl的偏移量,在sizeCtl进行cas使用
private static final long SIZECTL;
SIZECTL = U.objectFieldOffset
(k.getDeclaredField("sizeCtl"));

这里-N的定义是有问题的,应该取-N对应的二进制的低16位数值为M,此时有M-1个线程进行扩容。

参考链接

集合相关参考链接

链接

链接

Arrays.sort()

  • 排序的是引用类型时

​ 使用的是归并排序(Collections.sort()最终也是调用这方法)。

  • 排序的是基本数据类型时

​ 长度小于INSERTION_SORT_THRESHOLD(47):使用插入排序

​ 长度小于QUICKSORT_THRESHOLD(286): 使用快速排序

​ 长度大于286后会先统计其降序子数组数,计数count等于MAX_RUN_COUNT(67)被判断为这个数组不具备 结构(数据时而升时而降),使用快速排序,否则进入归并排序。

枚举(enum)

本质上是一个继承Enum抽象类的final类

链接

反射机制

链接

Java泛型

泛型擦除与多态冲突

桥方法

Java多态实现原理

学习链接

学习链接

Java注解

本质上是一个继承Annotation接口的接口

Java注解

学习链接

学习链接

学习链接

排查Java项目线上运行慢问题

  1. 使用top查看cpu占有率。
  2. 使用top -hp pid或ps -mp pid -o THREAD,tid,time查看对应进程的线程使用情况。
  3. 使用jstat -gcutil pid 【打印间隔时间(默认毫秒)】 【 打印次数】打印Jvm运行时数据区情况。
  4. 用jstack pid打印该Java进程的堆栈信息。定位对应代码。

参考链接

Java基础宝藏链接

响应式编程(Reactive Programming)

响应式编程是一种基于数据流(data stream)和变化传递(propagation of change)的声明式(declarative)的编程范式。

v2-5be72cbcf804dc2953f1198a1365ed9d_720w

参考链接

参考链接

WebFlux框架

640

参考链接

参考链接

JMX

JMX(Java Management Extensions,Java管理扩展)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。

参考链接

Java总结

Java知识体系总结

面试参考

其他链接

以JDBC为例谈双亲委派模型的破坏

END