判断
回到题目,如果你真想检验一个人的水平。第一步先考察一下基本的编程基础,问几个基本的编程问题,可以和前端相关也可以无关。比如垃圾收集大致是怎么做的,setTimeout 大致做了什么(说会在另一个线程里执行回调的直接毙掉)。
第二步考察一下知识面,问问http、tcp的基本知识,dns是怎么工作的,或者常用框架的实现原理,看看候选人是不是除了自己的一亩三分地什么都不关心。
第三步考察hold业务逻辑的能力,从一个简单的注册页,或者查询页开始,先让说下代码的基本架构,然后需求、性能、可靠性、安全层层加码,看看能不能很快的反馈出解决方案。能对答如流的要么做过,要么对他来说这种复杂度的东西是小case。
前三步都没问题,基本上说明候选人已经还行了,但是行到什么程度,不知道。如果想找比较厉害的,就增加个第四步,亮点项目考察。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了,我整理一套前端面试题分享给大家,希望对即将去面试的小伙伴们有帮助!
/\*\*
\* The number of times this list has been <i>structurally modified</i>\
\*/
protected transient int modCount = 0;
3、构造函数
3.1 无参构造函数
需要注意的是无参构造函数,给elementData赋值的是一个空的数组,在第一次添加元素的时候才把容量扩大为10,不要被注释给误导了。此处可见无参构造器是将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData
/\*\*
\* Constructs an empty list with an initial capacity of ten.
\*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
3.2 构造一个初始容量为initialCapacity的ArrayList
当initialCapacity>0,初始化一个大小为initialCapacity的Object数组,赋值给elementData;当initialCapacity==0,将EMPTY_ELEMENTDATA赋值给elementData;否则,抛出异常。
/\*\*
\* Constructs an empty list with the specified initial capacity.
\*/
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);
}
}
3.3 通过指定的Collection来初始化一个ArrayList
将Collection转为数组并且赋值给elementData,elementData.length赋值给size;如果size != 0,则判断elementData类型是否为Object[]类型,不是则做一次转换(注意此处其实是一个官方bug解决方案);如果size == 0则将EMPTY_ELEMENTDATA赋值给elementData,相当于new ArrayList(0)
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;
}
}
4、核心方法
4.1 add 操作
ensureCapacityInternal();
每次添加元素到集合中都会先确认集合容量的大小;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
calculateCapacity();
判断elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA则取DEFAULT_CAPACITY和minCapacity的最大值,也就是10;此处验证了在第一次插入的时候才会给数组初始容量10;
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
modCount++;
记录操作次数;
minCapacity - elementData.length > 0;
数组长度不够时对数组进行扩容,执行grow()函数的逻辑
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
oldCapacity + (oldCapacity >> 1)
默认将数组的大小扩容到原来的1.5倍
newCapacity - minCapacity < 0
如果扩容后的容量不足,则将所需容量minCapacity赋值给newCapacity,扩容后数组大小就是申请的容量
newCapacity - MAX_ARRAY_SIZE > 0
如果扩容后的数组容量太大,超过了Integer.MAX_VALUE - 8的大小,则数组的大小为hugeCapacity(),(minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
其余add()方法大同小异
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
4.2 remove 操作
rangeCheck(index);
检查index是否合法,当index >= size时,抛出IndexOutOfBoundsException异常;
modCount++;
记录集合的操作次数;
E oldValue = elementData(index);
取出移除元素,用于放回给方法调用者;
if (numMoved > 0)
判断当前删除的集合元素是否时数组的最后一个元素,如果不是最后一个元素,则调用System.arraycopy()方法做一次数组拷贝;如果移除的元素是最后一个元素或者在数组复制结束之后,将数组的最后一个元素置为null,等待GC垃圾回收;–size数组的大小减一;
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
4.3 get 操作
rangeCheck(index);
检查index是否合法,当index >= size时,抛出IndexOutOfBoundsException异常;
elementData(index);
由于ArrayList的底层是由数组实现的,所有获取元素直接调用数组的随机访问即可;
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
5、Iterator
5.1 前言
Java开发初级工程师在使用for遍历集合的时候,由于使用不正确的API方法对集合进行remove()操作,经常会抛出java.util.ConcurrentModificationException,因此我们来从源码的层面仔细的探究一下为什么会产生ConcurrentModificationException以及如何解决这个异常!foreach循环又称增强for循环,是jdk1.5为了简化数组或者和容器遍历而产生,foreach循环的适用范围:对于任何实现了Iterable接口的容器都可以使用foreach循环。foreach语法的冒号后面可以有两种类型,一种是数组类型;一种是实现了Iterable接口的类,因此在使用foreach循环遍历ArrayList的时候,可以看作就是在使用List的迭代器进行遍历
如下代码,foreach遍历list集合时,调用list.remove(s)方法,控制台输出了预期ConcurrentModificationException异常
5.2 ArrayList中Iterator
本质上返回的是一个Itr实例化对象
public Iterator<E> iterator() {
return new Itr();
}
ArrayList定义了一个Itr内部类实现了Iterator接口,Itr内部有三个属性。
cursor:代表的是下一个访问的元素下标;
lastRet:代表的是上一个访问元素的下标,默认值为-1;
expectedModCount:代表的是对ArrayList修改的次数,初始值等于modCount
/\*\*
\* An optimized version of AbstractList.Itr
\*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
}
hasNext();
判断是否存在下一个元素,返回值只要cursor下一个元素的下标不等于集合的元素个数size就返回true,其他情况返回false
public boolean hasNext() {
return cursor != size;
}
next() ;
获取集合中的下一个元素
checkForComodification();
判断modCount与expectedModCount是否相等,不相等抛出ConcurrentModificationException异常
i = cursor;i >= size;i >= elementData.length
判断cursor的大小,如果cursor的值大于集合中元素的个数,抛出NoSuchElementException异常;如果cursor大于了数组的长度抛出ConcurrentModificationException异常。
cursor = i + 1;lastRet = i
对象篇
模块化编程-自研模块加载器
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
GRhODZhZGZjNjY?x-oss-process=image/format,png)
模块化编程-自研模块加载器
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】