2025.02.18笔记 ~ langhai

2025.02.18笔记
ArrayList源码分析
// 默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
// 存储ArrayList元素的数组缓冲区
transient Object[] elementData;
// ArrayList的大小
private int size;
// 带参构造函数
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
// 无参构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 带参构造函数
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
// 添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 计算是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 从这里可以清楚看到 增加原来1.5倍的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 第一次添加元素 初始化的数组长度是10
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList底层是动态数组实现的,初始化容量为0,当第一次添加数据的时候容量变为10。扩容的话就是原来的1.5倍,每次扩容都需要进行数组的拷贝。每次在添加元素的时候,会判断是否有足够的容量进行存储。(当前数组已经使用的长度 + 1 是否大于当前数组的长度)
如果构造函数指定了容量,就是调用有参构造函数,例如 new ArrayList(10),这种情况下是直接创建数组的,并没有进行扩容。
ArrayList LinkedList
ArrayList是动态数组的数据结构 LinkedList是双向链表的数据结构。
ArrayList可以按照下标查询,时间复杂度是O(1),内存地址是连续的,可以根据寻公式查找。
CAS:Compare And Swap 比较在交换。乐观锁思想的体现,在没有锁的情况下,保证共享数据的原子性。
volatile:保证线程间的可见性 禁止进行指令重排序(其中写、读屏障来实现)。
AQS:抽象队列同步器,构建锁或者其他同步组件的基础框架。内部维护了一个先进先出的双向队列,以及一个属性状态,来判断是否是无锁的状态。这个属性值,操作的时候需要用到CAS,来保证多线程情况下的原子性。
垃圾回收:如果一个对象没有任何的引用指向他,这个对象就有可能被回收。
有两种方法来判断对象是不是垃圾:第一种就是引用计数法,第二种就是可达性分析算法。
引用计数法:对象被引用次数,引用次数为0,代表这个对象可以被回收。如果出现循环引用,引用计算法会失效,会出现内存泄漏的情况。
可达性分析算法:现在虚拟机采用的算法。有一个GC Roots节点为初始节点,形成一个引用链,如果在这个引用链上找不到该对象,就表示可以回收。
哪些对象可以被作为GC Root:虚拟机栈中引用的对象 方法区中类静态属性引用的对象 方法区中常量引用的对象 本地方法栈中的JNI(Native方法)引用的对象。
垃圾回收算法:标记清除算法 复制算法 标记整理算法。
标记清除算法:先根据可达性分析算法找到垃圾,对垃圾进行标记,然后进行回收垃圾。此算法标记和清除的速度比较快,但会出现内存碎片,导致内存不连贯。
标记整理算法:解决了标记清除算法内存碎片化的问题。需要对对象进行移动,效率低。
复制算法:清理之后没有内存碎片,分配2块内存空间,在同一个时刻,只能使用一半,内存使用率较低。
分代回收:新生代 1/3的空间占用 和 老年代 2/3的空间占用。
新生代里面就是包含 一个伊甸园区 Eden 和 幸存者区 from to。这三个是 8 :1 :1的空间占用关系。
新创建的对象都会被分配到伊甸园区 Eden,当幸存者区经过最多15次对象回收之后,会晋升到老年区。(幸存者区域不足 或者 对象太大 会导致提前晋升到老年代区域)
MinorGC:发生在新生代的垃圾回收,会造成短暂的STW。
FullGC:新生代 + 老年代完整垃圾回收,暂停的STW比较长。
MixedGC:新生代 + 老年代 部分区域进行垃圾回收,G1垃圾回收器的特性。
STW的就是指暂停应用程序的时间,需要等待。Stop - The - World。
设计模式
工厂模式:优点解耦
简单工厂模式:增加新产品时候,还是需要修改工厂类的代码。
工厂方法模式:
主要角色:抽象工厂 抽象产品 具体工厂 具体产品
用户只需要知道具体工厂的名称就能获取对应的产品,不需要知道产品的具体创建过程。
系统在添加新产品的时候,需要提供具体的产品类和对应的具体工厂类,是满足开闭原则的。
抽象工厂模式:
工厂方法模式的升级版本,能够创建多个等级的产品。