当我想在 Java 的类方法中使用类字段时,定义最终局部变量是一个好习惯吗?

问题描述

最近,我正在阅读 Java 源代码,例如ArrayList、arraydeque、LinkedList 等。我发现当他们想在类方法中使用某个类字段时,他们总是会声明一个等于该字段的最终局部变量。但是,如果我没有看了源码,我永远不会考虑做这件事。这样做是一个好习惯吗?或者为什么 Java Source Code 选择这样做?有什么优势,比如性能

参考:

LinkedList source code

ArrayList source code

ArrayDeque source code

示例: 链表中的 getFirst()

public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

arraydeque 中的布尔值 delete(int i)

    private boolean delete(int i) {
        checkInvariants();
        final Object[] elements = this.elements;
        final int mask = elements.length - 1;
        final int h = head;
        final int t = tail;
        final int front = (i - h) & mask;
        final int back  = (t - i) & mask;

        // Invariant: head <= i < tail mod circularity
        if (front >= ((t - h) & mask))
            throw new ConcurrentModificationException();

        // Optimize for least element motion
        if (front < back) {
            if (h <= i) {
                System.arraycopy(elements,h,elements,h + 1,front);
            } else { // Wrap around
                System.arraycopy(elements,1,i);
                elements[0] = elements[mask];
                System.arraycopy(elements,mask - h);
            }
            elements[h] = null;
            head = (h + 1) & mask;
            return false;
        } else {
            if (i < t) { // copy the null tail as well
                System.arraycopy(elements,i + 1,i,back);
                tail = t - 1;
            } else { // Wrap around
                System.arraycopy(elements,mask - i);
                elements[mask] = elements[0];
                System.arraycopy(elements,t);
                tail = (t - 1) & mask;
            }
            return true;
        }
    }

解决方法

这是一个微优化,它可能对标准库代码有意义,标准库代码将被很多人使用,而阅读的人却少得多。我不建议在你自己的代码中这样做,除非性能是一个真正的问题,并且你已经分析了你的应用程序以确定代码的哪些部分是真正的瓶颈,否则性能增益可能可以忽略不计,所以主要效果将是使您的代码更加冗长。

使用局部变量应该有一些非常小的性能优势,因为:

  • 局部变量将被分配在栈上或寄存器中,这比其他内存访问速度更快,因此仅从堆中进行单个字段访问肯定会最大限度地减少较慢访问的次数(充其量,访问内存在堆只能像访问局部变量一样快,但最坏的情况可能会更慢)。
  • 用于访问局部变量的 bytecode instructions 较短。前四个局部变量可以使用aload0aload1aload2aload3加载,每个都是一个字节,aload是两个字节,包括要加载的局部变量的索引。相比之下,getfield 需要三个字节,其中两个用于引用正在访问的字段。

非同步方法也可能存在语义差异,因为即使在方法执行期间该字段被另一个线程更改,局部变量也将保持相同的值。但是,您将在其中看到这种模式的大多数标准库类并不意味着是线程安全的,即使该类是为了并发,这样做也不一定使方法成为线程安全的。>