1. Overview
1.概述
Static methods are common to most object-oriented programming languages, including Java. What differentiates static from instance methods is that they have no object that owns them. Instead, static methods are defined on the class level and can be used without creating instances.
静态方法是大多数面向对象的编程语言所共有的,包括Java。静态方法与实例方法的不同之处在于,它们没有拥有它们的对象。相反,静态方法是在类的层次上定义的,可以在不创建实例的情况下使用。
In this tutorial, we’ll look at the definition of static methods in Java, as well as their limitations. Then we’ll look at common use cases for using static methods and recommend when it makes sense to apply them in our code.
在本教程中,我们将看看Java中静态方法的定义,以及它们的局限性。然后,我们将看看使用静态方法的常见用例,并建议何时在我们的代码中应用它们是有意义的。
Finally, we’ll see how to test static methods and how to mock them.
最后,我们将看到如何测试静态方法以及如何模拟它们。
2. Static Methods
2.静态方法
Instance methods are resolved polymorphically based on the runtime type of the object. On the other hand, static methods are resolved at compile-time based on the class in which they are defined.
实例方法是根据对象的运行时类型多态地解析的。另一方面,静态方法是在编译时根据其定义的类来解决的。
2.1. Class-level
2.1.阶级层面
A static method in Java is a part of the class definition. We can define a static method by adding the static keyword to a method:
Java中的静态方法是类定义的一部分。我们可以通过在方法中添加static关键字来定义静态方法。
private static int counter = 0;
public static int incrementCounter() {
return ++counter;
}
public static int getCounterValue() {
return counter;
}
To access static methods, we use the class name followed by a dot and the name of the method:
要访问静态方法,我们使用类的名称,后面加一个点和方法的名称。
int oldValue = StaticCounter.getCounterValue();
int newValue = StaticCounter.incrementCounter();
assertThat(newValue).isEqualTo(oldValue + 1);
We should note that this static method has access to the static state of the StaticCounter class. Often static methods are stateless, but they can work with class-level data as part of various techniques, including the singleton pattern.
我们应该注意,这个静态方法可以访问StaticCounter类的静态状态。通常情况下,静态方法是无状态的,但它们可以作为各种技术的一部分与类级数据一起工作,包括singleton模式。
Although it’s also possible to reference static methods using objects, this antipattern is often flagged as an error by tools such as Sonar.
尽管使用对象来引用静态方法也是可行的,但这种反模式经常被Sonar等工具标记为错误。
2.2. Limitations
2.2.限制条件
As static methods don’t operate on instance members, there are a few limitations we should be aware of:
由于静态方法不对实例成员进行操作,我们应该注意到一些限制。
- A static method cannot reference instance member variables directly
- A static method cannot call an instance method directly
- Subclasses cannot override static methods
- We cannot use keywords this and super in a static method
Each of the above results in a compile-time error. We should also note that if we declare a static method with the same name in a subclass, it doesn’t override but instead hides the base class method.
上述每一种情况都会导致一个编译时错误。我们还应该注意到,如果我们在子类中声明一个同名的静态方法,它不会覆盖,而是隐藏基类的方法。
3. Use Cases
3.使用案例
Let’s now have a look at common use cases when it makes sense to apply static methods in our Java code.
现在让我们来看看在Java代码中应用静态方法有什么意义的常见用例。
3.1. Standard Behaviour
3.1.标准行为
Using static methods makes sense when we are developing methods with standard behavior that operates on their input arguments.
当我们开发具有标准行为的方法,对其输入参数进行操作时,使用静态方法是有意义的。
The String operations from Apache StringUtils are a great example of this:
来自Apache StringUtils 的String操作是这方面的一个很好的例子。
String str = StringUtils.capitalize("baeldung");
assertThat(str).isEqualTo("Baeldung");
Another good example is the Collections class, as it contains common methods that operate on different collections:
另一个很好的例子是Collections类,因为它包含了对不同集合进行操作的常用方法。
List<String> list = Arrays.asList("1", "2", "3");
Collections.reverse(list);
assertThat(list).containsExactly("3", "2", "1");
3.2. Reuse Across Instances
3.2.跨实例的重复使用
A valid reason for using static methods is when we reuse standard behavior across instances of different classes.
使用静态方法的一个有效理由是当我们在不同类的实例中重复使用标准行为时。
For example, we commonly use Java Collections and Apache StringUtils in our domain and business classes:
例如,我们在领域和业务类中通常使用Java Collections 和Apache StringUtils。
As these functions don’t have a state of their own and are not bound to a particular part of our business logic, it makes sense to hold them in a module where they can be shared.
由于这些函数没有自己的状态,也没有绑定到我们业务逻辑的特定部分,所以把它们放在一个可以共享的模块中是有意义的。
3.3. Not Changing State
3.3.不改变状态
Since static methods cannot reference instance member variables, they are a good choice for methods that don’t require any object state manipulation.
由于静态方法不能引用实例成员变量,它们是不需要任何对象状态操作的方法的良好选择。
When we use static methods for operations where the state is not managed, then method calling is more practical. The caller can call the method directly without having to create instances.
当我们使用静态方法进行状态不被管理的操作时,那么方法调用就更加实用。调用者可以直接调用方法,而不需要创建实例。
When we share state by all instances of the class, like in the case of a static counter, then methods that operate on that state should be static. Managing a global state can be a source of errors, so Sonar will report a critical issue when instance methods write directly to static fields.
当我们通过类的所有实例共享状态时,就像静态计数器的情况一样,那么对该状态进行操作的方法应该是静态的。管理全局状态可能是错误的来源,因此当实例方法直接写入静态字段时,Sonar将报告关键问题。
3.4. Pure Functions
3.4.纯函数
A function is called pure if its return value depends only on the input parameters passed. Pure functions get all the data from their parameters and compute something from that data.
如果一个函数的返回值只取决于所传递的输入参数,则该函数被称为纯函数。纯函数从其参数中获取所有数据,并从这些数据中计算出一些东西。
Pure functions do not operate on any instance or static variables. Therefore, executing a pure function should also have no side effects.
纯函数不对任何实例变量或静态变量进行操作。因此,执行一个纯函数也应该没有副作用。
As static methods do not allow overriding and referencing instance variables, they are a great choice for implementing pure functions in Java.
由于静态方法不允许覆盖和引用实例变量,它们是在Java中实现纯函数的最佳选择。
4. Utility Classes
4.公用事业类
Since Java doesn’t have a specific type set aside for housing a set of functions, we often create a utility class. Utility classes provide a home for pure static functions. Instead of writing the same logic over and over, we can group together pure functions which we reuse throughout the project.
由于Java没有一个特定的类型来容纳一组函数,我们经常创建一个实用类。实用类为纯静态函数提供了一个家。我们可以将纯函数组合在一起,在整个项目中重复使用,而不是一遍又一遍地编写相同的逻辑。
A utility class in Java is a stateless class that we should never instantiate. Therefore, it’s recommended to declare it final, so it cannot be subclassed (which wouldn’t add value). Also, in order to prevent anyone from trying to instantiate it, we can add a private constructor:
Java中的实用类是一个无状态的类,我们不应该实例化它。因此,我们建议将其声明为final,这样它就不能被子类化(这不会增加价值)。另外,为了防止任何人试图实例化它,我们可以添加一个private constructor。
public final class CustomStringUtils {
private CustomStringUtils() {
}
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
}
We should note that all methods we put in the utility class should be static.
我们应该注意,我们放在实用类中的所有方法都应该是static。
5. Testing
5.测试
Let’s check how we can unit test and mock static methods in Java.
让我们看看如何在Java中进行单元测试和模拟静态方法。
5.1. Unit Testing
5.1.单元测试
Unit testing of well-designed, pure static methods with JUnit is quite simple. We can use the class name to call our static method and pass some test parameters to it.
用JUnit对精心设计的纯静态方法进行单元测试是非常简单的。我们可以使用类名来调用我们的静态方法,并向其传递一些测试参数。
Our unit under test will compute the result from its input parameters. Therefore, we can make assertions on the result and test for different input-output combinations:
我们的被测单元将根据其输入参数计算出结果。因此,我们可以对结果进行断言,对不同的输入输出组合进行测试。
@Test
void givenNonEmptyString_whenIsEmptyMethodIsCalled_thenFalseIsReturned() {
boolean empty = CustomStringUtils.isEmpty("baeldung");
assertThat(empty).isFalse();
}
5.2. Mocking
5.2.嘲弄
Most of the time, we don’t need to mock static methods, and we can simply use the real function implementation in our tests. The need to mock static methods typically hints at a code design issue.
大多数时候,我们不需要模拟静态方法,我们可以简单地在测试中使用真正的函数实现。需要模拟静态方法通常暗示着代码设计问题。
If we must, then we can mock static functions using Mockito. However, we will need to add an additional mockito-inline dependency to our pom.xml:
如果我们必须这样做,那么我们可以使用Mockito模拟静态函数。然而,我们需要在我们的pom.xml中添加一个额外的mockito-inline依赖项。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
Now, we can use the Mockito.mockStatic method to mock invocations to static method calls:
现在,我们可以使用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");
}
6. Conclusion
6.结语
In this article, we explored common use cases for using static methods in our Java code. We learned the definition of static methods in Java, as well as their limitations.
在本文中,我们探讨了在Java代码中使用静态方法的常见用例。我们了解了Java中静态方法的定义,以及它们的局限性。
Also, we explored when it makes sense to use static methods in our code. We saw that static methods are a good choice for pure functions with standard behavior that are getting reused across instances but not changing their state. Finally, we looked at how to test and mock static methods.
此外,我们还探讨了在代码中使用静态方法的意义。我们看到,静态方法是具有标准行为的纯函数的一个很好的选择,这些纯函数在不同的实例中被重复使用,但不改变其状态。最后,我们研究了如何测试和模拟静态方法。
As always, the complete source code is available over on GitHub.
一如既往,完整的源代码可在GitHub上获得,。