Java方法应该默认是静态的吗?

假设你在类A中写入方法foo() ,foo不会访问任何A的状态。 你什么都不知道foo做什么,或者它如何performance。 它可以做任何事情。

无论有什么其他的考虑,foo总是静止的吗? 为什么不?

看来我的class级总是积累很多私人帮手的方法,因为我把任务分解并应用唯一写一次原则。 这些大多数不依赖于对象的状态,但在类自身的方法之外决不会有用。 他们应该默认静态? 结束了大量的内部静态方法是错误的吗?

要回答标题上的问题,一般来说,Java方法默认情况下应该是静态的。 Java是一种面向对象的语言。

不过,你所说的有点不一样。 你特别谈论帮手方法。

在只需要将值作为参数并返回一个值而不访问状态的辅助方法的情况下,它们应该是静态的 。 私人和静态。 让我强调一下:

不访问状态的辅助方法应该是静态的。


1.主要优点:代码更具performance力。

使这些方法成为静态的至less有一个主要优点:在代码中完全明确地表示该方法不需要知道任何实例状态

代码自身说明。 对于其他读取你的代码的人来说,事情会变得更加明显,甚至在将来的某个时刻对你也是如此。

2.另一个优点:代码可以更简单地推理。

如果确保方法不依赖于外部或全局状态,那么它是一个纯函数 ,即math意义上的一个函数:对于相同的input,您可以确定总是获得相同的输出。

3.优化优势

如果这个方法是静态的,并且是一个纯函数,那么在某些情况下,它可以被记忆以获得一些性能增益(改变使用更多的内存)。

4.字节码级差异

在字节码级别,如果将辅助方法声明为实例方法或静态方法,则会获得两个完全不同的内容。

为了使本节更易于理解,我们使用一个例子:

 public class App { public static void main(String[] args) { WithoutStaticMethods without = new WithoutStaticMethods(); without.setValue(1); without.calculate(); WithStaticMethods with = new WithStaticMethods(); with.setValue(1); with.calculate(); } } class WithoutStaticMethods { private int value; private int helper(int a, int b) { return a * b + 1; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public int calculate() { return helper(value, 2 * value); } } class WithStaticMethods { private int value; private static int helper(int a, int b) { return a * b + 1; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public int calculate() { return helper(value, 2 * value); } } 

我们感兴趣的是对WithoutStaticMethodsWithStaticMethods类的helper(...)的调用。

没有静态方法

在第一种情况下,如果没有静态方法,当您调用helper方法时,JVM需要将引用推送到实例,以将其传递到invokespecial 。 看一下calculate()方法的代码:

  0 aload_0 1 aload_0 2 getfield #2 <app/WithoutStaticMethods.value> 5 iconst_2 6 aload_0 7 getfield #2 <app/WithoutStaticMethods.value> 10 imul 11 invokespecial #3 <app/WithoutStaticMethods.helper> 14 ireturn 

0(或1)处的指令aload_0会将引用加载到堆栈上的实例,稍后将由invokespecial 。 这个指令将把这个值作为helper(...)函数的第一个参数,并且从不使用,正如我们在这里看到的:

 0 iload_1 1 iload_2 2 imul 3 iconst_1 4 iadd 5 ireturn 

看看有没有iload_0 ? 它已被不必要地加载。

用静态方法

现在,如果声明了辅助方法static,那么calculate()方法将如下所示:

  0 aload_0 1 getfield #2 <app/WithStaticMethods.value> 4 iconst_2 5 aload_0 6 getfield #2 <app/WithStaticMethods.value> 9 imul 10 invokestatic #3 <app/WithStaticMethods.helper> 13 ireturn 

差异是:

  • 有一个更less的aload_0指令
  • 现在使用invokestatic调用helper方法

那么,辅助函数的代码也有点不同:没有this作为第一个参数,所以参数实际上在位置0和1,我们可以看到:

 0 iload_0 1 iload_1 2 imul 3 iconst_1 4 iadd 5 ireturn 

结论

从代码devise的angular度来说,声明辅助方法是更为合理的:代码为自己说话,它包含更多有用的信息。 它声明它不需要实例状态来工作。

在字节码级别,更清楚发生了什么事情,没有无用的代码(尽pipe我相信JIT没有办法优化它,不会产生显着的性能成本)。

如果一个方法不使用实例数据,那么它应该是静态的。 如果该函数是公共的,这将提供重要的效率提升,你不需要创build一个多余的对象实例来调用函数。 可能更重要的是自我logging的优点:通过声明静态函数,您可以向读者传达此函数不使用实例数据。

我不明白这里的许多海报的情绪,这是在Java程序中有静态function有什么问题。 如果一个函数在逻辑上是静态的,则将其设为静态。 Java库有许多静态函数。 Math类充满了静态函数。

如果我需要一个函数来计算平方根,理性的做法是:

 public class MathUtils { public static float squareRoot(float x) { ... calculate square root of parameter x ... return root; } } 

当然,你可以做一个“更多的OOPy”版本,看起来像这样:

 public class MathUtils { private float x; public MathUtils(float x) { this.x=x; } public float squareRoot() { ... calculate square root of this.x ... return root; } } 

但是,除了满足一些可能的使用面向对象的抽象目标之外,这又会如何呢? 它需要更多的代码行,并且不够灵活。

(是的,我现在在标准的Math类中有一个平方根函数,我只是用它作为一个方便的例子。)

如果只使用一个静态函数,并且每个可能被使用的地方都来自某个类,那么是的,使其成为该类的成员。 如果从课堂以外称呼它是没有意义的,那就把它变成私人的。

如果一个静态函数与一个类在逻辑上相关联,但是可以合理地从外部调用,那么使其成为公共静态。 就像Java的parseInt函数在Integer类中一样,因为它与整数有关,所以这是一个理性的地方。

另一方面,经常发生的是,你正在写一个类,你意识到你需要一些静态函数,但是这个函数并没有真正绑定到这个类。 这只是你第一次意识到你需要它,但它可能相当理性地被其他类与你现在正在做的事情无关。 比如,回到平方根例子,如果你有一个包含经度和纬度的“Place”类,并且你想要一个函数来计算两个地方之间的距离,并且你需要一个平方根作为计算的一部分,并假装标准库中没有可用的平方根函数),创build一个单独的平方根函数将会很有意义,而不是将其embedded到较大的逻辑中。 但它不会真的属于你的地方类。 这将是为“math公用事业”或其他类别创build一个单独的类的时候。

你问:“不pipe有什么其他的考虑,你应该永远是静止的吗?” 我会说“几乎,但不完全”。

我可以想到,使其不是静态的唯一原因是,如果一个子类想要覆盖它。

我想不出任何其他原因,但我不排除这种可能性。 我不愿意说“永远不会在任何情况下”,因为通常会有人提出一些特殊情况。

有趣的问题。 实际上,我不认为让A类的私有方法是静态的(除非它们和A中的公共访问静态方法有关)。 你没有获得任何东西 – 根据定义,任何可能需要它们的方法都已经有了一个可供使用的实例。 而且由于他们是幕后帮手方法,所以没有什么可以说你(或者另一个同事)最终不会决定其中一个无状态的助手实际上可以从知道状态中获益,这可能会导致一些一个重构的滋扰。

我认为结束大量的内部静态方法并不是错的 ,但是我也没有看到你从中得到的好处。 我说默认为非静态,除非你有一个很好的理由不要。

没有永不。 静态方法应该是一个例外。 面向对象是关于具有围绕对象状态的行为的对象。 Imho,理想情况下,不应该有任何(或很less)静态方法,因为与对象状态无关的东西都可以(并且避免引导对象和概念的概念应该被置于模块中的普通旧函数中水平。 工厂可能的例外,因为Complex.fromCartesian(以维基百科的例子)读得很好。

当然,这个(编辑:模块级别的函数)在单一范式的OO语言中是不可能的(编辑:就像Java) – 顺便说一句,这就是为什么我是如此热衷于多范式语言devise的倡导者。 但即使只是一种语言的OO,大多数方法将围绕着对象的状态,因此是非静态的。 也就是说,除非你的devise与OO无关 – 但在这种情况下,你使用的是错误的语言。

我通常不会让他们静态,但可能应该。 作为一个提示,告诉下一个编码器,这个方法可以修改你的对象的状态,而且当你修改方法来访问一个你正在改变方法性质的成员时,给你一个警告是有价值的。

编码就是与下一个编码器进行通信 – 不要担心代码的运行,这是微不足道的。 所以为了最大化沟通,我会说,如果你真的需要这样一个帮手,使它静态是一个好主意。 除非你正在做math,否则把它变成私人也是至关重要的。 喜欢上课。

Java将模块,名称空间,adt和类的概念混为一谈,声称某些面向类的OO纯度应该会阻止您将java类用作模块,命名空间或adt是荒谬的。

是的,方法应该是静态的。 纯粹的内部支持方法应该是私人的; 帮手方法受保护; 而实用function应该是公开的。 而且,静态字段,静态常量和公共静态方法之间存在着差异。 第一个是“全局variables”的另一个词。 而且几乎总是要避免,即使通过访问方法进行调解也几乎不能限制这种损害。 第二种是将java类视为一个符号常量的名称空间,完全可以接受。 第三种方法是将java类作为函数的模块来使用,因为一般情况下应该避免使用副作用,或者必要时限制传递给函数的任何参数。 静态的使用将有助于确保您不会通过访问对象的成员而无意中破坏这一点。

另一种情况是你会发现静态方法是非常宝贵的,当你在java中编写function代码的时候。 在这一点上,OO支持者开发的大部分经验法则都走出了窗口。 你会发现自己的类充满了静态方法,公共静态函数常量绑定到匿名内部函子。

最终,java具有非常薄弱的​​范围构造,将相同的“类”和“接口”语法下的许多概念混为一谈。 你不应该这么默认静态,因为当你感觉需要的时候,随意使用java提供的设施来提供命名空间,ADT,模块等。

我发现很难订阅那些避免静态方法的理论。 他们在那里促进一个完全卫生的面向对象的模型,在任何偏离对象关系的情况下,都可以被反清洗。 在面向实践的实践中,我认为没有任何必要的反间断纯粹的方法。

无论如何,所有的java.util.Arrays类都是静态的。 数字类Integer,Boolean,String有静态方法。 很多静态方法。 这些类中的所有静态方法都可以转换为或从它们各自的类实例中转换。

因为古老的Gosling等人被certificate是具有静态方法的有用angular色模型 – 避免它们是没有意义的。 我意识到有些人很困惑,不能投我的回应。 为什么很多程序员喜欢把他们的成员转换成静态的原因和习惯。

我曾经在一个项目负责人希望我们尽可能使静态方法静态化并最终完成的工作中工作。 另一方面,我并不是那么极端。 像关系数据库模式devise一样,这一切都取决于您的数据build模策略。

应该有一个一致的原因,为什么方法是静态的。 当方法变为静态时,遵循标准的Java库模式并没有什么坏处。

最重要的是编程生产力和质量。 在一个自适应的敏捷开发环境中,不仅要调整项目的粒度来有效地响应需求变化,而且还要适应编程的氛围,如提供一个适合的编码模型,以便充分利用您所拥有的编程技能。 在一天结束时(一个项目几乎永远不会结束),你希望团队成员高效和有效,而不是他们是否避免使用静态方法。

因此,devise一个编程模型,无论你想MVP,注入,方面驱动,静态避免/亲和力等级,并知道你为什么要他们 – 不是因为一些理论的坚果告诉你,你的编程实践会违反原则。 请记住,如果你在一个行业工作,总是质量和盈利能力,而不是理论纯度。

最后什么是面向对象? 面向对象和数据规范化是创build正交信息透视图的策略。 例如,在早些时候,IBM手册被写成非常正交的。 也就是说,如果一个信息是在成千上万的手册中的一个页面的某个地方写的,他们就避免重复这个信息。 这是不好的,因为你会阅读学习如何执行某个任务,经常遇到其他手册中提到的概念,你将不得不熟悉手册的“数据模型”,以追捕那些连接数千手册。

出于同样的原因,OS / 2未能与微软展开竞争,因为IBM的正交概念纯粹是基于机器和数据的,IBM非常自豪地宣称他们真正的面向对象与微软的面向对象的错误是人类视angular的错误。 他们已经忘记了我们人类有自己各自不同的正交信息的观点,这些信息不符合数据和机器正交性,甚至不符合对方。

如果您熟悉树的拓扑结构,您会意识到可以select任何叶节点并将其作为根。 甚至任何节点,如果你不介意有一个多主干树。 每个人都认为他/她的节点是根,实际上任何可能是根。 如果你认为你的面向对象的angular度是经典,那么再想一想。 更重要的是最小化被接受为候选根的节点的数量。

需要有效性和效率之间的妥协。 有一个高效的数据或对象模型是没有意义的,其他程序员很难有效地使用它。

我通常

按需要执行这些步骤:

a)我在成员方法中编写了一些代码,发现我可以重新使用这些代码

提取到非静态方法

b)现在我将看看这个方法是否需要访问状态,或者是否可以将它的需求放入一个或两个参数和一个返回语句中。 如果后者是这样的话:

使方法(私有)是静态的

c)如果我然后发现我可以在同一个包中的其他类中使用此代码

使方法公开并将Method移动到具有默认可见性的包助手类

例如在包com.mycompany.foo.bar.phleeem我会创build一个类PhleeemHelperPhleeemUtils与默认的知名度。

d)如果我然后意识到我需要这个function遍布我的应用程序,我

将辅助类移到专用的实用程序包中

例如com.mycompany.foo.utils.PhleeemUtils

一般来说,我喜欢最不可见的概念。 那些不需要我的方法的人不应该看到它。 这就是为什么我从私人访问开始,转向打包访问,并且只在专用包中公开时才公开。

除非传入对象引用,否则类上的static方法会强制该方法本身不能改变对象,因为它无法访问this对象。 在这方面, static修饰符向程序员提供关于该方法的意图的信息,即无副作用的信息。

反对纯粹主义者可能希望把那些反对实用纯粹主义者肯定反对的实用阶级去除。 但实际上,除了紧密结合到新的公用事业阶层之外,人为地将这些方法从他们唯一的呼叫站点中移走的是什么。

盲目提取常用工具方法到自己的类中的问题是,那些工具应该被视为一个新的公共API,即使它只被原始代码所使用。 很less有开发人员在执行重构时没有考虑到这一点。 使用蹩脚的实用程序类快速转发给其他开发人员。 稍后有人对扩展进行更改以适应自己。 如果你幸运的话可以试一两次,但可能不会。

如果它对这个类的对象没有任何作用,但实际上属于这个类(我会考虑把它移到其他地方),是的,它应该是静态的。

如果可以避免,请不要使用静态。 它与inheritance冲突(重写)。

另外,不问,但稍有关系,不公开实用的方法。

至于其余的,我同意马特b。 如果你有一个潜在的静态方法,不使用状态的负载,只是把它们放在一个私人类,或可能受保护或包受保护的类。

这取决于ig java.lang.Math没有不是静态的方法。 (你可以做一个静态导入来写cos(),而不是Math.cos())这不应该被过度使用,但作为一些代码被认为是一个实用程序被称为是可以接受的。 Ig Thread.currentThread()

一个静态方法被用来识别一个方法(或者这个问题的variables),这个方法不需要从这个类创build的对象,而是类本身。 例如,您需要一个variables来计算创build的对象的数量。 你会把这样的:'私人静态诠释实例= 0;' 然后在该类的构造函数中添加一些增加“实例”的东西,所以你可以保持计数。

Do think hard before creating a static method, but there are times when they are a good solution.

Joshua Bloch in "Item 1: Consider Static Factory Methods Instead of Constructors" in Effective Java makes a very persuasive case that static methods can be very beneficial. He gives the java.util.Collections class's 32 static factory methods as an example.

In one case, I have a hierarchy of POJO classes whose instances can be automatically serialized into XML and JSON, then deserialized back into objects. I have static methods that use Java generics to do deserialization: fromXML(String xml) and fromJSON(String json) . The type of POJO they return isn't known a priori, but is determined by the XML or JSON text. (I originally packaged these methods into a helper class, but it was semantically cleaner to move these static methods into the root POJO class.)

A couple of other examples:

  • Using a class as a namespace to group related methods (eg, java.lang.Math).
  • The method truly is a private class-specific helper method with no need to access instance variables (the case cited here). Just don't sneak a this -equivalent into its argument list!

But don't use statics unthinkingly or you run the danger of falling into a more disorganized and more procedural style of programming.

No, the use of statics should be quite niche.

In this case the OP is likely 'hiding' state in the parameters passed into the static method. The way the question is posed makes this non-obvious ( foo() has no inputs or outputs), but I think in real world examples the things that should actually be part of the object's state would fall out quite quickly.

At the end of the day every call to obj.method(param) resolves to method(obj, param) , but this goes on at a way lower level than we should be designing at.

If it's only ever used by methods in A and wouldn't have any use outside it, then it should be static (and, probably, be placed in a Helper Class. It doesn't have any use outside A now, but there's no guarantee it will never have. Otherwise, it shouldn't.

If it doesn't have anything to do with the state of A, it could be useful at other places…

Anyway, that doesn't make a good reason for Java methods to be static by default.

Talking about this last issue, they shouldn't be static by default, because having to write 'static' make people think before writing static methods. That's a good practice when you have heterogeneous teams (the ones where Java is most useful).

If foo is private, it may be anything, static or not. But, most of the time it will be not static as these is one less word to type. Then, if you need to use the state because you've changed your code, you can do it right away.

When it is protected or public, it depends on what it does. A rule of thumb is to make it not static when it isn't a part of the instance's behaviour, and make it static when it makes sense to call it without any object. If you are unsure, ask yourself if it makes sense to override the method in a subclass.

I think letting the methods in Java to be static will result in a rather chaotic implementation by beginner who haven't understand OO correctly. We've been there and think about it. If the methods were static as default how hard it is for us to understand the OO principle?

So yes, once you mastered the concept, it is a bit itchy to have static all over the methods (as result of refactoring). Nothing we ca do about that I think.

NB: Let me guess, are you by any chance have read Clean Code ?

When you write a static method you should keep in mind that you'r gonna use it at use-site with static-import (make it look class free) and thus it should behave just like a function which doesn't something and may or may not return something and is isolated with the state of class it belongs to. So static methods should be a rare situation.

If you seem to be making a lot of helper methods, then consider using package-private instance methods instead of private ones. Less typing, less boilerplate since you can re-use them as a helper to other classes in the same package.

I think "private static" (edit: for methods) is kind of an oxymoron in Java. The main point of static methods in my mind is to provide access to functions outside of the context of object instances. In other words, they're practically only ever useful if they're public. If you're only calling a method from within the context of a single object instance, and that method is private, it makes no sense to make it static. (edit: but, it makes no practical difference).

In this sort of case, I usually try to make the methods abstract enough that they're useful in other contexts, and I make them public in a utility class. Look at it as writing supporting library code, and think hard about your api.

Most static methods are written because

  1. You break down a complex method into submethods, or
  2. You wish String (or Date, or…) had some functionality that it doesn't have

The first is not bad per se, but it's often a sign that you're missing objects. Instead of working with default types such as String or List, try inventing your own classes and move the static methods to those classes.

The second reason produces the always-popular StringUtil, DateUtil, FooUtil classes. These are problematic because you have no way to discover that they exist, so programmers often write duplicates of these utility methods. The solution, again, is to avoid using String and Date all the time. Start creating your own objects, perhaps by wrapping the original object. The static methods become non-static methods of the new object.

If foo() doesn't have anything to do with Object A then why is the method in there?

Static methods should still be relevant. If there isn't anything going on then why have you written a method that has no association with it?

Plenty of interesting answers.

If you desperately seek a rule, then use this:

If the code is only ever used by instance methods of a single class, then make it an instance method – it is simply an extraction of code out of an instance context – which could be refactored back into (or out of) methods that access instance state.

If the code is used by MORE THAN ONE class, and contains no access to instance variables in the class in which the method resides, then make it static.

故事结局。