如果私有帮助方法可以是静态的,那么它们应该是静态的

ava*_*lys 197 java methods static static-methods

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

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) {... } 
} 
Run Code Online (Sandbox Code Playgroud)

有没有特别的理由指定computeOnecomputeMore作为静态方法 - 或任何特殊原因不?

将它们保持为非静态是最容易的,即使它们肯定是静态的而不会引起任何问题.

Esk*_*ola 169

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

  • 我最喜欢的一个帖子.我正在使用NetBeans,它显示了使用斜体字体的静态方法调用. (2认同)
  • 我通常将这些辅助方法声明为`protected static`,这使得它们在同一个包中的测试类中非常容易测试. (2认同)
  • @James或只是`静态`(如果我们只是想让它进行测试). (2认同)
  • "不会修改对象的状态" - 完美解释如何将其与私有方法区分开来. (2认同)

Mic*_*ers 108

它可能会导致字节码略小,因为静态方法无法访问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);
    }
}
Run Code Online (Sandbox Code Playgroud)

字节码(检索到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

}
Run Code Online (Sandbox Code Playgroud)

调用静态方法需要两个字节码(byteops?):( iconst_0对于参数)和invokestatic.
调用非静态方法需要三个:( aload_1对于TestBytecodeSize对象,我猜),iconst_0(对于参数),和invokespecial.(请注意,如果这些不是私有方法,那么它将invokevirtual取而代之invokespecial;请参阅JLS§7.7调用方法.)

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

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

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

总而言之......我想我仍然坚持我之前所说的话.速度不应该是选择其中之一的原因,因为它最多只是微观优化.

  • 在进行任何微观优化之前,您应该考虑热点编译器的影响.底线:编写代码以供人类阅读,并将优化留给编译器 (6认同)
  • 这就是为什么我说"我不认为它在速度方面有任何不同(如果确实如此,它可能太小而不能总体上有所不同)." (3认同)
  • JIT编译器无论如何都会内联并优化它们,因此字节码指令的数量与它的速度几乎没有关系. (2认同)

Ste*_* B. 18

我个人的偏好是宣布它们是静态的,因为它是一个明确的标志,他们是无国籍的.


Pow*_*ord 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 */ } 
}
Run Code Online (Sandbox Code Playgroud)

  • +1:即使示例很糟糕,太多可能是静态的方法也是一种糟糕的代码味道.顺便说一句,静态方法实际上是功能,它们根本不是OO.它们可能属于另一个类中的方法,或者您可能缺少此函数可以作为方法所属的类. (5认同)

oxb*_*kes 11

您可能想要声明静态辅助方法的一个原因是,您需要在"之前" this或之前的类构造函数中调用它们super.例如:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Axe*_*ler 10

我无法真正想到私有静态方法的明显优势.话虽如此,使它们非静态也没有特别的优势.这主要是一个介绍问题:你可能想让它们变得静态,以明确强调它们不会改变一个对象.

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

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

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

  • Clint是一个私有方法,因此您无法覆盖该方法. (7认同)
  • 不使它静态的优点是有人可以子类化并覆盖方法的行为. (4认同)

小智 7

正确的答案是:

任何不从字段中获取任何信息且不将任何信息放入字段的方法都不一定是实例方法.任何不使用或改变其类或对象中的任何字段的方法也可能是静态方法.


Kip*_*Kip 5

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

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

public class Example {
   //...

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

   //...
}
Run Code Online (Sandbox Code Playgroud)


Osc*_*Ryz 5

或任何特殊原因不[声明它们是静态的]?

是.

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

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

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

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

所以,我的建议是将它们作为实例方法,并尽可能避免静态.
充分利用语言提供的活力.

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

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

  • 我们正在讨论*private*helpers,这意味着它们不属于该类的公共API.这意味着什么都不会阻止您以后提供不同的实现,使它们非静态,或进行任何其他更改. (22认同)
  • 完全不同意.没有很好的理由你想要在不改变定义它的类的情况下更改私有方法.做你建议的唯一方法是通过字节码操作. (2认同)
  • 如果方法不是私有的话,你的建议可能有意义,但即便如此,我建议你根据当前需要进行设计,而不是根据想象的需要进行设计. (2认同)

Jon*_*ott 5

关于使用静态方法的一个问题是它可以使对象在单元测试中更难以使用.Mockito无法为静态方法创建模拟,也无法创建方法的子类实现.

  • 您不应该为私有方法编写测试。请参阅[此处](https://lostechies.com/chadmyers/2008/11/21/do-not-test-private-methods/)。无需测试私有方法是否是静态的。 (2认同)