214

假设我有一个设计为实例化的类。我在类中有几个私有的“帮助器”方法,它们不需要访问任何类成员,并且只对它们的参数进行操作,返回结果。

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

是否有任何特殊的理由来指定computeOnecomputeMore作为静态方法 - 或者有任何特殊的理由不这样做?

将它们保留为非静态当然是最容易的,即使它们当然可以是静态的而不会引起任何问题。

4

21 回答 21

177

我更喜欢这样的辅助方法private static;这将使读者清楚他们不会修改对象的状态。我的 IDE 还会以斜体显示对静态方法的调用,因此我无需查看签名即可知道该方法是静态的。

于 2009-02-11T21:33:45.087 回答
112

由于静态方法无法访问this. 我认为它对速度没有任何影响(如果有的话,它可能太小而无法整体产生影响)。

我会让它们成为静态的,因为如果可能的话,我通常会这样做。但这只是我。


编辑:这个答案一直被低估,可能是因为关于字节码大小的未经证实的断言。所以我会实际运行一个测试。

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

字节码(用 检索javap -c -private TestBytecodeSize):

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

调用静态方法需要两个字节码(byteops?):(iconst_0用于参数)和invokestatic.
调用非静态方法需要三个:(aload_1对于TestBytecodeSize对象,我想)、iconst_0(对于参数)和invokespecial. (请注意,如果这些不是私有方法,它会invokevirtual代替invokespecial;请参阅JLS §7.7 Invoking Methods。)

invokestatic现在,正如我所说,除了需要少一个字节码的事实之外,我不认为这两者之间的性能会有很大差异。invokestatic并且invokespecial都应该比 快一点invokevirtual,因为它们都使用静态绑定而不是动态绑定,但我不知道其中一个是否比另一个快。我也找不到任何好的参考资料。我能找到的最接近的是这篇 1997 JavaWorld article,它基本上重申了我刚才所说的内容:

最快的指令很可能是invokespecialand invokestatic,因为这些指令调用的方法是静态绑定的。当 JVM 解析这些指令的符号引用并将其替换为直接引用时,该直接引用可能包含指向实际字节码的指针。

但自 1997 年以来,许多事情都发生了变化。

所以总而言之......我想我仍然坚持我之前所说的。速度不应该是选择一个而不是另一个的理由,因为它充其量只是一个微优化。

于 2009-02-11T21:32:53.607 回答
18

我个人的偏好是将它们声明为静态,因为它们是无国籍的明确标志。

于 2009-02-11T21:30:56.683 回答
18

答案是……这取决于。

如果 member 是特定于您正在处理的对象的实例变量,那么为什么将它作为参数传递呢?

例如:

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}
于 2009-02-11T21:55:55.627 回答
11

您可能想要声明静态辅助方法的一个原因是您是否需要在类构造函数“之前”thissuper. 例如:

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

这是一个人为的例子,但recoverInt在这种情况下显然不能成为实例方法。

于 2009-02-11T23:55:07.103 回答
10

我真的想不出私有静态方法的明显优势。话虽如此,使它们成为非静态也没有特定的优势。这主要是一个表示问题:您可能希望使它们成为静态以清楚地强调它们没有改变对象的事实。

对于具有不同访问权限的方法,我认为有两个主要论点:

  • 可以在不创建对象实例的情况下调用静态方法,这很有用
  • 静态方法不能被继承,如果您需要多态(但与私有方法无关),这可能是个问题。

除此之外,差异非常小,我强烈怀疑传递给实例方法的额外 this 指针是否会产生显着差异。

于 2009-02-11T21:35:00.107 回答
8

正确答案是:

任何不从字段中获取任何信息并且不将任何信息放入字段中的方法不必是实例方法。任何不使用或更改其类或对象中的任何字段的方法也可能是静态方法。

于 2011-09-05T13:00:24.290 回答
6

或者有什么特别的理由不[将它们声明为静态的]?

是的。

通过将它们保留为实例方法,您可以稍后提供不同的实现。

这听起来可能很愚蠢(实际上,如果这些方法仅由您在 50 行程序中使用),但在更大的应用程序或其他人使用的库中,您可能会决定选择更好的实现,但不要想要破坏现有的代码。

因此,您创建一个子类并在新版本中返回它,并且由于这些方法被声明为实例方法,您只需让多态性完成它的工作。

此外,出于同样的原因,您可以从将构造函数设为私有并提供静态工厂方法中受益。

因此,我的建议是将它们保留为实例方法,并尽可能避免使用静态方法。
利用语言提供的活力。

看这里有一些相关的视频:如何设计一个好的 API 及其重要性

虽然它与“静态 vs 实例”方法的讨论没有直接关系,但它触及了 API 设计中的一些有趣点。

于 2009-02-12T00:28:18.333 回答
5

如果该方法基本上只是一个永远不会使用状态信息的子例程,则将其声明为静态。

这允许它在其他静态方法或类初始化中使用,即:

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}
于 2009-02-11T21:43:30.913 回答
5

使用静态方法的一个问题是它会使对象在单元测试中更难使用。Mockito 不能为静态方法创建模拟,并且您不能创建该方法的子类实现。

于 2014-04-25T14:02:26.443 回答
3

静态/非静态问题归结为“我真的需要使用此类的对象”吗?

那么,您是否在不同方法之间传递对象?对象是否包含在静态方法上下文之外有用的信息?如果您将两种方式都使用,是否有任何理由不定义两种方式?

如果您处于这种两难境地,在我看来,您拥有该方法所需的所有数据,这些数据都在对象之外的代码中浮动。这是你想要的吗?每次都将数据收集到一个对象中会更容易吗?您可能只是对致力于单一模型感到矛盾。如果您可以使用一种方式完成所有操作,请选择静态或非静态并继续使用。

于 2009-02-11T21:34:33.977 回答
3

在这种情况下,我的偏好是 makecomputeOnecomputeMorestatic 方法。原因:封装。可以访问您的类的实现的代码越少越好。

在您给出的示例中,您声明computeOne并且computeMore不需要访问类的内部,那么为什么要让类的维护者有机会干预内部。

于 2009-02-11T21:48:08.407 回答
3

我想澄清其他发帖人所说的一些事情,因为它提供了错误的信息。

首先,由于这些方法是私有的,即使您将它们声明为静态,您也无法在此类之外访问它们。其次,它们是私有的,因此您甚至不能在子类中覆盖,因此静态或非静态没有任何区别。第三,也可以从类的构造函数中调用非静态私有方法,它不必是静态的。

现在来回答您的问题,如果应将私有辅助方法定义为静态或非静态。我将使用史蒂夫的回答,因为将私有方法标记为静态表明此方法是无状态的,因为我在编码时也遵循此规则。

于 2009-02-12T04:20:31.397 回答
3

根据经验,我会说这种私有方法往往是相当普遍和可重用的。

我认为首先要做的是询问该方法在当前类上下文之外是否有用。如果是这样,我会完全按照每个人的建议去做,并将这个方法作为静态方法提取到某个 utils 类中,希望有人在执行完全相同的事情之前检查它。

这种通用的私有方法是项目中大部分代码重复的来源,因为每个开发人员都在她需要使用的地方独立地重新发明它们。因此,这些方法的集中化是一条可行的路。

于 2012-11-07T07:10:47.420 回答
2

更具体地说,对于您给出的示例,定义这些方法的目的似乎是在您阅读它时更清楚代码而不是功能(它们定义为私有)。在这种情况下,使用 static 真的对您没有任何帮助,因为 static 的目的是公开类功能。

于 2009-02-11T21:39:30.143 回答
1

一个原因是,在其他条件相同的情况下,静态方法调用应该更快。静态方法不能是虚拟的,并且不采用隐式 this 引用。

于 2009-02-23T14:35:38.290 回答
1

正如很多人所说,让它成为一个静态的!这是我遵循的经验法则:如果您认为该方法只是一个数学函数,即它是无状态的,不涉及任何实例变量(=> 方法中没有蓝色变量 [in eclipse]),结果对于“n”次调用,该方法将是相同的(当然,使用相同的参数),然后将该方法标记为 STATIC。

如果您认为此方法对其他类有用,则将其移至 Util 类,否则,将该方法作为私有方法放在同一类中。(最小化可访问性)

于 2009-11-25T05:57:31.343 回答
1

题外话:我会将辅助方法保留在一个独立的实用程序/辅助类中,其中只有静态方法。

在使用时使用辅助方法(阅读“同一类”)的问题在于,下线的人可能只是选择在同一个地方发布他们自己不相关的辅助方法

于 2009-11-25T06:29:42.587 回答
1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

私有静态方法的优点是,如果您需要重新初始化类变量,它们可以在以后重用。

于 2011-08-05T06:47:18.767 回答
1
  1. 如果没有静态修饰符,您将无法确定该方法是无状态的,而无需额外分析,这在您(重新)编写该方法时可以轻松完成。

  2. 然后“静态”修饰符可能会为您提供有关重构的想法,以及其他人可能认为无用的其他内容。例如将方法移动到某个实用程序类或将其转换为成员方法..

于 2012-10-25T07:07:55.030 回答
0

我会将它们声明为静态以将它们标记为无状态。

Java 对于不导出的小操作没有更好的机制,所以我认为私有静态是可以接受的。

于 2009-02-11T21:51:38.897 回答