将方法声明为静态有什么好处

时间:2012-06-28 07:44:33

标签: java optimization coding-style static

我最近在Eclipse中查看了我的警告并遇到了这个警告:

static warning

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

[edit] Eclipse帮助中的确切报价,对私有和最终的压力:

  

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

是的我知道我可以关掉它,但我想知道开启它的原因?

为什么将每种方法都声称为静态会是一件好事?

这会带来任何性能优势吗? (在移动领域)

指出一个方法为静态,我想是显示你不使用任何实例变量因此可以移动到utils样式类?

在一天结束时,我应该将其关闭'忽略',还是应该修复它给我的100多个警告?

您是否认为这只是 dirty 代码的额外关键字,因为编译器无论如何只会内联这些方法? (有点像你没有声明每个变量你可以最终但你可以)。

9 个答案:

答案 0 :(得分:129)

每当您编写方法时,您都会在给定范围内履行合同。范围越窄,您编写错误的可能性就越小。

当方法是静态的时,您无法访问非静态成员;因此,你的范围更窄。因此,如果您不需要 并且永远不需要(甚至在子类中) 非静态成员来履行您的合同,为什么要为您的方法提供对这些字段的访问权限?在这种情况下声明方法static将让编译器检查您是否使用了不打算使用的成员。

而且,它将帮助阅读代码的人了解合同的性质。

这就是为什么在实际实现静态契约时声明方法static被认为是好的。

在某些情况下,您的方法仅表示与您的类的实例相关的内容,并且它的实现实际上并不使用任何非静态字段或实例。在这种情况下,您不会标记方法static

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

  • 一个不做任何事情的扩展挂钩(但可以对子类中的实例数据做某事)
  • 一个非常简单的默认行为,可以在子类中自定义。
  • 事件处理程序实现:实现因事件处理程序的类而异,但不会使用事件处理程序实例的任何属性。

答案 1 :(得分:13)

这里没有优化的概念。

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

  

启用后,编译器将对private或final方法发出错误或警告,并且仅引用静态成员。

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

答案 2 :(得分:6)

我没有关于性能的信息,我认为它最多只是稍微好一点,因为代码不需要根据类型进行动态调度。

然而,反对重构为静态方法的更强有力的论据是,目前使用静态被认为是不好的做法。静态方法/变量不能很好地集成到面向对象的语言中,并且难以正确测试。这就是为什么一些较新的语言完全放弃静态方法/变量的概念,或者尝试将其内化到语言中以更好地利用OO(例如Scala中的对象)。

大多数情况下,你需要静态方法来实现只使用参数作为输入并使用它生成输出的函数(例如,实用程序/辅助函数)在现代语言中,有一个第一类函数概念允许,所以不需要静电。 Java 8将集成lambda表达式,因此我们已经朝着这个方向发展。

答案 3 :(得分:3)

1。声明方法static会带来轻微的性能优势,但更有用的是,它允许在没有手头的对象实例的情况下使用它(例如,考虑工厂方法或得到一个单身人士)。它还用于说明方法性质的文档目的。这个文档化的目的不应该被忽略,因为它会立即向代码的读者和API的用户提供有关方法性质的暗示,并且还可以作为原始程序员思考的工具 - 明确指出有意义的帮助你也直接思考并生成质量更好的代码(我认为根据我的个人经验,但人们不同)。例如,它是合乎逻辑的,因此需要区分对类型进行操作的方法和作用于类型实例的方法(如Jon Skeet in his comment to a C# question所指出的)。

static方法的另一个用例是模仿过程编程接口。想想java.lang.System.println()类及其中的方法和属性。类java.lang.System类似于分组名称空间而不是可实例化对象。

2. Eclipse(或任何其他编程或其他类型的 - 生物可组合或非生物可组合的 - 实体)如何确定哪种方法可以声明为静态?即使基类没有访问实例变量或调用非静态方法,通过继承机制也可以改变。只有通过继承子类不能覆盖该方法,我们才能100%确定该方法确实可以声明static。在

的两种情况下,完全不可能覆盖方法
  1. private(没有子类可以直接使用它,甚至原则上也不知道它),或者
  2. final(即使子类可以访问,也无法更改方法以引用实例数据或函数)。
  3. 因此Eclipse选项的逻辑。

    3。原始海报也问:“指出一个方法为静态,我想是显示你不使用任何实例变量因此可以转移到utils样式上课?“这是一个非常好的观点。有时这种设计变更由警告表示。

    这是一个非常有用的选项,我个人一定要确保启用,我是否使用Eclipse,我是用Java编程的。

答案 4 :(得分:1)

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

您还询问了表现:

可能会有很小的性能提升,因为调用静态方法 不需要隐含的“this”引用作为参数。

然而,这种性能影响非常小。因此,这完全取决于范围。

答案 5 :(得分:1)

根据Android效果指南:

  

首选静态虚拟如果您不需要访问对象   字段,使您的方法静态。调用将约为15%-20%   快点。这也是很好的做法,因为你可以从方法中看出来   调用方法的签名不能改变对象的状态。

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

答案 6 :(得分:0)

嗯,Eclipse文档说明了有问题的警告:

  

方法可以是静态的

     

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

我认为它几乎说明了一切。如果该方法是私有的并且是最终的并且仅引用静态成员,那么所讨论的方法也可以被声明为静态,并且由此明确表示我们只打算从中访问静态内容。

老实说,我认为背后还有其他任何神秘的原因。

答案 7 :(得分:0)

我错过了速度差异的一些数字。所以我尝试对它们进行基准测试,结果并不那么容易:Java loop gets slower after some runs / JIT's fault?

我终于使用了Caliper,结果与手动运行测试相同:

静态/动态调用没有明显差异。至少不适用于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

Caliper Test课程是:

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;
    }

}

我自己的测试课是:

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;
    }

}

答案 8 :(得分:-2)

您可以声明为static的方法是不需要实例化的方法,例如

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

然后你可以在没有实例化该类的情况下在任何其他类中调出。

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

......但那可能是你已经知道的。除了更清楚地表明该方法不使用任何实例变量之外,它本身并没有给你带来任何实际好处。

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