何时使用静态方法

1068

我想知道何时使用静态方法?比如说,如果我有一个类,里面有几个getter和setter,还有一两个方法,并且我希望这些方法只能在该类的实例对象上调用。这是否意味着我应该使用静态方法?

例如:

Obj x = new Obj();
x.someMethod();

...或者:

Obj.someMethod(); // Is this the static way?

我很困惑!


1
这是一篇关于Java中何时使用静态方法的文章:https://www.tutorialspoint.com/When-to-use-static-methods-in-Java - masterxilo
25个回答

1643
一条经验法则:问问自己,“即使没有构造对象,调用此方法是否有意义?” 如果是这样的话,它肯定应该是静态的。
因此,在一个叫做 Car 的类中,你可能会有一个方法:
double convertMpgToKpl(double mpg)

...这将是静态的,因为即使没有人制造过Car,某些人可能仍想知道35mpg相当于多少。但是这个方法(设置一个特定Car的效率):

void setMileage(double mpg)

由于在构造任何“汽车”之前调用该方法是不可想象的,因此它不能是静态的。

(顺便说一下,反过来并不总是成立:有时您可能会有涉及两个“汽车”对象的方法,但仍希望它是静态的。例如:

Car theMoreEfficientOf(Car c1, Car c2)

尽管这可以转换为非静态版本,但一些人可能会认为由于不存在一个“特权”选择哪个Car更重要,你不应该强迫调用者选择一个Car作为将调用方法的对象。尽管如此,这种情况只占所有静态方法的相当小的比例。

381
这里有一些很好的例子。但我想补充一点,当你知道某些内容不会在实例之间发生改变时,“静态”的概念通常是有价值的。如果是这种情况,我真的建议考虑“单一职责原则”,这意味着一个类应该只有一个职责,因此只有一个改变的原因。我认为应该考虑将“ConvertMpgToKpl(double mpg)”函数和类似的方法移动到它们自己的类中。汽车对象的目的是允许汽车的实例化,而不是提供它们之间的比较。那些应该是外部类的。 - Zack Jannsen
37
我认为我更喜欢使用 Car#isMoreEfficientThan(Car) 这种方法。它的优点在于当出现平局时,你返回哪辆车不是随意的。从该方法的标题就可以明显看出在平局情况下会返回哪辆车。 - Cruncher
11
我也会小心谨慎地创建使用某些外部资源(文件系统、数据库等)的静态方法,这种类型的静态方法可能会使测试消费方法变得非常困难。我个人尝试将静态方法保持在“实用程序”领域。 - Seth M.
10
事实上,它应该被实现为一个比较器 - Dogweather
3
当然可以。我的意思是“在平局情况下归还哪一辆车”,其中“true”指的是被调用的车,“false”指的是传入的车,这样就没有歧义了。 - Cruncher
显示剩余9条评论

604

只有在以下情况下定义静态方法:

  1. 如果您正在编写实用程序类,并且不希望它们被更改。
  2. 如果该方法没有使用任何实例变量。
  3. 如果任何操作不依赖于实例创建。
  4. 如果存在一些可轻松与所有实例方法共享的代码,请将该代码提取为静态方法。
  5. 如果您确定该方法的定义永远不会更改或重写,因为静态方法无法重写。

59
好的观点,但它们只是使方法静态化时需要遵循的要求,而非静态化方法的原因。 - tetsuo
4
关于需求5,你什么时候能够百分之百确定一个方法不会被更改或覆盖?在编写静态方法时,难道不会存在当前无法考虑到的未知因素吗? - PixelPlex
11
“Utility-Classes” 很难理解,不好的一点是,迟早所有东西都会开始像一个 utility(没错,我指的是那个臃肿、无法改动和测试不充分的 “util” 包),而且你的测试案例需要更多的工作(模拟静态工具类很难)。最好优先考虑使用对象。 - Sergio
2
@Mohd 这个答案正是我所需要的。在多线程中使用静态方法时,我遇到了很多问题。您能否详细说明第2点和第3点(最好附上示例)?100个赞给您! - Prakash P
如果您要使用静态变量和方法,我认为应该发明一个“静态类”。 - Robert
4不应该是一个实例方法吗?没有必要将其设为静态的,对吧? - Yassin Hajaj

219

使用静态方法有一些有效的原因:

  • 性能:如果你想运行某些代码,而不想实例化额外的对象来执行此操作,则将其放入静态方法中。JVM也可以对静态方法进行优化(我认为我曾经读过James Gosling宣称你不需要在JVM中使用自定义指令,因为静态方法将像这样快,但我找不到来源 - 所以可能完全是错误的)。是的,这是微优化,可能不需要。我们程序员永远不会做不必要的事情,只是因为它们很酷,对吧?

  • 实用性:不要调用new Util().method(arg),而是调用Util.method(arg)或带有静态导入的method(arg)。更容易,更短。

  • 添加方法:你真的希望类String有一个removeSpecialChars()实例方法,但它不存在(而且不应该存在,因为你的项目的特殊字符可能与其他项目的不同),并且你无法添加它(因为Java有点明智),所以你创建一个实用程序类,并调用removeSpecialChars(s)而不是s.removeSpecialChars()。Sweet。

  • 纯度:采取一些预防措施,您的静态方法将是纯函数,也就是说,它所依赖的唯一事物是其参数。数据进去,数据出来。这更容易阅读和调试,因为你不必担心继承怪癖。你也可以使用实例方法来实现,但编译器会在静态方法上帮你一点忙(不允许引用实例属性,重写方法等)。

如果你想创建单例,也必须创建静态方法,但是……别这么做。我的意思是,考虑清楚。

现在,更重要的是,为什么你不想创建静态方法?基本上,多态性将不复存在。您将无法覆盖该方法,也不能在接口中声明它(Java 8之前)。它会从设计中带走很多灵活性。此外,如果您需要状态,如果不小心,您将遇到大量并发错误和/或瓶颈。


2
这里列出了很多使用静态方法的好理由。我能想到的另一个理由是,为这样的方法编写单元测试非常简单明了。 - nilesh
1
@tetsuo 谢谢!你的解释非常清晰,提供的理由非常有逻辑性,让人感到很有道理。 - Deniss M.
5
我们程序员从不做没有必要的事情,只是因为它们看起来很酷,对吧?+1 - Scaramouche
1
这意味着静态方法变成了一个完整的命名函数。https://dev59.com/pXVC5IYBdhLWcg3w4VVz - user2188550
1
我同意性能和实用性,但不赞成纯洁性。静态方法可以修改类的静态成员(可能是私有的)。这很有用。例如,您可以拥有一个像“static synchronized int allocateID() {return idNext ++;}”这样的方法。事实上,静态方法在副作用方面可以与非静态方法一样纯净或不纯净。 - Adam Gawne-Cain
@AdamGawne-Cain 好的,Java不是一种函数式语言,也没有真正支持纯函数。我想当我写这个的时候,我的思路是,由于静态方法放弃了许多功能 - 如多态性、动态绑定、继承和实例作用域 - 所以保持方法纯洁会更容易,因为你只需要担心不使用任何类状态,并且只调用其他纯方法。但是,是的,你是对的,它仍然与实例方法共享许多有关副作用的问题。 - tetsuo

46

阅读Misko的文章后,我认为从测试的角度看,静态方法不好。你应该使用工厂模式(也许使用像Guice这样的依赖注入工具)。

我如何确保只有一个实例?

确保“我只有一个实例”这个问题得到了很好的解决。您仅在主函数中实例化一个ApplicationFactory,因此您仅实例化所有单例的单个实例。

静态方法的基本问题在于它们是过程性代码

静态方法的基本问题在于它们是过程性代码。我不知道如何对过程性代码进行单元测试。单元测试假定我可以将应用程序的一部分隔离出来进行实例化。在实例化期间,我使用mocks/friendlies替换真实的依赖项来连接依赖项。由于过程性编程没有对象,所以没有任何东西可“连接”,代码和数据是分离的。


22
我不理解不能对过程式代码进行单元测试的部分。你难道不只是设置测试用例,使用静态方法和该类作为“单元”,将正确的输入映射到正确的输出吗? - tjb
3
你可以使用这种方法来测试那些函数。但是当在其他需要测试的类中使用这些静态方法时,我认为你无法伪造它们(模拟/友好类)或任何东西,因为你不能实例化一个类。 - Alfred
4
@Alfred:请查看PowerMock,它具有模拟静态方法的能力。使用PowerMock,几乎没有任何情况下你会发现无法模拟方法依赖。 - Carles Sala
7
你可以使用PowerMock来对静态方法进行单元测试,但你会很快发现你的Permgen空间不够用(我已经经历过了),而且这仍然很糟糕。除非你确信(基于至少十年自己编写真正面向对象语言的经验,而不是从C迁移),否则不要这么做。说真的,我见过的最糟糕的代码来自于嵌入式开发人员对静态方法的使用,在大多数情况下我们被困在其中,永远无法修改,添加更多的代码只会把我们更紧密地锁定在不可修改的巨型代码库中。松耦合:没有,可测试性:勉强,可修改性:永远没有。避免! - user1016765
16
我理解测试依赖静态状态的静态方法的难度。但是,当您测试无状态的静态方法,例如Math.abs()Arrays.sort()等,即使是您可以将所有依赖项传递进去的方法,我也不认为这会妨碍单元测试。 我会说一个简单的经验法则是:如果您有任何原因模拟过程逻辑,则不要将其放在静态方法中。我从未有过模拟Arrays.sort()Math.abs()的原因。 - Andy
显示剩余9条评论

38

static方法是一种不需要初始化对象即可调用的方法类型。你是否注意到在Java中static被用于main函数中?程序从那里开始执行,不需要创建对象。

考虑以下示例:

 class Languages 
 {
     public static void main(String[] args) 
     {
         display();
     }

     static void display() 
     {
         System.out.println("Java is my favorite programming language.");
     }
  }

最佳答案 - Yahya

22

Java中的静态方法属于类(而不是其实例)。它们不使用实例变量,通常从参数中获取输入,对其执行操作,然后返回一些结果。实例方法与对象相关联,顾名思义可以使用实例变量。


13

如果您在任何方法中使用static关键字,它就被称为静态方法。

  1. 静态方法属于类而不是类的对象。
  2. 可以调用静态方法而不需要创建类的实例。
  3. 静态方法可以访问静态数据成员并更改其值。
  4. 可以使用类名.静态方法名称来访问静态方法。例如:Student9.change();
  5. 如果要使用类的非静态字段,则必须使用非静态方法。

//更改所有对象(静态字段)的公共属性的程序。

class Student9{  
 int rollno;  
 String name;  
 static String college = "ITS";  

 static void change(){  
 college = "BBDIT";  
 }  

 Student9(int r, String n){  
 rollno = r;  
 name = n;  
 }  

 void display (){System.out.println(rollno+" "+name+" "+college);}  

public static void main(String args[]){  
Student9.change();  

Student9 s1 = new Student9 (111,"Indian");  
Student9 s2 = new Student9 (222,"American");  
Student9 s3 = new Student9 (333,"China");  

s1.display();  
s2.display();  
s3.display();  
}  }

O/P: 111 印度 BBDIT 222 美国 BBDIT 333 中国 BBDIT


13

不,静态方法与实例无关;它们属于类。 静态方法是您的第二个示例;实例方法是第一个示例。


1
如果不需要操作对象的状态,应该使用静态方法。 - MastAvalons

11
静态方法应该在类上调用,实例方法应该在类的实例上调用。但这到底是什么意思呢?以下是一个有用的例子:
汽车类可能有一个名为Accelerate()的实例方法。只有当汽车实际存在(已被构造),才能加速汽车,因此这将是一个实例方法。
汽车类还可能有一个名为GetCarCount()的计数方法。这将返回创建的汽车总数(或已构造)。如果没有构造汽车,则此方法将返回0,但仍应能够调用它,因此它必须是一个静态方法。

10

静态方法与实例无关,因此不能访问类中的任何非静态字段。

如果该方法不使用类的任何字段(或仅使用静态字段),则应使用静态方法。

如果使用了类的任何非静态字段,则必须使用非静态方法。


1
清晰、简短、简单的回答。 - Yosidroid

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