eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this tutorial, we’ll talk about a very interesting feature that Project Valhalla brings to the Java ecosystem, Value-based Classes. Value-based classes were introduced in Java 8 and have gone through major refactors and enhancements in later releases.

2. Value-based Classes

2.1. Project Valhalla

Project Valhalla is an experimental project by OpenJDK to add new features and capabilities to Java. The primary goal of this initiative is to add improved support for value types, generic specialization, and performance improvements while maintaining complete backward compatibility.

Value-based classes are one of the features introduced by Project Valhalla to introduce primitive, immutable values to the Java language without the added overhead that traditional object-oriented classes bring.

2.2. Primitives and Value-Types

Before we come to the formal definition of value-based classes, let’s look at two important semantics in Java – primitives and value types.

Primitive data types, or primitives, in Java, are simple data types that represent a single value and are not objects. Java provides eight such primitive data types: byte, short, int, long, float, double, char, and boolean. While these are simple types, Java provides wrapper classes for each of these for us to interact with them in an object-oriented way.

It is also important to remember that Java performs auto-boxing and unboxing automatically to convert between the object and primitive type efficiently:

List<Integer> list = new ArrayList<>();
list.add(1); // this is autoboxed

Primitive types live on the stack memory, while the objects that we use in our code live on the heap memory.

Project Valhalla introduced a new type in the Java ecosystem that is somewhat between an object and a primitive, and it is termed a value-type. Value types are immutable types, and they do not have any identity. These value types also do not support inheritance.

Value types are not addressed by their reference but by their values, just like primitives.

2.3. Value-Based Classes

Value-based classes are classes that are designed to behave like and encapsulate value-types in Java. The JVM can freely switch between value types and its value-based class, much like auto-boxing and unboxing. Value-based classes are hence, identity free, for the same reason.

3. Properties of Value-Based Classes

Value-based classes are classes that represent simple immutable values. A Value-based class has several properties that can be categorized into some general themes.

3.1. Immutability

Value-based classes are meant to represent immutable data, similar to primitives like int, and have the following characteristics:

  • A value-based class is always final
  • It contains only the final fields
  • The class can extend the Object class or a hierarchy of abstract classes that declare no instance fields

3.2. Object Creation

Let’s understand how creating new objects of value-based classes works:

  • The class does not declare any accessible constructor
  • In case there are accessible constructors, they should be marked as deprecated for removal
  • The class should be instantiated only through factory methods. The instance received from the factory may or may not be a new instance, and the calling code should not make any assumption about its identity

3.3. Identity and equals(), hashCode(), toString() Methods

Value-based classes are identity-free. As they are still classes in Java, we need to understand how methods inherited from Object class happens:

  • The implementations of equals(), hashCode(), and toString() are defined solely based on the values of its instance members and not from their identities, nor any other instance’s state
  • We consider two objects to be equal solely on the objects’ equals() check and not on reference-based equality, i.e. ==
  • We can use two equal objects interchangeably, and they should produce the same result on any computation or method invocation.

3.4. Some Additional Caveats

We should consider some additional limitations while working with value-based classes:

  • Two objects, which are equal based on the equals() method, might be different objects in the JVM or the same
  • We cannot ensure exclusive ownership of the monitor, making instances unsuitable for synchronization

4. Examples of Value-Based Classes

4.1. Value-Based Classes in the JDK

There are several classes in the JDK that follow the Value-based class specification.

When it was first introduced, java.util.Optional and the DateTime API (java.time.LocalDateTime) were value-based classes. As of Java 16 and beyond, Java has defined all the wrapper classes of primitive types such as Integer and Long as value-based.

These classes have the @ValueBased annotation from the jdk.internal package present:

@jdk.internal.ValueBased
public final class Integer extends Number implements Comparable<Integer>, Constable, ConstantDesc {
    // Integer class in the JDK
}

4.2. Custom Value-Based Class

Let’s create our custom class which follows the value-based class specification defined above. For our example, let’s take a Point class which identifies a point in 3D space. The class has 3 integer fields x, y, and z.

We can argue that the Point definition serves as a good candidate for a value-based class because a specific point in space is unique and can be referred to only by its value. It is constant and unambiguous, much like an integer of value 302.

We’ll start by defining the class to be final and its attributes x, y, and z as final. Let’s also make the constructor private:

public final class Point {
    private final int x;
    private final int y;
    private final int z;
    // inaccessible constructor
    private Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    // ...
}

Now, let’s have the origin(0, 0, 0) instance of the class created beforehand, and we return the same instance every time there is a call to create a point with x = 0, y = 0, and z = 0:

private static Point ORIGIN = new Point(0, 0, 0);

We now need to provide an object creation mechanism in the form of a factory method:

public static Point valueOfPoint(int x, int y, int z) {
    // returns a cached instance if it is the origin, or a new instance
    if (isOrigin(x, y, z)) {
        return ORIGIN;
    }
    return new Point(x, y, z);
}

// checking if a point is the origin
private static boolean isOrigin(int x, int y, int z) {
    return x == 0 && y == 0 && z == 0;
}

The factory method valueOfPoint() could return a new instance or a cached one depending on the parameters. This forces the calling code not to make any assumption on the state of the object or compare references of two instances. 

Finally, we should define the equals() method based only on the values of instance fields:

@Override
public boolean equals(Object other) {
    if (other == null || getClass() != other.getClass()) {
        return false;
    }
    Point point = (Point) other;
    return x == point.x && y == point.y && z == point.z;
}

@Override
public int hashCode() {
    return Objects.hash(x, y, z);
}

We now have a class Point, which can behave as a value-based class. We can put the @ValueBased annotation to the class after importing it from jdk.internal package. However, it is not mandatory for our case.

Let’s now test that two instances of the same point in space denoted by (1,2,3) are equal:

@Test
public void givenValueBasedPoint_whenCompared_thenReturnEquals() {
    Point p1 = Point.valueOfPoint(1,2,3);
    Point p2 = Point.valueOfPoint(1,2,3);

    Assert.assertEquals(p1, p2);
}

Additionally, for the sake of this exercise, let’s also see that two instances, if compared by reference, are the same when two origin points are created:

@Test
public void givenValueBasedPoint_whenOrigin_thenReturnCachedInstance() {
    Point p1 = Point.valueOfPoint(0, 0, 0);
    Point p2 = Point.valueOfPoint(0, 0, 0);

    // the following should not be assumed for value-based classes
    Assert.assertTrue(p1 == p2);
}

5. Advantages of Value-Based Classes

Now that we know what value-based classes are and how we can define one, let’s understand why we might need value-based classes at all.

Value-based classes being part of the Valhalla specification, are still in the experimental phase and continue to evolve. Therefore, the benefits of such classes may change over time.

As of now, the most important benefit that comes out of using value-based classes is memory utilization. Value-based classes are more memory efficient as they do not have reference-based identity. Additionally, the JVM can reuse existing instances or create new ones based on the requirements, thereby reducing the memory footprint.

Also, they do not require synchronization, increasing overall performance, especially in multithreaded applications.

6. Difference Between Value-Based Classes and Other Types

6.1. Immutable Classes

Immutable classes in Java share a lot of common ground with Value-based classes. Hence, it is very crucial to understand the differences between them.

While value-based classes are new and part of an ongoing experimental feature, Immutable classes have been a core and integral part of the Java ecosystem for a long time. The String class, Enums, and wrapper classes in Java, such as the Integer class, are examples of immutable classes.

Immutable classes are not identity-free like value-based classes. Instances of Immutable classes having the same state are distinct, and we can compare them based on reference equality. Instances of value-based classes do not have the notion of reference-based equality:

Immutable classes are free to provide accessible constructors and can have multiple attributes and complex behaviors. However, value-based classes represent simple values and do not define complex behavior with dependent attributes.

Finally, we should note that value-based classes are, by definition, immutable but not vice-versa.

6.2. Records

Java introduced the notion of Records in Java 14 as an easy way to pass around immutable data objects. Records and value-based classes fulfill different purposes even if they seem similar in behavior and semantics.

The most noticeable distinction between records and value-based classes is that records have public constructors, while value-based classes lack them.

7. Conclusion

In this article, we talked about value-based classes and the notion of value types in Java. We touched upon the important properties that value-based classes must abide by and the benefits they bring. We also discussed the differences between value-based classes and similar Java concepts, such as immutable classes and records.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments