ProgrammingJava Developer

What is the difference between using utility classes with static methods and creating separate class instances to handle business logic in Java? When should each approach be used?

Pass interviews with Hintsage AI assistant

Answer.

Background

From the beginning, Java was developed as an object-oriented language, where the main task was solved by creating class instances. However, utility classes with a set of static methods (e.g., java.util.Collections) began to appear for frequently used methods (e.g., sorting, data transformation).

Problem

Static methods simplify the calling of utility functions, but they are not suitable for storing state, injecting dependencies, and testing in isolation. On the other hand, class instances are more flexible but increase the amount of code required for initialization and require careful lifecycle management.

Solution

  • Utility classes — a set of stateless static methods that do not require the creation of an object. They are good for operations on collections, transformations, and mathematical operations.

  • Class instances — they store state, use dependencies, and provide extensibility and testability. They are used in business logic, services, controllers, etc.

Example code:

// Example of a utility class public class MathUtils { public static int add(int a, int b) { return a + b; } } // Usage: int sum = MathUtils.add(1, 2); // Example of a class instance for business logic public class OrderService { private final OrderRepository repo; public OrderService(OrderRepository repo) { this.repo = repo; } public void placeOrder(Order o) { repo.save(o); } }

Key features:

  • Stateless and not testable in isolation — utility classes.
  • Classes with state can be easily replaced through dependency injection (DI).
  • Utility classes are often finalized and have a private constructor.

Trick questions.

Can utility classes be inherited, and can their static methods be extended?

No, utility classes are usually declared as final, with a private constructor. Inheriting static methods is possible but makes no sense, as static methods are not inherited at the instance call level.

Example code:

public final class MyUtils { private MyUtils() {} // prevents instance creation }

Can utility classes contain state?

No. If a utility class contains state (instance or static fields), it violates the principle of writing utilities, leads to multithreading errors, and decreases readability.

Can static methods of utility classes be mocked during testing?

Only using special tools like PowerMock, which make tests more complex and sometimes unstable. In typical cases, a DI-friendly approach with instances is preferred for tests.

Common mistakes and anti-patterns

  • Violation of the single responsibility principle: utilities start storing state.
  • Inheriting or extending utility classes.
  • Using static methods where state management is required (e.g., business services).

Real-life example

Negative case

In the project, all services were implemented using utility static methods. Dependency injection was impossible, unit tests were not isolated, and each test depended on the state of the environment.

Pros:

  • Quick start.
  • Simplicity of calls.

Cons:

  • Lack of testability.
  • Responsibility allocation problems.

Positive case

In services, separate classes with dependency injection through interfaces are used. For transformations and simple operations, separate stateless utility classes are utilized.

Pros:

  • Architectural flexibility, good extensibility.
  • Excellent testability.

Cons:

  • More code required for class definitions and dependency injection.