将方法声明为静态的好处是什么?

我最近一直在浏览Eclipse中的警告,并碰到这个:

静态警告

如果该方法可以被声明为静态,它将给出编译器警告。

在Eclipse帮助中的精确引用,强调私有和最终:

启用后,编译器将针对私有最终方法以及仅引用静态成员的方法发出错误或警告。

是的,我知道我可以closures它,但我想知道打开它的原因?

为什么将每种可能的方法都声明为静态是一件好事?

这会给予任何性能好处吗? (在移动领域)

指出一个方法是静态的,我想表明你不使用任何实例variables,因此可以移动到一个实用程序样式类?

在一天结束的时候,我应该closures“忽略”还是应该修复它给我的100多个警告?

你认为这只是额外的关键字, 肮脏的代码,因为编译器将只是内联这些方法吗? (有点像你不声明每个variables,你可以最后, 但你可以 )。

每当你写一个方法,你就在一个给定的范围内履行合同。 范围越窄,你写错误的几率就越小。

当一个方法是静态的,你不能访问非静态成员; 因此,您的范围更窄。 所以,如果你不需要也不会需要(甚至在子类中)非静态成员来履行你的合同,为什么要给你的方法访问这些领域? 在这种情况下声明static方法会让编译器检查你不使用你不想使用的成员。

此外,它将帮助人们阅读你的代码,理解合同的性质。

这就是为什么在实际实现静态合同时声明一个static方法是好的。

在某些情况下,你的方法只意味着相对于你的类的一个实例的东西,而且它的实现恰好不会实际使用任何非静态的字段或实例。 在这种情况下,您不会将该方法标记为static

您不使用static关键字的示例:

  • 一个什么都不做的扩展钩子(但是可以在子类中做实例数据)
  • 一个非常简单的默认行为意味着可以在一个子类中定制。
  • 事件处理程序实现:实现将随事件处理程序的类而变化,但不会使用事件处理程序实例的任何属性。

这里没有优化的概念。

一个static方法是static因为你明确地声明该方法不依赖任何实例封闭的类,只是因为它不需要。 所以Eclipse的警告,正如文档中所述:

启用后,编译器将针对私有或最终方法以及仅引用静态成员的方法发出错误或警告。

如果你不需要任何实例variables,并且你的方法是私有的(不能从外部调用)或final(不能被重写),那么没有理由让它成为一个普通的方法,而是一个静态的方法。 一个静态方法本质上是更安全的,即使只是因为你可以做更less的事情(它不需要任何实例,你没有任何隐含的this对象)。

我没有关于性能的信息,我认为它最好稍微好一点,因为代码不需要根据types进行dynamic调度。

然而,反对重构成静态方法的一个更强大的论据是,目前使用静态被认为是不好的做法。 静态方法/variables不能很好地整合到面向对象的语言中,而且很难正确testing。 这就是为什么一些较新的语言完全放弃了静态方法/variables的概念,或者尝试将其内在化到语言中,以便与OO(例如Scala中的Objects)更好地发挥作用。

大多数情况下,你需要静态方法来实现只使用参数作为input的函数,并使用该函数产生输出(例如效用/助手函数)在现代语言中,有一个第一类函数的概念,允许静态是不需要的。 Java 8将集成lambdaexpression式,所以我们正在朝着这个方向前进。

1. static声明方法给予了轻微的性能好处,但是更有用的是,它允许在没有对象实例的情况下使用它(例如考虑工厂方法或获取单例)。 它也用来说明方法的性质。 这个文档化的目的不应该被忽略,因为它直接提示API的读者和API用户的方法的本质,也可以作为思考原始程序员的工具 – 明确意图的含义你也可以直接思考,生成更高质量的代码(我认为基于我的个人经验,但人们是不同的)。 例如,区分使用types的方法和作用于types实例的方法(正如Jon Skeet在他对C#问题的评论中指出的那样)是合乎逻辑的,因此是合乎需要的。

static方法的另一个用例是模拟程序编程接口。 考虑java.lang.System.println()类及其中的方法和属性。 类java.lang.System被用作分组命名空间而不是可实例化的对象。

2. Eclipse(或任何其他程序或其他types的生物兼容性或非生物兼容性实体)如何确切知道哪种方法可以被声明为静态? 即使基类不访问实例variables或调用非静态方法,但通过inheritance机制,事情可以改变。 只有当方法不能被inheritance的子类覆盖时,我们才能100%肯定地声明方法确实可以被声明为static 。 在两种情况下,重写一个方法是不可能的

  1. private (没有子类可以直接使用它,甚至在原则上不知道它),或
  2. final (即使可以通过子类访问,也没有办法改变引用实例数据或函数的方法)。

因此Eclipse选项的逻辑。

3.原来的海报还问:“ 指出一个方法是静态的,我想表明你没有使用任何实例variables,因此可以移动到一个utils风格的类? ”这是一个很好的观点。 有时这种devise变化是由警告指出的。

这是一个非常有用的select,我将亲自确保启用,我是否使用Eclipse,并且我是用Java编程的。

关于方法的范围如何变化,请参阅Samuel的答案。 我想,这是使静态方法的主要方面。

你也问过关于performance:

可能会有一个微小的性能增益,因为对静态方法的调用不需要隐含的“this”引用作为参数。

但是,这种性能影响非常小。 因此,这只是范围。

那么,Eclipse文档中提到了这个警告:

方法可以是静态的

启用后,编译器将针对私有或最终方法以及仅引用静态成员的方法发出错误或警告

我认为这几乎是全部的。 如果方法是私有的并且是最终的,并且只涉及静态成员,那么所讨论的方法可能也被声明为静态的,由此明白,我们只打算从其中访问静态内容。

我真的不认为有任何其他神秘的原因背后。

从Android性能指南:

将静态优先于虚拟如果您不需要访问对象的字段,请将您的方法设为静态。 调用速度将快15%-20%。 这也是一个好习惯,因为从方法签名中可以看出,调用方法不能改变对象的状态。

http://developer.android.com/training/articles/perf-tips.html#PreferStatic

我错过了一些速度差异的数字。 所以我试图对它们进行基准testing,结果并不那么容易: 在一些运行/ JIT的错误之后,Java循环变慢了?

我终于使用了Caliper,结果和手动运行我的testing一样:

静态/dynamic调用没有可衡量的差异。 至less不适用于Linux / AMD64 / Java7。

Caliper结果在这里: https ://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenario.vmSpec.options 。 CMSLargeSplitSurplusPercent,scenario.vmSpec.options.CMSSmallCoalSurplusPercent,scenario.vmSpec.options.CMSSmallSplitSurplusPercent,scenario.vmSpec.options.FLSLargestBlockCoalesceProximity,scenario.vmSpec.options.G1ConcMarkStepDurationMillis

我自己的结果是:

 Static: 352 ms Dynamic: 353 ms Static: 348 ms Dynamic: 349 ms Static: 349 ms Dynamic: 348 ms Static: 349 ms Dynamic: 344 ms 

卡尺testing课是:

 public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark { public static void main( String [] args ){ CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args ); } public int timeAddDynamic( long reps ){ int r=0; for( int i = 0; i < reps; i++ ) { r |= addDynamic( 1, i ); } return r; } public int timeAddStatic( long reps ){ int r=0; for( int i = 0; i < reps; i++ ) { r |= addStatic( 1, i ); } return r; } public int addDynamic( int a, int b ){ return a+b; } private static int addStatic( int a, int b ){ return a+b; } } 

我自己的testing课是:

 public class TestPerformanceOfStaticVsDynamicCalls { private static final int RUNS = 1_000_000_000; public static void main( String [] args ) throws Exception{ new TestPerformanceOfStaticVsDynamicCalls().run(); } private void run(){ int r=0; long start, end; for( int loop = 0; loop<10; loop++ ){ // Benchmark start = System.currentTimeMillis(); for( int i = 0; i < RUNS; i++ ) { r += addStatic( 1, i ); } end = System.currentTimeMillis(); System.out.println( "Static: " + ( end - start ) + " ms" ); start = System.currentTimeMillis(); for( int i = 0; i < RUNS; i++ ) { r += addDynamic( 1, i ); } end = System.currentTimeMillis(); System.out.println( "Dynamic: " + ( end - start ) + " ms" ); // Do something with r to keep compiler happy System.out.println( r ); } } private int addDynamic( int a, int b ){ return a+b; } private static int addStatic( int a, int b ){ return a+b; } } 

你可以声明为静态的方法是那些不需要实例化的方法,比如

 public class MyClass { public static string InvertText(string text) { return text.Invert(); } } 

然后,你可以在任何其他class级召唤出来,而不需要那个class级的实例化。

 public class MyClassTwo { public void DoSomething() { var text = "hello world"; Console.Write(MyClass.InvertText(text)); } } 

…但是你可能已经知道了 它本身并没有给你带来任何真正的好处,除了更清楚地表明这个方法不使用任何实例variables。

换句话说,你可以最安全地把它完全closures。 如果你知道你永远不会在其他类中使用方法(在这种情况下它应该是私有的),你根本不需要它是静态的。