一、概述
静态方法在大多数面向对象的编程语言中都很常见,包括 Java。 静态方法与实例方法的区别在于它们没有拥有它们的对象。 相反,静态方法是在类级别定义的,可以在不创建实例的情况下使用。
在本教程中,我们将了解 Java 中静态方法的定义以及它们的局限性。 然后,我们将查看使用静态方法的常见用例,并建议何时将它们应用到我们的代码中。
最后,我们将看到如何测试静态方法以及如何模拟它们。
2. 静态方法
实例方法根据对象的运行时类型进行多态解析。 另一方面,静态方法在编译时根据定义它们的类来解析。
2.1。 班级级别
Java 中的静态方法是类定义的一部分。 我们可以通过在方法中添加关键词来定义静态方法:static
private static int counter = 0; public static int incrementCounter() { return ++counter; } public static int getCounterValue() { return counter; }
要访问静态方法,我们使用类名后跟一个点和方法名:
int oldValue = StaticCounter.getCounterValue(); int newValue = StaticCounter.incrementCounter(); assertThat(newValue).isEqualTo(oldValue + 1);
我们应该注意到,这个静态方法可以访问类的静态状态。 静态方法通常是无状态的,但它们可以作为各种技术的一部分处理类级数据,包括单例模式。StaticCounter
尽管也可以使用对象引用静态方法,但这种反模式经常被Sonar等工具标记为错误。
2.2.限制
由于静态方法不对实例成员进行操作,因此我们应该注意一些限制:
静态方法不能直接引用实例成员变量
静态方法不能直接调用实例方法
子类不能覆盖静态方法
我们不能在静态方法中使用关键字和
this
super
以上每一项都会导致编译时错误。 我们还应该注意,如果我们在子类中声明一个同名的静态方法,它不会覆盖而是隐藏基类方法。
3.用例
现在让我们看一下在我们的 Java 代码中应用静态方法有意义的常见用例。
3.1。 标准行为
当我们开发具有对其输入参数进行操作的标准行为的方法时,使用静态方法是有意义的。
来自 Apache 的操作就是一个很好的例子:StringUtils
String
String str = StringUtils.capitalize("baeldung"); assertThat(str).isEqualTo("Baeldung");
另一个很好的例子是类,因为它包含对不同集合进行操作的常用方法:Collections
List<String> list = Arrays.asList("1", "2", "3"); Collections.reverse(list); assertThat(list).containsExactly("3", "2", "1");
3.2.跨实例重用
使用静态方法的一个正当理由是当我们跨不同类的实例重用标准行为时。
例如,我们通常在域和业务类中使用 Java 和 Apache :Collections
StringUtils
由于这些函数没有自己的状态,也没有绑定到我们业务逻辑的特定部分,因此将它们保存在可以共享的模块中是有意义的。
3.3.不改变状态
由于静态方法不能引用实例成员变量,因此对于不需要任何对象状态操作的方法来说,它们是一个不错的选择。
当我们使用静态方法进行状态不受管理的操作时,方法调用更实用。 调用者可以直接调用该方法,而无需创建实例。
当我们通过类的所有实例共享状态时,例如在静态计数器的情况下,那么在该状态上操作的方法应该是静态的。 管理全局状态可能是错误的来源,因此当实例方法直接写入静态字段时,Sonar 会报告一个关键问题。
3.4.纯函数
如果函数的返回值仅取决于传递的输入参数,则该函数称为纯函数。 纯函数从它们的参数中获取所有数据,并从这些数据中计算出一些东西。
纯函数不对任何实例或静态变量进行操作。 因此,执行纯函数也应该没有副作用。
由于静态方法不允许覆盖和引用实例变量,因此它们是在 Java 中实现纯函数的绝佳选择。
4. 实用程序类
由于 Java 没有为容纳一组函数预留特定的类型,因此我们经常创建一个实用程序类。 实用程序类为纯静态函数提供了归宿。 我们可以将在整个项目中重用的纯函数组合在一起,而不是一遍又一遍地编写相同的逻辑。
Java 中的实用程序类是我们永远不应该实例化的无状态类。 因此,建议将其声明为 ,因此它不能被子类化(这不会增加价值)。 另外,为了防止任何人试图实例化它,我们可以添加一个私有构造函数:final
public final class CustomStringUtils { private CustomStringUtils() { } public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } }
我们应该注意,我们放在实用程序类中的所有方法都应该是的。static
5. 测试
让我们检查一下如何在 Java 中对静态方法进行单元测试和模拟。
5.1。 单元测试
使用 JUnit 对设计良好的纯静态方法进行单元测试非常简单。 我们可以使用类名来调用我们的静态方法并传递一些测试参数给它。
我们的被测单元将根据其输入参数计算结果。 因此,我们可以对结果进行断言并测试不同的输入输出组合:
@Test void givenNonEmptyString_whenIsEmptyMethodIsCalled_thenFalseIsReturned() { boolean empty = CustomStringUtils.isEmpty("baeldung"); assertThat(empty).isFalse(); }
5.2.嘲笑
大多数时候,我们不需要模拟静态方法,我们可以在测试中简单地使用真实的函数实现。 模拟静态方法的需要通常暗示代码设计问题。
如果必须,那么我们可以使用 Mockito 模拟静态函数。 但是,我们需要在 pom.xml 中添加一个额外的mockito-inline
依赖项:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>3.8.0</version> <scope>test</scope> </dependency>
現在,我們可以使用 模拟方法調用的調用的方法:Mockito.mockStatic
try (MockedStatic<StringUtils> utilities = Mockito.mockStatic(StringUtils.class)) { utilities.when(() -> StringUtils.capitalize("karoq")).thenReturn("Karoq"); Car car1 = new Car(1, "karoq"); assertThat(car1.getModelCapitalized()).isEqualTo("Karoq"); }
六,结论
在本文中,我们**探讨了在 Java 代码中使用静态方法的常见用例。 **我们了解了 Java 中静态方法的定义,以及它们的局限性。
此外,我们探讨了在代码中使用静态方法何时有意义。 我们看到,对于具有标准行为的纯函数来说,静态方法是一个不错的选择,这些函数可以跨实例重用,但不会改变它们的状态。 最后,我们研究了如何测试和模拟静态方法。
0 评论