Java在for循环中使用getter还是创建局部变量?

Java在for循环中使用getter还是创建局部变量?,java,performance,loops,for-loop,getter,Java,Performance,Loops,For Loop,Getter,我有一个for循环,它运行4096次,应该尽可能快。性能在这里非常重要。目前,我在循环中使用getter方法,它只返回字段中的值或对象,而这些字段在循环进行时不会更改 例如: for (;;) { doSomething(example.getValue()); } Object object = example.getValue(); for (;;) { doSomething(object); } 使用getter是否有开销?使用以下方法是否更快 例如: for (;;

我有一个for循环,它运行4096次,应该尽可能快。性能在这里非常重要。目前,我在循环中使用getter方法,它只返回字段中的值或对象,而这些字段在循环进行时不会更改

例如:

for (;;) {
    doSomething(example.getValue());
}
Object object = example.getValue();
for (;;) {
    doSomething(object);
}
使用getter是否有开销?使用以下方法是否更快

例如:

for (;;) {
    doSomething(example.getValue());
}
Object object = example.getValue();
for (;;) {
    doSomething(object);
}
如果是,访问公共字段(如
example.value
)是否也是如此


编辑:我不在循环中使用
System.out.println()


编辑:某些字段不是
final
。没有字段是易变的,也没有方法(getter)是同步的

如果您需要尽快运行它,您不应该在关键部分使用
System.out.println


关于getter:使用getter有轻微的开销,但您不应该为此烦恼。Java确实在JIT编译器中进行了getter和setter优化。因此,最终它们将被本机代码取代。

这取决于getter

如果它是一个简单的getter,那么JIT无论如何都会将它与一个直接的字段访问连接起来,因此不会有可测量的差异。从风格的角度来看,使用getter——它的代码更少

如果getter正在访问一个
volatile
字段,则会有一个额外的内存访问命中,因为该值不能保存在寄存器中,但是命中非常小


如果getter是同步的,那么使用局部变量的速度将明显加快,因为不需要每次调用都获取和释放锁,但是循环代码将使用getter调用时字段的潜在过时值。

您应该更喜欢循环外的局部变量,基于以下原因:

  • 它通过避免在一行代码中调用嵌套的方法(如
    doSomething(example.getValue())
    ),并允许代码为getter方法返回的值提供更好、更具体的名称,使代码更易于阅读/理解
  • 并非所有的getter方法都是琐碎的(即,它们有时会做一些潜在的昂贵工作),但开发人员通常不会注意到这一点,假设给定的方法是琐碎且廉价的,而实际上并非如此。在这种情况下,代码可能会在开发人员没有意识到的情况下受到重大性能影响。提取到局部变量往往可以避免这个问题

  • 对性能的担忧很容易超出需要。我知道这种感觉。需要考虑的一些事项:

  • 4096并不多,所以除非必须在极短的时间内完成,否则不要太担心性能
  • 如果在这个循环中还有其他一些非常昂贵的事情发生,那么getter就不重要了
  • 过早的优化是万恶之源。首先集中精力使代码正确、清晰。然后测量和分析它,缩小最昂贵的东西的范围,并处理好它。如果可能,改进实际算法
  • 关于你的问题,我不知道JIT到底做了什么,但除非它能够确定地证明
    example.getValue()
    example.value
    在循环中不会改变(这很难做到,除非字段是
    final
    并且getter是微不足道的)那么从逻辑上讲,它无法避免在前一个示例中重复调用getter,因为这可能会改变程序的行为。重复的呼叫当然是一些非零量的额外工作

    说到这里,在循环之外创建局部变量,不管它是否更快,因为它更清晰。也许这让你感到惊讶,但是好的代码并不总是最短的。表达意图和其他信息非常重要。在这种情况下,循环外的局部变量使任何阅读代码的人都清楚地看到,
    doSomething
    的参数没有改变(特别是如果将其设置为final),这一点很有用。否则,他们可能需要做一些额外的挖掘,以确保他们知道程序的行为。

    As,在循环外部获取对象引用(
    object object=example.getValue();
    )可能比在循环内部调用getter更快(或者至少永远不会慢),因为

    • 在“最坏”的情况下,
      example.getValue()
      实际上可能会在后台做一些非常昂贵的计算,尽管这被认为是“微不足道的”。通过分配一次引用并重新使用它,您只需进行一次昂贵的计算
    • 在“最佳”情况下,
      example.getValue()
      执行一些琐碎的操作,例如
      返回值
      
    但是,更重要的是这两者之间的语义差异及其在多线程环境中可能产生的影响:如果对象的状态
    example
    发生变化,导致
    example.getValue()
    返回对不同对象的引用,则在每次迭代中,方法
    doSomething(Object Object)
    将通过直接调用
    doSomething(example.getValue())对
    Object
    的不同实例进行实际操作。另一方面,通过在循环外部调用getter并设置对返回实例的引用(
    objectobject=example.getValue();
    ),
    doSomething(Object)
    将对
    对象进行n次迭代操作

    语义上的这种差异可能导致多线程环境中的行为与单线程环境中的行为完全不同。此外,这不一定是一个实际的“内存中”多线程问题:如果
    example.getValue()
    依赖于例如数据库/HDD/网络资源,则可能