私人帮手方法应该是静态的,如果他们可以是静态的

比方说,我有一个类被devise来实例化。 我有几个私人的“帮手”方法在类中,不需要访问任何类成员,并完全操作自己的参数,返回结果。

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作为静态方法 – 或任何特定的原因不?

将它们保持为非静态是最容易的,尽pipe它们当然可以是静态的而不会造成任何问题。

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

它可能会导致稍小的字节码,因为静态方法将无法访问this 。 我不认为这在速度上有什么区别(如果是这样的话,它可能会太小而不能在整体上做出改变)。

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


编辑:这个答案不断downvoted,可能是因为字节码大小的未经证实的断言。 所以我会实际运行一个testing。

 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 } 

调用静态方法需要两个字节码(字节?): iconst_0 (用于参数)和invokestatic
调用非静态方法有三个: aload_1 (对于aload_1对象,我想), iconst_0 (对于参数)和invokespecial 。 (请注意,如果这些方法不是私有方法,那么它将是invokespecial而不是invokespecial ;请参阅JLS第7.7节“调用方法” 。

现在,正如我所说,除了invokestatic需要less一个字节码之外,我不认为这两者之间的性能会有很大差异。 invokestaticinvokespecial应该比invokespecial稍微快一些,因为它们都使用静态绑定而不是dynamic的,但我不知道是否比另一个更快。 我也找不到任何好的参考。 我能find的最接近的是1997年的JavaWorld文章 ,它基本上重申了我刚才所说的:

最快的指令最有可能是invokespecialinvokestatic ,因为这些指令调用的方法是静态绑定的。 当JVMparsing这些指令的符号引用并用直接引用replace它时,该直接引用可能会包含指向实际字节码的指针。

但自一九九七年以来,很多事情已经改变

所以最后…我想我仍然坚持我之前说的话。 速度不应该是select其中一个的原因,因为它最好是一个微型优化。

答案是…这取决于。

如果member是一个特定于你正在处理的对象的实例variables,那么为什么把它作为parameter passing呢?

例如:

 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 */ } } 

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

你可能想要声明静态帮助器方法的一个原因是,如果你需要在类的构造函数“ 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)); } } 

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

我不能真正想到私有静态方法的明显优势。 也就是说,让它们变成非静态也没有什么特别的好处。 这主要是一个介绍的问题:你可能想要使它们是静态的,以清楚地强调它们不改变对象的事实。

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

  • 可以调用静态方法而不创build对象的实例,这可能是有用的
  • 静态方法不能被inheritance,如果你需要多态性可能是一个问题(但与私有方法无关)。

除此之外,差别是相当小的,我强烈怀疑,这个额外的指针传递给实例方法有很大的不同。

正确的答案是:

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

或者有什么特别的理由不宣称它们是静态的?

是。

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

这可能听起来很愚蠢(事实上,如果这些方法只能由你在50行程序中使用),但在较大的应用程序或其他人使用的库中,你可能决定select一个更好的实现,但不要想要打破现有的代码。

所以你创build一个子类并在新版本中返回它,并且由于这些方法被声明为实例方法,所以你可以让多态性完成它的工作。

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

所以,我的build议是保持它们作为实例方法,并尽可能避免静态。
利用语言提供的活力。

看到这里有一些相关的video: 如何devise一个好的API以及为什么它很重要

虽然它与“静态与实例”方法的讨论没有直接关系,但它涉及到APIdevise中的一些有趣点。

如果方法基本上只是一个永远不会预见到使用状态信息的子例程,那么声明它是静态的。

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

 public class Example { //... //Only possible if computeOne is static public final static double COMPUTED_ONE = computeOne(new Something("1")); //... } 

在这种情况下,我的首选是使computeOnecomputeMore静态方法。 原因:封装。 可以访问你的课程的代码越less越好。

在你给出的例子中,你声明computeOnecomputeMore不需要访问类的内部,所以为什么让类的维护者有机会干涉内部。

我想澄清一些其他海报人士所说的错误信息。

首先,既然方法是私人的,即使你声明它们是静态的,你也不能在这个类之外访问它们。 其次它们是私人的,所以你甚至不能在子类中重写,所以静态或非静态没有任何区别。 第三,一个非静态的私有方法也可以从类的构造函数中调用,它不需要是静态的。

现在来谈谈你的问题,如果私人帮手方法应该被定义为静态或非静态。 我会用史蒂夫的答案作为标记一个私有方法静态显示,这种方法是无状态的,因为我也遵循这个规则,当我的代码。

有一个关于静态方法的问题是它可能使对象在unit testing中更难使用。 Mockito不能为静态方法创build模拟,也不能创build方法的子类实现。

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

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

如果你处在这个困境中,在我看来你已经拥有了在对象之外的代码中所需要的所有数据。 这是你想要的吗? 每次总是将这些数据收集到一个对象中会更容易吗? 您可能对承诺使用单个模型感到矛盾。 如果你可以用一种方法做到这一点,然后select静态或非静态,并与之一起去。

更具体地说,你给出的例子看起来,定义这些方法的目的,更多的是为了读取代码而不是function(它们定义为私有)。 在这种情况下,使用静态对你来说确实没有任何作用,因为静态的目的是暴露类的function。

从经验来看,我会说这样的私人方法往往是相当普遍和可重用的。

我认为要做的第一件事就是询问这个方法在当前类的上下文之外是否有用。 如果是这样,我会做每个人都build议,并提取这种方法静态的某些使用类,有人希望检查前执行新方法做同样的事情。

这种普遍使用的私有方法是项目中代码重复的主要部分,因为每个开发人员都独立地将它们重新创build到需要使用它的地方。 所以这些方法的集中化是一条路。

其中一个原因是,在其他条件相同的情况下,静态方法调用应该更快。 静态方法不能是虚拟的,不要隐含这个引用。

离题:我会保持辅助方法在一个独立的实用程序/助手类中只有静态方法。

在使用时使用辅助方法的麻烦(读取“同一类”)是某人在线下可能只是select在同一个地方发布自己不相关的辅助方法

 class Whatever { public static varType myVar = initializeClassVariable(); private static varType initializeClassVariable() { //initialization code goes here } } 

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

  1. 如果没有静态修饰符,那么当您(重新)编写该方法时,您无法弄清楚该方法是无状态的,无需进行额外的分析。

  2. 然后,“静态”修饰符可以给你重构的想法,除了别人可能会发现无用的其他事情。 例如,将该方法移动到某个Utility类或将其转换为成员方法..

我会宣布它们是静态的,将它们标记为无状态。

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

正如很多人所说,让它成为一个静态的 ! 这里是我遵循的拇指规则:如果你认为这个方法只是一个math函数,也就是说它是无状态的,不涉及任何实例variables(=>在方法中没有蓝色variables),并且该方法对于“n”个调用(具有相同的参数,当然)是相同的,然后将该方法标记为STATIC。

如果你认为这个方法对其他类是有用的,那么把它移到一个Util类,否则把这个方法放在同一个类中。 (最小化可访问性)