The idea behind encapsulation in object-oriented programming is moving data and the code that uses it together.
There is no "but the data are constants, treat them specially." If you have colors, keep them in a
Color class. Java's
Calendar class has calendar field constants in it.
BigInteger each contain constants of their own type.
The idea is that referencing constants involves their scope:
BigDecimal.ZERO for example. I know that is a decimal zero because I typed
BigDecimal.ZERO and not
One of the worst anti-patterns in this regard is having a "constant interface" which is honestly lazy and unclear. Sometimes you come across an interface containing a bunch of random constants thrown together, and classes will implement the interface just to make it easy to refer to the constants. That generally ends up violating LSP and makes it difficult to determine what those constants do.
Constants should be:
An enum if appropriate, such as
java.math.RoundingMode. The Java Calendar fields which were created before
enum existed should be an enum. This necessarily scopes them properly and ensures other constants are not in the same namespace (class static final field).
Located on the class that defines their type, if appropriate. This covers the reusable numbers on
BigDecimal for example.
Located somewhere else where like constants are together and not intermingled with other, unlike constants.
One could argue this is a matter of opinion. I feel that if you asked any group of experts to compare e.g.
BigDecimal and some random AWT constants class, we would come to a consensus that there are some really bad practices in reality and some that make code clearer and easier to understand.