存取方法性能和优化

时间:2012-02-28 23:12:42

标签: java performance coding-style getter

通常,我会遇到代码,其中 Getter 方法被重复使用/滥用以获取某些值或将其作为方法参数传递,例如:

public class Test {
   public void someMethod() {
      if(person.getName() != null && person.getName().equalsIgnoreCase("Einstein")) {
           method1(person.getName());
      }
      method2(person.getName());
      method3(person.getName());
      method4(person.getName());
   }
}

我通常编码,如下所示:

public class Test {
   public void someMethod() {
      String name = person.getName();
      if(name != null && name.equalsIgnoreCase("Einstein")) {
           method1(name);
      }
      method2(name);
      method3(name);
      method4(name);
   }

在我看来,将getter分配给变量并使用它有很大的内存/性能优势,因为Getters是Java方法并使用堆栈帧。编码方式真的有相当大的优势吗?     }

6 个答案:

答案 0 :(得分:15)

您最近描述 意见

<强>性能:

这可能是一个微观优化,在1999 - 2001年之前在1.2 JVM之前需要关注,即便如此,除非有一些严肃的数字显示,否则我会质疑它。

现代JIT实施会告诉您今天意见不合适。

现代编译器实现会进行各种优化,这使得在这样的事情上思考会浪费Java时间。 JIT只会让人更加担心。

<强>逻辑:

在并发情况下,如果要查看更改,则两个代码块在逻辑上不等同,使本地副本可以防止这种情况发生。根据您的想法,一种或另一种方法可能会产生非常微妙的非确定性错误,这些错误在更复杂的代码中很难确定。

特别是如果返回的内容是可变的,而不像String那是不可变的。然后,即使是本地副本也可能会发生变化,除非你做了深度克隆,并且很快就会很容易出错。

关注自己正确,然后衡量然后优化重要,只要它不会减少代码可维护性。

JVM将内联对final实例成员的任何调用,如果方法调用中除了return this.name;之外没有任何内容,则删除方法调用它知道访问器方法中没有逻辑,它知道引用是final所以它知道它可以内联值,因为它不会改变。

为此目的

person.getName() != null && person.getName().equalsIgnoreCase("Einstein") 

更正确地表达为

person != null && "Einstein".equalsIgnoreCase(person.getName())

因为没有NullPointerException

的机会

<强>重构:

现代IDE重构工具也删除了有关必须在一堆地方更改代码的任何争论。

答案 1 :(得分:5)

没有性能差异。如果有的话,它很小。

更重要的是,在存在多个线程的情况下,实际上的两个代码示例完全不同

说到中途,有人拨打setName并更改名称。现在你的代码正在经历一条完全意想不到的道路!这实际上是几个历史(甚至内核级)安全漏洞的根本原因。

请注意,我并不主张盲目地将所有结果复制到局部变量中。我只是指出了一个潜在的陷阱。

答案 2 :(得分:2)

如果你在Java中做某事的理由是因为“它可能会表现得更好”,那么你做错了。 JIT编译器将优化琐事public String getName(){return name;}。相反,你应该根据“这是正确的OOP方式来做决定”来做出决定。

从OOP的角度来看,只使用吸气剂。然后,子类可以根据需要覆盖该方法以执行其他奇特的操作(例如忽略名称“爱因斯坦”)。

答案 3 :(得分:2)

如果将值放在局部变量中,这肯定会更具可读性,这就是为什么你应该这样做而不是因为它表现更好。

另一个巨大的区别是,如果getName()有副作用(它不应该有,但你不能盲目地信任其他类),或者如果你在多线程系统中工作。在第一种情况下,差异是显而易见的,在第二种情况下,另一个线程可能会在您执行语句时更改值getName()返回。

在这两种情况下,您可能只需要调用getName()一次。

答案 4 :(得分:1)

我原则上同意预先优化是邪恶的,但我仍然更喜欢使用第二种形式。主要原因是它与其他情况其他一致,而不是吸气剂,反复调用它是愚蠢的,例如昂贵的操作:

if(person.expensiveOp() != null && person.expensiveOp().equalsIgnoreCase("Einstein")) {
       method1(person.expensiveOp());
  }
  method2(person.expensiveOp());
  method3(person.expensiveOp());
  method4(person.expensiveOp());

此外,通过语法高亮IDE,我可以隔离值的所有用法。有人会回复说你也可以突出显示该方法的所有用法。但是在对象的不同实例上可能会对同一方法进行多次调用(即toString()

答案 5 :(得分:0)

在询问问题并找到我要找的内容之后,我对这个主题进行了一些研究。问题在于,在构建问题时我没有使用正确的术语。我正在寻找'Inline Expansion'(编译器内联)。这是来自Wiki的引用:

  

在计算中,内联扩展内联是手动或编译器   用函数体取代函数调用站点的优化   被叫方。此优化可以改善运行时的时间和空间使用,   以增加计划最终规模的可能成本(即   二进制文件大小)。

我还发现此主题已在本网站上讨论过:

1)Do getters and setters impact performance in C++/D/Java?

以下是上述链接的引用:

  

在Java中,JIT编译器可能迟早会内联它。如   据我所知,JVM JIT编译器只优化了大量使用的代码,   所以你最初可以看到函数调用开销,直到    getter / setter已被充分调用

2)Inlining In Java