Java的LinkedList源码解析(中级Java工程师面试必备,详细注释全部实现方法,内部类与变量)

介绍

参看Java8中LinkedList源码(不包括继承父类的)包含57个方法(method)、4个内部类(inner class)和4个定义的变量(field)。

4个变量

transient int size = 0;//链表结点的个数,即链表元素的个数
transient Node<E> first;//指向链表的第一个结点
transient Node<E> last;//指向链表的最后一个结点
private static final long serialVersionUID = 876323262645176354L;//序列化使用

1个内部类(其余3个内部类在方法中注释讲解)

private static class Node<E> {//链表的结点实现
    E item;//链表存放的元素
    Node<E> next;//当前结点的后继
    Node<E> prev;//当前结点的前驱
    Node(Node<E> prev, E element, Node<E> next) {//结点的初始化构造函数,需要指明结点元素、前驱和后继
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

57个方法

在57个方法中可以分为两类,一类是基础方法,这些方法都是private或default修饰的,会作为底层实现被类中其他方法调用,另一类是对外提供的接口方法,他们中大部分会调用到基础方法。所以在注释讲解中主要重点讲解基础方法,看懂基础方法那么对于链表结点实现的链表也就差不多都懂了。对外提供的接口方法在调用基础方法的基础上加入一些自己特有的逻辑。

2个构造函数

public LinkedList() {}
public LinkedList(Collection<? extends E> c) {//使用集合c中的元素初始化链表
    this();
    addAll(c);//addAll函数执行将集合c中的元素放入到当前队列的操作,源码后面讲解
}

7个基础方法

7个基础方法是其他方法的底层实现,难点在于前驱后继的指针变化,链表的实现是双向链表,删除和增加结点时都需要操作前驱引用和后继引用。

private void linkFirst(E e) {//元素e放入链表的第一个结点位置
    final Node<E> f = first;//记录当前第一个结点的位置
    final Node<E> newNode = new Node<>(null, e, f);//初始化新结点元素为e,前驱为null,后继为当前的第一个结点
    first = newNode;//first指向新的第一个结点
    if (f == null)//如果原来的第一个结点为null,说明插入之前链表为null
        last = newNode;//last指向新的第一个结点,因为新插入的第一个元素也是最后一个元素
    else//如果原来的第一个结点不为null,插入之前链表有结点
        f.prev = newNode;//原来链表的第一个结点的前驱指向新插入的结点
    size++;//元素个数加1
    modCount++;//修改记录加1
}
void linkLast(E e) {//元素e作为最后一个结点放入链表
    final Node<E> l = last;//记录当前最后一个结点的位置
    final Node<E> newNode = new Node<>(l, e, null);//初始化新结点元素为e,前驱为当前最后一个结点,后继为null
    last = newNode;//last指向新的最后一个结点
    if (l == null)//如果原来的最后一个结点为null,说明插入之前链表为null
        first = newNode;//first指向新的最后一个结点,因为新插入的最后一个元素也是第一个元素
    else//如果原来的最后一个结点不为null,插入之前链表有结点
        l.next = newNode;//原来链表的最后一个结点的后继指向新插入的结点
    size++;//元素个数加1
    modCount++;//修改记录加1
}
void linkBefore(E e, Node<E> succ) {//元素e插入到非空结点succ之前
    final Node<E> pred = succ.prev;//保存succ结点的前驱
    final Node<E> newNode = new Node<>(pred, e, succ);//初始化新的结点,元素为e,前驱指向succ的前驱,后继指向succ
    succ.prev = newNode;//succ前驱指向新结点
    if (pred == null)//如果保存的succ结点前驱为null,则原来succ为第一个结点
        first = newNode;//first指向新的结点
    else//如果保存的succ结点前驱不为null
        pred.next = newNode;//保存的succ结点前驱的后继指向新的结点
    size++;//元素个数加1
    modCount++;//修改记录加1
}
private E unlinkFirst(Node<E> f) {//卸下链表的第一个非空结点f
    final E element = f.item;//记录f结点的元素值
    final Node<E> next = f.next;//记录f结点的后继
    f.item = null;//f结点元素置null
    f.next = null; //f结点后继引用置null
    first = next;//first指向f的后继,此时f的后继为第一个结点
    if (next == null)//如果f的后继为null,则链表只含有一个结点f
        last = null;//卸下f之后链表为空,last置null
    else//如果f的后继不为null
        next.prev = null;//f的后继的前驱引用置null,这样f的后继就是新的第一个结点。原来结点f没有引用指向将会被GC回收
    size--;//元素个数减1
    modCount++;//修改记录加1
    return element;//返回f结点的元素值
}
private E unlinkLast(Node<E> l) {//卸下链表的最后一个非空结点l
    final E element = l.item;//记录l结点的元素值
    final Node<E> prev = l.prev;//记录l结点的前驱
    l.item = null;//l结点元素置null
    l.prev = null; //l结点前驱引用置null
    last = prev;//l结点的前驱为新的最后一个结点,last指向记录的l的前驱
    if (prev == null)//如果l的前驱为null,则链表中只有一个结点l
        first = null;//first指向null
    else//如果l的前驱不为null
        prev.next = null;//l前驱的后继引用置null,因为l的前驱为新的最后一个结点。原来结点l没有引用指向将会被GC回收
    size--;//元素个数减1
    modCount++;//修改记录加1
    return element;//返回l结点的元素值
}
E unlink(Node<E> x) {{//卸下链表的一个非空结点x
    final E element = x.item;//保存结点x的元素值
    final Node<E> next = x.next;//保存结点x的后继
    final Node<E> prev = x.prev;//保存结点x的前驱
    /*结点x与前驱结点prev解除关系*/
    if (prev == null) {//如果前驱为null,则x点为第一个结点
        first = next;//first指向x结点的后继
    } else {
        prev.next = next;//x结点的前驱的后继引用指向x结点的后继
        x.prev = null;//x结点的前驱引用置null
    }
    /*结点x与后继结点next解除关系*/
    if (next == null) {//如果后继为null,则x点为最后一个结点
        last = prev;//last指向x的前驱
    } else {
        next.prev = prev;//x结点的后继结点的前驱引用指向x结点的前驱
        x.next = null;//x结点后继引用置null
    }
    x.item = null;//x结点的元素值置null
    size--;//元素个数减1
    modCount++;//修改记录加1
    return element;//返回x结点的元素值
}
Node<E> node(int index) {//返回指定索引的非空元素
    if (index < (size >> 1)) {//如果index小于链表元素个数的1/2值,也就是index在链表的前半部分
        Node<E> x = first;//从头往后查找
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;//返回找到结点x
    } else {//index在链表的后半部分
        Node<E> x = last;//从后往前查找
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;//返回找到结点x
    }
}

48个对外提供的接口方法

增删改查操作
public E getFirst() {//获取链表的第一个元素值
    final Node<E> f = first;//链表第一个元素值就是结点first的值
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}
public E getLast() {//获取链表的最后一个元素值
    final Node<E> l = last;//链表最后一个元素值就是结点last的值
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}
public E removeFirst() {//删除链表的第一个结点
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);//调用unlinkFirst函数
}
public E removeLast() {//删除链表的最后一个结点
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return unlinkLast(l);//调用unlinkLast函数
}
public void addFirst(E e) {//元素e插入到链表开始
    linkFirst(e);//调用linkFirst函数
}
public void addLast(E e) {//元素e插入到链表末尾
    linkLast(e);//调用linkLast函数
}
public boolean contains(Object o) {//链表是否包含元素o
    return indexOf(o) != -1;
}
public int indexOf(Object o) {//从前往后查找元素o所在的结点,返回索引值
    int index = 0;
    if (o == null) {//如果o为null,查找第一个元素为null的结点
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null)
                return index;
            index++;
        }
    } else {//查找第一个元素为o的结点
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item))
                return index;
            index++;
         }
    }
    return -1;
}
public int lastIndexOf(Object o) {//从后往前查找元素o所在的结点,返回索引值
    int index = size;
    if (o == null) {//如果o为null,查找元素为null最后的结点
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (x.item == null)
                return index;
        }
    } else {//查找元素值为o的最后的结点
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (o.equals(x.item))
                return index;
        }
    }
    return -1;
}
public int size() {//返回链表中元素的个数
    return size;
}
public boolean add(E e) {//插入元素e到链表的末尾
    linkLast(e);//调用linkLast函数
    return true;
}
public boolean remove(Object o) {//从前往后删除元素为o的第一个结点
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);//调用unlink函数
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);//调用unlink函数
                return true;
            }
        }
    }
    return false;
}
public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {//从index处插入集合c的全部元素到链表中
    checkPositionIndex(index);//检验索引是否超出范围
    Object[] a = c.toArray();//集合c转化为数组
    int numNew = a.length;
    if (numNew == 0)//如果数组元素格式为0,证明没有需要添加的元素
        return false;
    /*查找插入点index处,前驱和后继结点,插入的所有元素将在pred和succ之间*/
    Node<E> pred, succ;
    if (index == size) {//插入链表的末尾
        succ = null;
        pred = last;
    } else {//插入链表的中间
        succ = node(index);//后继就是当前index索引的结点,这样新插入的元素才从index起
        pred = succ.prev;//前驱是index-1索引的结点,这里用succ的前驱引用降低时间复杂度
    }
    for (Object o : a) {//变量集合c生成的数组
        @SuppressWarnings("unchecked") E e = (E) o;
        Node<E> newNode = new Node<>(pred, e, null);//只操作前驱结点的引用,依次将新结点链接到pred结点之后
        if (pred == null)//pred为null,则新插入的结点为第一个结点
            first = newNode;
        else
            pred.next = newNode;//处理pred的next引用指向新结点
        pred = newNode;//pred指向新结点
    }
    /*最后操作后继结点的引用*/
    if (succ == null) {//succ为null说明插入的为链表的末尾
        last = pred;//last指向pred
    } else {//插入链表的中间
        pred.next = succ;//pred的next引用指向succ
        succ.prev = pred;//succ的prev引用指向pred
    }
    size += numNew;//增加size
    modCount++;//修改操作加1
    return true;
}
public void clear() {
    /*链表的结点全部处理,前驱引用、后继引用和元素值都置null,不可达对象可以被GC回收*/
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;//first和last都置null
    size = 0;//size为0
    modCount++;//操作增加1
}
public E get(int index) {//根据索引index找到链表结点,返回元素值
    checkElementIndex(index);
    return node(index).item;//调用node函数
}
public E set(int index, E element) {//链表index处的结点元素值设置为element
    checkElementIndex(index);
    Node<E> x = node(index);//调用node函数
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
public void add(int index, E element) {//在index处增加元素为element的结点
    checkPositionIndex(index);
    if (index == size)//如果在size处相当于在链表末尾添加
        linkLast(element);//调用linkLast函数
    else
        linkBefore(element, node(index));//调用linkBefore函数
}
public E remove(int index) {//删除所以index的结点
    checkElementIndex(index);
    return unlink(node(index));//调用unlink函数
}
private boolean isElementIndex(int index) {//判断元素索引是否正确
    return index >= 0 && index < size;
}
private boolean isPositionIndex(int index) {//判断添加添加元素索引是否正确
    return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {//打印越界错误信息
    return "Index: "+index+", Size: "+size;
}
private void checkElementIndex(int index) {//检查元素索引信息
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void checkPositionIndex(int index) {//检查添加元素索引信息
    if (!isPositionIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
public E peek() {//获取但不删除链表的第一个结点,如果不存在返回null
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}
public E element() {//获取但不删除链表的第一个结点,如果不存在弹出NoSuchElementException异常
    return getFirst();
}
public E poll() {//获取并且删除链表的第一个结点,如果不存在返回null
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
public E remove() {//删除链表第一个结点,并且返回元素值
    return removeFirst();
}
public boolean offer(E e) {//添加元素e到链表的末尾
    return add(e);
}
public boolean offerFirst(E e) {//添加元素e作为链表的第一个结点
    addFirst(e);
    return true;
}
public boolean offerLast(E e) {//添加元素e到链表末尾
    addLast(e);
    return true;
}
public E peekFirst() {//获取但不删除链表的第一个结点元素,不存在返回null
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}
public E peekLast() {//获取但不删除链表的最后一个结点元素,不存在返回null
    final Node<E> l = last;
    return (l == null) ? null : l.item;
}
public E pollFirst() {//获取并且删除链表的第一个结点,不存在返回null
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {//获取并且删除链表的最后一个结点,不存在返回null
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}
public void push(E e) {//LinkedList模拟实现栈结构,入栈操作。元素作为第一个结点添加到链表中
    addFirst(e);
}
public E pop() {//LinkedList模拟实现栈结构,出栈操作。弹出并且删除链表的第一个结点
    return removeFirst();
}
public boolean removeFirstOccurrence(Object o) {//从前往后遍历删除链表中出现的第一个元素o
    return remove(o);
}
public boolean removeLastOccurrence(Object o) {//从后往前遍历删除链表中出现的最后为o的结点
    if (o == null) {//o为null
        for (Node<E> x = last; x != null; x = x.prev) {
            if (x.item == null) {
                unlink(x);//删除最后为null的结点
                return true;
            }
        }
    } else {
        for (Node<E> x = last; x != null; x = x.prev) {
            if (o.equals(x.item)) {
                unlink(x);//删除最后为o的结点
                return true;
            }
         }
    }
    return false;
}
2个迭代器相关函数
public ListIterator<E> listIterator(int index) {//初始化迭代器,下一个遍历到的元素结点是index索引处
    checkPositionIndex(index);
    return new ListItr(index);
}
private class ListItr implements ListIterator<E> {//迭代器
    private Node<E> lastReturned;//记录上一次访问到的结点
    private Node<E> next;//记录将要访问的结点
    private int nextIndex;//记录将要访问结点的索引
    private int expectedModCount = modCount;//期望修改的次数
    ListItr(int index) {//初始化迭代器
        //next将要访问的结点为索引index结点,如果index为size,next则为null
        next = (index == size) ? null : node(index);
        nextIndex = index;//将要访问的结点索引初始化为index
    }
    public boolean hasNext() {//是否有下一个可以访问的结点
        return nextIndex < size;
    }
    public E next() {//迭代获取下一个元素
        checkForComodification();
        if (!hasNext())
            throw new NoSuchElementException();
        lastReturned = next;//lastReturned指向访问next
        next = next.next;//next后移一位
        nextIndex++;//nextIndex自增1
        return lastReturned.item;//返回lastReturned的元素值
    }
    public boolean hasPrevious() {//判断是否有前一个元素
        return nextIndex > 0;
    }
    public E previous() {//迭代前一个元素
        checkForComodification();
        if (!hasPrevious())
            throw new NoSuchElementException();
        //next为null时,lastReturned和next设置为last。因为next为null表明没有下一个元素需要访问了,那么上一个元素就是链表的最后一个元素
        //next不为null时,lastReturned和next设置为next的前驱,访问的是next的前一个元素
        lastReturned = next = (next == null) ? last : next.prev;
        nextIndex--;
        return lastReturned.item;
    }
    public int nextIndex() {//返回将要访问结点的索引
        return nextIndex;
    }
    public int previousIndex() {//返回前一个元素的索引
        return nextIndex - 1;
    }
    public void remove() {//删除最近一次访问到的结点
        checkForComodification();
        if (lastReturned == null)
            throw new IllegalStateException();
        Node<E> lastNext = lastReturned.next;
        unlink(lastReturned);//删除最近一次访问到的结点
        if (next == lastReturned)//可能由于访问前一个结点等操作导致next == lastReturned,这时需要修改next,而nextIndex保持不变
            next = lastNext;
        else//否则nextIndex自减1即可
            nextIndex--;
        lastReturned = null;//因为上次访问的结点已经删除,所以lastReturned置null
        expectedModCount++;
    }
    public void set(E e) {//最近访问的结点元素设置为e
        if (lastReturned == null)
            throw new IllegalStateException();
        checkForComodification();
        lastReturned.item = e;//上次访问结点lastReturned元素置e
    }
    public void add(E e) {//添加元素在下次访问结点next之前一个位置
        checkForComodification();
        lastReturned = null;
        if (next == null)//链表遍历结束,e插入到链表末尾
            linkLast(e);
        else
            linkBefore(e, next);//插入到next之前的一个位置
        nextIndex++;
        expectedModCount++;
    }
    public void forEachRemaining(Consumer<? super E> action) {//从当前迭代位置开始,对之后的链表结点遍历执行action操作
        Objects.requireNonNull(action);
        while (modCount == expectedModCount && nextIndex < size) {
            action.accept(next.item);//action的操作
            lastReturned = next;//迭代遍历
            next = next.next;
            nextIndex++;
        }
        checkForComodification();
    }
    final void checkForComodification() {//检查是否有其他线程修改链表
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}
public Iterator<E> descendingIterator() {//初始化一个反向迭代器,从后向前迭代遍历
    return new DescendingIterator();
}
private class DescendingIterator implements Iterator<E> {//使用正向迭代器ListItr实现,所有操作执行相反的逻辑
    private final ListItr itr = new ListItr(size());//迭代器从size索引处开始
    public boolean hasNext() {//是否有下一个元素
        return itr.hasPrevious();//调用itr去判断前一个元素
    }
    public E next() {//下一个元素
        return itr.previous();//调用itr去找寻前一个元素
    }
    public void remove() {//删除操作
        itr.remove();
    }
}
2个clone函数
private LinkedList<E> superClone() {//拷贝LinkedList对象
    try {
        return (LinkedList<E>) super.clone();
    } catch (CloneNotSupportedException e) {
        throw new InternalError(e);
    }
}
public Object clone() {//执行浅拷贝,新的链表和新的结点,但是结点所指向元素仍旧唯一的
    LinkedList<E> clone = superClone();
    // Put clone into "virgin" state,设置clone对象为初始状态
    clone.first = clone.last = null;
    clone.size = 0;
    clone.modCount = 0;
    // Initialize clone with our elements,拷贝链表的元素到clone对象中
    for (Node<E> x = first; x != null; x = x.next)
        clone.add(x.item);
    return clone;
}
2个生成数组函数
public Object[] toArray() {//链表元素生成数组。可以安全地修改数组。
    Object[] result = new Object[size];
    int i = 0;
    for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;
    return result;
}
public <T> T[] toArray(T[] a) {//链表元素放入数组a中
    if (a.length < size)//数组a长度不够,需要扩容成一个更大容量的数组
        a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
    int i = 0;
    Object[] result = a;
    for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;
    if (a.length > size)
        a[size] = null;
    return a;
}
2个IO流函数
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
    //链表元素写入到输出流中
    // Write out any hidden serialization magic
    s.defaultWriteObject();
    // Write out size
    s.writeInt(size);
    // Write out all elements in the proper order.
    for (Node<E> x = first; x != null; x = x.next)
        s.writeObject(x.item);
}
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
    //输入流读取写入到链表中
    // Read in any hidden serialization magic
    s.defaultReadObject();
    // Read in size
    int size = s.readInt();
    // Read in all elements in the proper order.
    for (int i = 0; i < size; i++)
        linkLast((E)s.readObject());
}
1个生成Spliterator函数

Spliterator接口是Java8提供的新功能,对链表进行分段访问。
最重要的两个函数:

  • trySplit函数用于对链表进行拆分,每执行一次从链表上拆分出来一部分结点
  • tryAdvance函数尝试判断是否还有未拆分或遍历的结点。如果存在,则对当前未访问的结点中的第一个结点执行action操作
@Override
public Spliterator<E> spliterator() {//生成Spliterator
    return new LLSpliterator<E>(this, -1, 0);
}
static final class LLSpliterator<E> implements Spliterator<E> {
    static final int BATCH_UNIT = 1 << 10;  // batch array size increment
    static final int MAX_BATCH = 1 << 25;  // max batch array size;
    final LinkedList<E> list; // null OK unless traversed
    Node<E> current;      // current node; null until initialized
    int est;              // size estimate; -1 until first needed
    int expectedModCount; // initialized when est set
    int batch;            // batch size for splits
    LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
        this.list = list;
        this.est = est;
        this.expectedModCount = expectedModCount;
    }
    final int getEst() {
        int s; // force initialization
        final LinkedList<E> lst;
        if ((s = est) < 0) {
            if ((lst = list) == null)
                s = est = 0;
            else {
                expectedModCount = lst.modCount;
                current = lst.first;
                s = est = lst.size;
            }
         }
        return s;
    }
    public long estimateSize() { return (long) getEst(); }
    public Spliterator<E> trySplit() {//对链表进行拆分,每执行一次从链表上拆分出来一部分结点
        Node<E> p;
        int s = getEst();
        if (s > 1 && (p = current) != null) {
            int n = batch + BATCH_UNIT;
            if (n > s)
                n = s;
            if (n > MAX_BATCH)
                n = MAX_BATCH;
            Object[] a = new Object[n];
            int j = 0;
            do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
            current = p;
            batch = j;
            est = s - j;
            return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
        }
        return null;
    }
    public void forEachRemaining(Consumer<? super E> action) {
        Node<E> p; int n;
        if (action == null) throw new NullPointerException();
        if ((n = getEst()) > 0 && (p = current) != null) {
            current = null;
            est = 0;
            do {
                E e = p.item;
                p = p.next;
                action.accept(e);
             } while (p != null && --n > 0);
         }
         if (list.modCount != expectedModCount)
             throw new ConcurrentModificationException();
    }
    public boolean tryAdvance(Consumer<? super E> action) {
        //尝试判断是否还有未拆分或遍历的结点。如果存在,则对当前未访问的结点中的第一个结点执行action操作
        Node<E> p;
        if (action == null) throw new NullPointerException();
        if (getEst() > 0 && (p = current) != null) {
            --est;
            E e = p.item;
            current = p.next;
            action.accept(e);
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return true;
         }
         return false;
    }
    public int characteristics() {
        return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
    }
}

总结

学习LinkedList源码主要从两点掌握,第一,掌握熟悉链表的结点实现(双向链表实现)以及链表的操作实现(主要是如何设置结点前驱后继的引用完成增删改查操作);第二,学习JDK开发者的编程思想,对于模块是如何设计,怎样尽可能的让模块可以复用,以及程序代码健壮性需要考虑的校验等问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容