何时使用静态方法和静态字段?

6

我知道什么是静态,但不确定何时使用它。

静态变量: 我只在常量字段中使用它。有时候一个类中会有很多常量,所以使用静态常量可以节省大量内存。还有其他典型的用例吗?

静态方法: 我在算法类中使用它。例如,提供不同排序算法的类。这是否违反了OOP设计原则?我认为这种方式比在需要使用它们的每个类中实现排序算法更好维护。我错了吗?还有哪些好的用例?

此外,使用静态和非静态字段/方法之间是否有性能差异?


静态类级别方法,非静态对象级别方法。 - nachokk
6个回答

23
您正在描述使用静态的情况,但这并没有从根本上解释为什么会使用静态而不是非静态-它们不仅是常量和实用方法的关键字。
当某个东西不是静态的(实例),它意味着每个类实例都有一个实例。每个实例可以独立更改。
当某个东西是静态的时,它意味着所有类实例只有一个副本,因此从任何位置更改它都会影响所有其他实例。
静态变量/方法通常使用较少的内存,因为无论您有多少类实例,它们只有一个副本。在适当的情况下,静态方法在面向对象设计中完全可以使用。
如果您只需要一个实例的方法/变量(例如常量或实用程序方法),那就将其设置为静态。但要理解,使方法静态意味着它不能被覆盖。因此,如果您想在子类中覆盖方法,则不要将其设置为静态。
一般的经验法则是:如果您只需要一个副本,请将其设置为静态。如果需要每个实例的副本,则将其设置为非静态。

"static"对于方法并不意味着"final"。它仍然可以被覆盖(称为“隐藏”),但是调用的类方法取决于它被调用的类。http://docs.oracle.com/javase/tutorial/java/IandI/override.html - gobernador
覆盖和隐藏在您提供的链接中有所不同。 - Jeff Storey

3

还有其他典型的用例吗?

全局变量

这违反了面向对象设计吗?

并非完全如此,因为静态方法是无状态的,不需要特定类的实例。我的首选方法是实用程序方法(例如Apache Commons)。但是您可能已经意识到,某些方法可能比静态成员更合适。

此外,一旦无法覆盖这些方法或替换为模拟实现,静态方法可能会使类的可测试性更加困难。

性能差异?

Google有一个性能Android建议说“优先静态而不是虚拟”:

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

我不确定对于JVM是否真的如此,因为Android使用不同的VM,但考虑到链接指出的原因,这是有道理的:

如果您不需要访问对象的字段,请将方法设为静态。调用大约会快15%-20%。这也是个好习惯,因为您可以从方法签名中看出调用该方法不会改变对象的状态。"


虽然我想知道在电脑上的性能差异,但+1给那唯一尝试回答性能问题的人。 - Alex

0

静态变量属于类,因此被所有对象共享,如果您真的希望变量被共享,则内存使用量较少。如果您将变量声明为公共和静态,则全局可用于所有人。

静态方法通常是实用方法,取决于访问修饰符,可以在类内或跨类中使用。静态实用程序类将再次有助于减少内存使用,因为您无需创建对象即可调用这些方法。


6
不同意静态变量是全局变量。它们是两个不同的概念。私有静态变量可以作为类常量,但这并不使其成为全局变量。全局变量通常违反了面向对象编程的原则,而静态变量则没有。 - Jeff Storey

0

我个人的经验法则是,静态的东西就像是“悬在那里的”。它们是(免责声明,不完全正确的)全局的东西,但与这个特定的类一起包含是有意义的。

如果你发现自己重复加载一些重量级的对象,那么静态字段是很好的选择。例如,我现在正在处理的项目有两个图像之间的切换。这些是静态字段,应用程序加载后会一直保留在内存中,而不是每次重新加载并让GC来处理混乱。


在这种情况下,存储图像的 SoftReference,并在垃圾收集器清除引用后将其恢复。 - Eric Jablow

0
除了特定情况,我只将静态(和最终)变量用于常量。当然,这是完全有效的。
我倾向于避免使用静态实用方法,因为它们使得编写代码的单元测试更加困难(模拟方法调用的结果)。当你开始采用测试驱动的方式进行开发时,这个问题变得非常明显。我更喜欢使用依赖注入和单例bean(尽管这取决于您的需求和情况)。

我认为限制编写某些程序的东西是不好的... :d,你避免使用静态实用方法,这使得编写单元测试更加困难,那么问题就不在于静态。 - Aadam

0

静态字段在所有对象中只有一个值,也称为类成员,因为它与类相关。

  • 您可以将静态字段用作实用程序。

    例如,假设我们需要知道有多少个实例:

class Counter

     public class Counter {


     public static int instanceCount ;

        public Counter()
        {
            instanceCount++;
        }

        public int getInstanceCount()
        {
            return instanceCount;
        }



    }

创建了两个Counter类的实例。但是它们共享相同的instanceCount字段,因为它是一个静态字段,所以firstCountersecondCounter中的instanceCount值将变得相同。

main

       Counter firstCounter = new Counter();
       // will print 1
       System.out.println(co.getInstanceCount());
       // will print 2
        Counter secondCounter = new Counter();

        System.out.println(co1.getInstanceCount());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接