Baeldung

Java, Spring and Web Development tutorials

 

Fix Sonar’s “Make Transient or Serializable” Warning in Java
2026-03-31 15:43 UTC by Andrei Branza

1. Introduction

When working with Java’s native serialization combined with SonarQube’s code evaluation tool, sometimes we encounter “Fields in a ‘Serializable’ class should either be ‘transient’ or ‘Serializable'” (rule key: java:S1948) warning. This rule is an important guardrail and it prevents a common runtime failure: the NotSerializableException.

In this tutorial, we’ll explore why this warning occurs. Also, we’ll talk about the underlying mechanics of Java serialization, and the best strategies to resolve it.

2. Understanding the Serialization Contract

To make a Java object serializable, its class must implement the java.io.Serializable interface. This is a marker interface that tells the JVM the object’s state is converted into a byte stream for storage or network transfer.

As such, the fundamental rule of this contract is recursive: if a class is serializable, all its non-static and non-transient member fields must also be serializable. If the serialization mechanism encounters a field that doesn’t implement Serializable and isn’t marked as transient, the process will fail at runtime. Sonar flags these fields during static analysis and helps us catch this error before the code even runs.

In addition, it’s important to remember that serialization isn’t just about the top-level class; it’s about the entire object graph.

3. Reproducing the Sonar Warning

Let’s look at a typical scenario that triggers the java:s1948 warning. First, let’s add the SLF4J dependency to our pom.xml:

<dependency>
    <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>2.0.17</version>
</dependency>

Now, let’s create a User class that we want to store in a distributed session or cache. This class also contains a reference to another class, Address, which does not implement the Serializable interface. We’ll start with a simple implementation:

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
         
    private String username;
    private Address address; // Sonar Warning: Make "address" transient or serializable
    private final Logger logger = LoggerFactory.getLogger(User.class); // Sonar Warning
         
     public User(String username, Address address) {
        this.username = username;
        this.address = address;
     }
}

In this example, the User class correctly implements Serializable. However, if the Address class is defined without the interface, Sonar will flag the address field. Similarly, since the SLF4J Logger does not implement Serializable, it will also trigger the warning. Furthermore, if we try to serialize an instance of User, the JVM will throw a NotSerializableException at runtime. This is exactly what Sonar is trying to help us avoid. Now, let’s look at some ways we can tackle this warning.

4. Making the Field Serializable

The most straightforward fix is to ensure that the nested class also implements the Serializable interface. This is the preferred approach when the field represents a core part of the object’s state that must be preserved. As such, if we own the source code of the nested object, we should simply update the class definition:

public class Address implements Serializable {
    private static final long serialVersionUID = 1L;
        
    private String street;
    private String city;
        
    public Address(String street, String city) {
        this.street = street;
        this.city = city;
    }
}

By adding implements Serializable and providing a serialVersionUID, we satisfy the contract requirements. It’s a best practice to always include serialVersionUID to ensure compatibility during the deserialization process. Especially if the class structure evolves over time.

5. Using the static Modifier

Another effective way to resolve the warning for certain fields is to declare them as static. In Java, static fields are not serialized because they belong to the class itself rather than a specific instance. Since they are excluded from the serialization process by the JVM, SonarQube does not flag them.

This is the standard solution for loggers and constants:

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
       
    private static final Logger logger = LoggerFactory.getLogger(User.class); // No Warning
    private String username;
    private Address address;
    // getters, setters ...
 }

By making the logger static, the warning disappears, and we follow the common Java pattern for logger declarations. This approach is ideal for any field that is shared across all instances of the class and does not represent the unique state of an individual object.

6. Leveraging the transient Keyword

If a field is instance-specific but shouldn’t be serialized (like a reference to a temporary cache, or a non-serializable third-party object) we use the transient keyword. This modifier tells the JVM to skip the field during the serialization process:

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
       
    private String username;
    private Address address;
    private transient List<String> temporaryCache; // Warning Resolved
    // getters and setters ...
}

We need to take into consideration that when an object is deserialized, all transient fields are initialized to their default values: null for objects and 0 for primitives. As such, if a transient field is required for the object to function after being restored, we must re-initialize it. A way to achieve this is to use the readObject method:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    this.temporaryCache = new ArrayList<>();
}

7. Handling Framework Dependencies

In modern Java frameworks like Spring, this warning often appears in @SessionScoped beans when injecting a @Service or @Repository. Since these services are managed by the container and typically aren’t serializable, they should be marked as transient:

@SessionScoped
public class UserPreferences implements Serializable {
    @Autowired
    private transient PreferenceService service; // Fixed by marking it with transient
}

Spring handles the dependency injection, so it will re-inject the service when the bean is restored from the session, provided the framework’s proxy mechanism supports it. This keeps our session-scoped beans serializable while allowing them to interact with stateless services.

Furthermore, we might encounter situations where we use third-party library classes that do not implement Serializable. If we cannot modify their source code, we have two main options:

  • Wrap the object: Create a serializable DTO that holds only the necessary primitive data.\
  • Custom Serialization: Use transient for the third-party object and manually handle its state during serialization using the writeObject and readObject methods.

For example, if we have a non-serializable Metadata object, we can store its state as a serializable Map or and reconstruct it upon deserialization. This keeps our domain models serializable while still utilizing powerful third-party tools.

8. Conclusion

In this tutorial, we’ve covered how to handle the Sonar warning “Make Transient or Serializable”. Although the warning is helpful to ensure runtime stability in Java applications, it can cause confusion. As such, by understanding the recursive nature of Java serialization, we can choose the correct fix based on the field’s role in our application.

Implement Serializable if the field is a core part of the object’s state. Use static for loggers, constants and shared class-level members. Mark as transient if the field is a resource, or temporary data. Refactor the design if we’re trying to serialize stateless services or third-party objects that aren’t meant to be persisted.

The post Fix Sonar’s “Make Transient or Serializable” Warning in Java first appeared on Baeldung.
       

 

Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative.