当我想在 Java 中的 class 方法中使用 class 字段时,定义最终局部变量是一种好习惯吗?
Is it a good practice to define final local variable when I want to use class field in class method in Java?
最近,我正在阅读 Java 源代码,例如ArrayList、ArrayDeque、LinkedList 等。我发现当他们想要在 class 方法中使用某些 class 字段时, 他们将始终声明一个等于该字段的最终局部变量。 但是,如果我没有读过源代码,我绝对不会考虑做这个东西。这样做是一种好习惯吗?或者为什么 Java Source Code 选择这样做?有什么优势,比如性能?
参考:
示例:
链表中的 getFirst()
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
ArrayDeque 中的布尔删除(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, 0, elements, 1, i);
elements[0] = elements[mask];
System.arraycopy(elements, h, elements, h + 1, 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, elements, i, back);
tail = t - 1;
} else { // Wrap around
System.arraycopy(elements, i + 1, elements, i, mask - i);
elements[mask] = elements[0];
System.arraycopy(elements, 1, elements, 0, t);
tail = (t - 1) & mask;
}
return true;
}
}
这是一个微优化,对于标准库代码来说可能有意义,因为标准库代码将被很多人使用,而阅读的人要少得多。我不建议在你自己的代码中这样做,除非性能是一个真正的问题并且你已经分析了你的应用程序以确定代码的哪些部分是真正的瓶颈,否则性能增益可能可以忽略不计,所以主要效果将使您的代码更加冗长。
使用局部变量应该有一些非常小的性能优势,因为:
- 局部变量将分配在堆栈或寄存器中,这比其他内存访问速度更快,因此仅从堆中访问单个字段肯定会最大限度地减少较慢访问的次数(最好是访问内存在堆上只能和访问局部变量一样快,但在最坏的情况下它可能会更慢。
- 用于访问局部变量的 bytecode instructions 更短。前四个局部变量可以使用
aload0
、aload1
、aload2
和aload3
加载,每个都是一个字节,aload
是两个字节包括要加载的局部变量的索引。相比之下,getfield
占用三个字节,其中两个用于引用正在访问的字段。
非同步方法也可能存在语义差异,因为即使在方法执行期间该字段被另一个线程更改,局部变量也会保持相同的值。然而,大多数标准库 classes 你会看到这种模式并不意味着是线程安全的,即使 class 是为了并发,这样做也不一定该方法无论如何都是线程安全的。
最近,我正在阅读 Java 源代码,例如ArrayList、ArrayDeque、LinkedList 等。我发现当他们想要在 class 方法中使用某些 class 字段时, 他们将始终声明一个等于该字段的最终局部变量。 但是,如果我没有读过源代码,我绝对不会考虑做这个东西。这样做是一种好习惯吗?或者为什么 Java Source Code 选择这样做?有什么优势,比如性能?
参考:
示例: 链表中的 getFirst()
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
ArrayDeque 中的布尔删除(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, 0, elements, 1, i);
elements[0] = elements[mask];
System.arraycopy(elements, h, elements, h + 1, 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, elements, i, back);
tail = t - 1;
} else { // Wrap around
System.arraycopy(elements, i + 1, elements, i, mask - i);
elements[mask] = elements[0];
System.arraycopy(elements, 1, elements, 0, t);
tail = (t - 1) & mask;
}
return true;
}
}
这是一个微优化,对于标准库代码来说可能有意义,因为标准库代码将被很多人使用,而阅读的人要少得多。我不建议在你自己的代码中这样做,除非性能是一个真正的问题并且你已经分析了你的应用程序以确定代码的哪些部分是真正的瓶颈,否则性能增益可能可以忽略不计,所以主要效果将使您的代码更加冗长。
使用局部变量应该有一些非常小的性能优势,因为:
- 局部变量将分配在堆栈或寄存器中,这比其他内存访问速度更快,因此仅从堆中访问单个字段肯定会最大限度地减少较慢访问的次数(最好是访问内存在堆上只能和访问局部变量一样快,但在最坏的情况下它可能会更慢。
- 用于访问局部变量的 bytecode instructions 更短。前四个局部变量可以使用
aload0
、aload1
、aload2
和aload3
加载,每个都是一个字节,aload
是两个字节包括要加载的局部变量的索引。相比之下,getfield
占用三个字节,其中两个用于引用正在访问的字段。
非同步方法也可能存在语义差异,因为即使在方法执行期间该字段被另一个线程更改,局部变量也会保持相同的值。然而,大多数标准库 classes 你会看到这种模式并不意味着是线程安全的,即使 class 是为了并发,这样做也不一定该方法无论如何都是线程安全的。