java集合源码分析二:List与AbstractList

概述

上一篇文章基本介绍了 List 接口的上层结构,也就是 Iterable 接口,Collection 接口以及实现了 Collection 接口的抽象类的基本情况,现在在前文的基础上,我们将继续向实现前进,进一步的探索 List 接口与其抽象实现类 AbstractList 的源码,了解他是如何在三大实现类与 Collection 接口之间实现承上启下的作用的。

一、List 接口

List 接口的方法

List 接口继承了 Collection 接口,在 Collection 接口的基础上增加了一些方法。相对于 Collection 接口,我们可以很明显的看到,List 中增加了非常多根据下标操作集合的方法,我们可以简单粗暴的分辨一个方法的抽象方法到底来自 Collection 还是 List:参数里有下标就是来自 List,没有就是来自 Collection。

可以说,List 接口在 Collection 的基础上,进一步明确了 List 集合运允许根据下标快速存取的特性

1.新增的方法

  • get():根据下标获取指定元素;
  • replaceAll():参数一个函数式接口UnaryOperator<E>,这个方法允许我们通过传入的匿名实现类的方法去对集合中的每一个类做一些处理以后再放回去;
  • sort():对集合中的数据进行排序。参数是 Comparator<? super E>,这个参数让我们传入一个比较的匿名方法,用于数组排序;
  • set():用指定的元素替换集合中指定位置的元素;
  • indexOf():返回指定元素在此列表中首次出现的索引;如果此列表不包含该元素,则返回-1;
  • lastIndexOf():返回指定元素在此列表中最后一次出现的索引,否则返回-1;
  • listIterator():这个是个多态的方法。无参的 listIterator()用于获取迭代器,而有参的 listIterator()可以传入下标,从集合的指定位置开始获取迭代器。指定的索引指示首次调用next将返回的第一个元素。
  • subList():返回此列表中指定的两个指定下标之间的集合的视图。注意,这里说的是视图,因而对视图的操作会影响到集合,反之亦然。

2.同名的新方法

  • add():添加元素。List 中的 add() 参数的(int,E),而 Collection 中的 add() 参数是 E,因此 List 集合中同时存在指定下标和不指定下标两种添加方式
  • remove():删除指定下标的元素。注意,List 的 remove() 参数是 int ,而 Collection 中的 ``remove()` 参数是 Objce,也就是说,List 中同时存在根据元素是否相等和根据元素下标删除元素两种方式

3.重写的方法

  • spliterator():List 接口重写了 Collection 接口的默认实现,换成了根据顺序的分割。

二、AbstractList 抽象类

AbstractList 类是一个继承了 AbstractCollection 类并且实现了 List 接口的抽象类,它相当于在 AbstractCollection 后的第二层方法模板。是对 List 接口的初步实现,同时也是 Collection 的进一步实现。

1.不支持的实现

可以直接通过下标操作的set()add()remove()都是 List 引入的新接口,这些都 AbstractList 都不支持,要使用必须由子类重写。

public E set(int index,E element) {
    throw new UnsupportedOperationException();
}
public void add(int index,E element) {
    throw new UnsupportedOperationException();
}
public E remove(int index) {
    throw new UnsupportedOperationException();
}

2.内部类们

跟 AbstractCollection 类不同,AbstractList 拥有几个特别的内部类,他们分别的迭代器类:Itr 和 ListItr,对应获取他们的方法是:

  • iterator():获取 Itr 迭代器类;
  • listIterator():获取 ListItr 迭代器类。这是个多态方法,可以选择是否从指定下标开始,默认从下标为0的元素开始迭代;

视图类 SubList 和 RandomAccessSubList:

  • subList():获取视图类,会自动根据实现类是否继承 RandomAccess 而返回 SubList 或 RandomAccessSubList。

这些内部类同样被一些其他的方法所依赖,所以要全面的了解 AbstractList 方法的实现,就需要先了解这些内部类的作用和实现原理。

三、subList方法与内部类

subList()算是一个比较常用的方法了,在 List 接口的规定中,这个方法应该返回一个当前集合的一部分的视图:

public List<E> subList(int fromIndex,int toIndex) {
    // 是否是实现了RandomAccess接口的类
    return (this instanceof RandomAccess ?
            // 是就返回一个可以随机访问的内部类RandomAccessSubList
            new RandomAccessSubList<>(this,fromIndex,toIndex) :
            // 否则返回一个普通内部类SubList
            new SubList<>(this,toIndex));
}

这里涉及到 RandomAccessSubList 和 SubList 这个内部类,其中,RandomAccessSubList 类是 SubList 类的子类,但是实现了 RandomAccess 接口。

1.SubList 内部类

我们可以简单的把 SubList 和 AbstractList 理解为装饰器模式的一种实现,就像 SynchronizedList 和 List 接口的实现类一样。SubList 内部类通过对 AbstractList 的方法进行了再一次的封装,把对 AbstractList 的操作转变为了对 “视图的操作”。

我们先看看 SubList 这个类的成员变量和构造方法:

class SubList<E> extends AbstractList<E> {
    // 把外部类AbstractList作为成员变量
    private final AbstractList<E> l;
    // 表示视图的起始位置(偏移量)
    private final int offset;
    // SubList视图的长度
    private int size;

    SubList(AbstractList<E> list,int fromIndex,int toIndex) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > list.size())
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
        // 获取外部类的引用
        // 这也是为什么操作视图或者外部类都会影响对方的原因,因为都操作内存中的同一个实例
        l = list;
        // 获取当前视图在外部类中的起始下标
        offset = fromIndex;
        // 当前视图的长度就是外部类截取的视图长度
        size = toIndex - fromIndex;
        this.modCount = l.modCount;
    }
    
}

我们可以参考图片理解一下:

image-20201126114026855

然后 subList 里面的方法就很好理解了:

public E set(int index,E element) {
    // 检查下标是否越界
    rangeCheck(index);
    // 判断是存在并发修改
    checkForComodification();
    // 把元素添加到偏移量+视图下标的位置
    return l.set(index+offset,element);
}

其他方法都差不多,这里便不再多费笔墨了。

2.RandomAccessSubList 内部类

然后是 SubList 的子类 RandomAccessSubList:

class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
    RandomAccessSubList(AbstractList<E> list,int toIndex) {
        super(list,toIndex);
    }

    public List<E> subList(int fromIndex,int toIndex) {
        return new RandomAccessSubList<>(this,toIndex);
    }
}

我们可以看见,他实际上还是 SubList,但是实现了 RandomAccess 接口。关于这个接口,其实只是一个标记,实现了该接口的类可以实现快速随机访问(下标),通过 for 循环+下标取值会比用迭代器更快。

Vector 和 ArrayList 都实现了这个接口,而 LinkedList 没有。专门做此实现也是为了在实现类调用的 subList()方法时可以分辨这三者。

四、iterator方法与内部类

在 AbstractList 里面,为我们提供了 Itr 和 ListItr 两种迭代器。

迭代器是 AbstractList 中很重要的一块内容,他是对整个接口体系的顶层接口,也就是 Iterable 接口中的 iterator() 方法的实现,源码中的很多涉及遍历的方法,都离不开内部实现的迭代器类。

1.迭代器的 fast-fail 机制

我们知道,AbstractList 默认是不提供线程安全的保证的,但是为了尽可能的避免并发修改对迭代带来的影响,JDK 引入一种 fast-fail 的机制,即如果检测的发生并发修改,就立刻抛出异常,而不是让可能出错的参数被使用从而引发不可预知的错误。

对此,AbstractList 提供了一个成员变量 modCount,JavaDoc 是这么描述它的:

已对该列表进行结构修改的次数。

结构修改是指更改列表大小或以其他方式干扰列表的方式,即正在进行的迭代可能会产生错误的结果。该字段由iterator和listIterator方法返回的迭代器和列表迭代器实现使用。如果此字段的值意外更改,则迭代器(或列表迭代器)将抛出ConcurrentModificationException,以响应下一个,移除,上一个,设置或添加操作。

面对迭代期间的并发修改,这提供了快速失败的行为,而不是不确定的行为。

子类对此字段的使用是可选的。如果子类希望提供快速失败的迭代器(和列表迭代器),则只需在其add(int,E)和remove(int)方法(以及任何其他覆盖该方法导致结构化的方法)中递增此字段即可)。

一次调用add(int,E)或remove(int)不得在此字段中添加不超过一个,否则迭代器(和列表迭代器)将抛出虚假的ConcurrentModificationExceptions。

如果实现不希望提供快速失败迭代器,则可以忽略此字段。

这个时候我们再回去看看迭代器类 Itr 的一部分代码,可以看到:

private class Itr implements Iterator<E> {
    // 迭代器认为后备列表应该具有的modCount值。如果违反了此期望,则迭代器已检测到并发修改。
    int expectedModCount = modCount;
    
    // 检查是否发生并发操作
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

结合代码,我们就不难理解这个 fast-fail 机制是怎么实现的了:

AbstractList 提供了一个成员变量用于记录对集合结构性修改的次数,如果子类希望实现并发修改错误的检查,就需要结构性操作的方法里让modCount+1。这样。在获取迭代器以后,迭代器内部会获取当前的modCount赋值给expectedModCount

当使用迭代器迭代的时候,每一次迭代都会检测modCountexpectedModCount是否相等。如果不相等,说明迭代器创建以后,集合结构被修改了,这个时候再去进行迭代可能会出现错误(比如少遍历一个,多遍历一个),因此检测到后会直接抛出 ConcurrentModificationException异常。

ListItr 继承了 Itr ,因此他们都有一样的 fast-fail机制。

值得一提的是,对于启用了 fast-fail 机制的实现类,只有使用迭代器才能边遍历边删除,原因也是因为并发修改检测:

2.Itr 迭代器

现在,回到 Itr 的代码上:

private class Itr implements Iterator<E> {
    // 后续调用next返回的元素索引
    int cursor = 0;

    // 最近一次调用返回的元素的索引。如果通过调用remove删除了此元素,则重置为-1。
    int lastRet = -1;

    // 迭代器认为后备列表应该具有的modCount值。如果违反了此期望,则迭代器已检测到并发修改。
    int expectedModCount = modCount;
	
    public boolean hasNext() {
        return cursor != size();
    }

    public E next() {
        checkForComodification();
        try {
            int i = cursor;
            E next = get(i);
            lastRet = i;
            cursor = i + 1;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }
	
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

迭代方法

除了并发修改检测外,迭代器迭代的方式也出乎意料。我们可以看看 hasNext()方法:

public E next() {
    // 检验是否发生并发修改
    checkForComodification();
    try {
        int i = cursor;
        E next = get(i);
        lastRet = i;
        cursor = i + 1;
        return next;
    } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

这个逻辑其实跟链表的遍历是一样的,只不过指针变成了数组的下标。以链表的方式去理解:

我们把循环里调用next()之后的节点叫做下一个节点,反正称为当前节点。假如现在有 a,b,c 三个元素:

  • 当初始化的时候,指向最后一次操作的的节点的指针 lastRet=-1,即当前节点不存在,当前游标 cursor=0,即指向下一个节点 a;
  • 当开始迭代的时候,把游标的值赋给临时指针 i,然后通过游标获取并返回下一个节点 a,再把游标指向 a 的下一个节点 b,此时 cursor=1lastRet=-1i=1
  • 接着让lastRet=i,也就是当前指针指向新的当前节点 a,现在 lastRet=0cursor=1`,完成了对第一个节点 a 的迭代;
  • 重复上述过程,把节点中的每一个元素都处理完。

现在我们知道了迭代的方式,cursorlastRet 的作用,也就不难理解 remove()方法了:

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        // 调用删除方法
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
		   // 因为删除了当前第i个节点,所以i+1个节点就会变成第i个节点,
            // 调用next()以后cursor会+1,因此如果不让cursor-1,就会,next()以后跳过原本的第i+1个节点
            // 拿上面的例子来说,你要删除abc,但是在删除a以后会跳过b直接删除c
            cursor--;
        // 最近一个操作的节点被删除了,故重置为-1
        lastRet = -1;
        // 因为调用了外部类的remove方法,所以会改变modCount值,迭代器里也要获取最新的modCount
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
    }
}

至于hasNext()方法没啥好说的,如果 cursor已经跟集合的长度一样长了,说明就已经迭代到底了。

2.ListItr 迭代器

ListItr 继承了 Itr 类,并且实现了 ListIterator 接口。其中,ListIterator 接口又继承了 Iterator 接口。他们的类关系图是这样的:

ListIterator 的类关系图

ListIterator 接口在 Iterator 接口的基础上,主要提供了六个新的抽象方法:

  • hasPrevious():是否有前驱节点;
  • previous():向前迭代;
  • nextIndex():获取下一个元素的索引;
  • previousIndex():返回上一个元素的索引;
  • set():替换元素;
  • add():添加元素;

可以看出来,实现了 ListIterator 的 ListItr 类要比 Itr 更加强大,不但可以向后迭代,还能向前迭代,还可以在迭代过程中更新或者添加节点。

private class ListItr extends Itr implements ListIterator<E> {
    // 可以自己设置迭代的开始位置
    ListItr(int index) {
        cursor = index;
    }
	
    // 下一节点是否就是第一个节点
    public boolean hasPrevious() {
        return cursor != 0;
    }

    public E previous() {
        // 检查并发修改
        checkForComodification();
        try {
            // 让游标指向当前节点
            int i = cursor - 1;
            // 使用AbstractList的get方法获取当前节点
            E previous = get(i);
            lastRet = cursor = i;
            return previous;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }
	
    // 获取下一节点的下标
    public int nextIndex() {
        return cursor;
    }

    // 获取当前节点(下一个节点的上一个节点)的下标
    public int previousIndex() {
        return cursor-1;
    }

    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.set(lastRet,e);
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    public void add(E e) {
        checkForComodification();

        try {
            int i = cursor;
            // 往下一个节点的位置添加新节点
            AbstractList.this.add(i,e);
            lastRet = -1;
            cursor = i + 1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

这里比较不好理解的是下一节点还有当前节点这个概念,其实可以这么理解:cursor游标指定的必定是下一次 next()操作要得到的节点,因此cursor在操作前或者操作后指向的必定就是下一节点,因此相对下一节点,cursor其实就是当前节点,相对下一节点来说就是上一节点。

也就是说,假如现在有 a,b,c 三个元素,现在的 cursor 为2,也就是指向 b。调用 next()以后游标就会指向 c,而调用previous()以后游标又会指回 b。

至于lastRet这个成员变量只是用于记录最近一次操作的节点是哪个,跟方向性是无关。

五、AbstractList 实现的方法

1.add

注意,现在现在 AbstractList 的 add(int index,E e)仍然还不被支持,add(E e)只是定义了通过 add(int index,E e)把元素添加到队尾的逻辑。

// 不指定下标的add,默认逻辑为添加到队尾
public boolean add(E e) {
    add(size(),e);
    return true;
}

关于 AbstractList 和 AbstractCollection 中 add()方法之间的关系是这样的:

add方法的实现逻辑

AbstractList 这里的 add(E e)就非常有模板方模式提到的“抽象类规定算法骨架”这个感觉了。AbstractCollection 接口提供了 add(E e)的初步实现(尽管只是抛异常),然后到了 AbstractList 中就完善了 add(E e)方法的逻辑——通过调用 add(int index,E e)方法把元素插到队尾,但是具体的 add(int index,E e)怎么实现再交给子类决定。

2.indexOf/LastIndexOf

public int indexOf(Object o) {
    ListIterator<E> it = listIterator();
    if (o==null) {
        while (it.hasNext())
            if (it.next()==null)
                return it.previousIndex();
    } else {
        while (it.hasNext())
            if (o.equals(it.next()))
                return it.previousIndex();
    }
    return -1;
}

public int lastIndexOf(Object o) {
    ListIterator<E> it = listIterator(size());
    if (o==null) {
        while (it.hasPrevious())
            if (it.previous()==null)
                return it.nextIndex();
    } else {
        while (it.hasPrevious())
            if (o.equals(it.previous()))
                return it.nextIndex();
    }
    return -1;
}

3.addAll

这里的addAll来自于List 集合的 addAll。参数是需要合并的集合跟起始下标:

public boolean addAll(int index,Collection<? extends E> c) {
    rangeCheckForAdd(index);
    boolean modified = false;
    for (E e : c) {
        add(index++,e);
        modified = true;
    }
    return modified;
}

这里的 rangeCheckForAdd()方法是一个检查下标是否越界的方法:

private void rangeCheckForAdd(int index) {
    // 不得小于0或者大于集合长度
    if (index < 0 || index > size())
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

4.removeRange

这个方法是 AbstractList 私有的方法,一般被子类用于删除一段多个元素,实现上借助了 ListIter 迭代器。

protected void removeRange(int fromIndex,int toIndex) {
    ListIterator<E> it = listIterator(fromIndex);
    // 从fromIndex的下一个开始,删到toIndex
    for (int i=0,n=toIndex-fromIndex; i<n; i++) {
        it.next();
        it.remove();
    }
}

六、AbstractList 重写的方法

1.equals

equals()方法比较特殊,他是来自于 Collection 和 List 接口中的抽象方法,在 AbstractList 得中实现,但是实际上也是对 Object 中方法的重写。考虑到 equals()情况特殊,所以我们也认为它是一个重写的方法。

我们可以先看看 JavaDoc 是怎么说的:

比较指定对象与此列表是否相等。当且仅当指定对象也是一个列表,并且两个列表具有相同的大小,并且两个列表中所有对应的元素对相等时,才返回true

然后再看看源码是什么样的:

public boolean equals(Object o) {
    // 是否同一个集合
    if (o == this)
        return true;
    // 是否实现了List接口
    if (!(o instanceof List))
        return false;
	
    // 获取集合的迭代器并同时遍历
    ListIterator<E> e1 = listIterator();
    ListIterator<?> e2 = ((List<?>) o).listIterator();
    while (e1.hasNext() && e2.hasNext()) {
        E o1 = e1.next();
        Object o2 = e2.next();
        // 两个集合中的元素是否相等
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }
    // 是否两个集合长度相同
    return !(e1.hasNext() || e2.hasNext());
}

从源码也可以看出,AbstractList 的 equals() 是要求两个集合绝对相等的:顺序相等,并且相同位置的元素也要相等。

2.hashCode

hashCode()equals()情况相同。AbstractList 重新定义了 hashCode()

public int hashCode() {
    int hashCode = 1;
    for (E e : this)
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    return hashCode;
}

新的计算方式会获取集合中每一个元素的 hashCode 去计算集合的 hashCode,这可能是考虑到原本情况下,同一个集合哪怕装入的元素不同也会获得相同的 hashCode,可能会引起不必要的麻烦,因此重写了次方法。

我们可以写个测试看看:

List<String> list1 = new ArrayList<>();
list1.add("a");
System.out.println(list1.hashCode()); // 128
list1.add("c");
System.out.println(list1.hashCode()); // 4067

七、总结

List 接口继承了 Collection 接口,新增方法的特点主要体现在可以通过下标去操作节点,可以说大部分下标可以作为参数的方法都是 List 中添加的方法。

AbstractList 是实现了 List 的抽象类,他实现了 List 接口中的大部分方法,同时他继承了 AbstractCollection ,沿用了一些 AbstractCollection 中的实现。这两个抽象类可以看成是模板方法模式的一种体现。

他提供了下标版的 add()remove()set()的空实现。

AbstractList 内部提供两个迭代器,Itr 和 ListItr,Itr 实现了 Iterator接口,实现了基本的迭代删除,而 ListItr 实现了ListIterator,在前者的基础上增加了迭代中添加修改,以及反向迭代的相关方法,并且可以从指定的位置开始创建迭代器。

AbstractList 的 SubList 可以看成 AbstractList 的包装类,他在实例化的时候会把外部类实例的引用赋值给成员变量,同名的操作方法还仍然是调用 AbstractList 的,但是基于下标的调用会在默认参数的基础上加上步长,以实现一种类似“视图”的感觉。

AbstractList 引入了并发修改下 fast-fail 的机制,在内部维护一个成员变量 modelCount,默认为零,每次结构性修改都会让其+1。在迭代过程中会默认检查 modelCount是否符合预期值,否则抛出异常。值得注意的是,这个需要实现类的配合,在实现 add()等方法的时候要让 modelCount+1。对于一些实现类,在迭代中删除可能会抛出 ConcurrentModificationExceptions,就是这方面的问题。

AbstractList 重写了 hashCode()方法,不再直接获取实例的 HashCode 值,而遍历集合,根据每一个元素的 HashCode 计算集合的 HashCode,这样保证了内容不同的相同集合不会得到相同的 HashCode。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&amp;FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = &quot;hello&quot;; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let&#39;s briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越