I believe that there are two related uses of the canonical: forms and instances.
A canonical form means that the values ​​of a particular type of resource can be described or represented in several ways, and one of these methods is selected as the preferred canonical form. (This form is canonized, like books that turned it into a bible, but other forms are not.) A classic example of a canonical form is paths in a hierarchical file system, where a single file can be referenced in several ways
myFile.txt
The classic definition of the canonical representation of this file will be the last path. With local or relative paths, you cannot globally identify a resource without contextual information. With absolute paths, you can identify a resource, but you cannot determine whether two paths belong to the same object. If two or more paths are converted to their canonical forms, you can do all of the above, and determine whether the two resources are the same or not, if it is important for your application (solve the smoothing problem).
Note that the canonical form of the resource is not a quality of this particular form itself; there can be several possible canonical forms for a given type, for example, paths to files (say, lexicographically, primarily possible absolute paths). One form is simply chosen as the canonical form for a specific reason for use, or maybe arbitrarily, so that everyone speaks the same language.
Forcing objects into their canonical instances is one and the same basic idea, but instead of defining one “best” representation of the resource, he arbitrarily selects one instance of the instance class with the same “contents” as the canonical reference, then converts all references to equivalent objects for use of one canonical instance.
This can be used as a method of optimizing time and space. If there are several instances of equivalent objects in the application, then, forcing them to solve everything as the only canonical instance of a certain value, you can exclude all but one of the values, saving space and, possibly, time, since now you can compare those values ​​with the reference identifier (==) as opposed to equivalence of objects ( equals() method).
A classic example of optimizing performance with canonical instances is folding lines with the same content. Calling String.intern() for two strings with the same sequence of characters is guaranteed to return the same canonical String object for this text. If you pass all your lines through this canonizer, you know that equivalent lines are actually identical references to objects, i.e. Aliases
Enumeration types in Java 5.0+ force all instances of a particular enumeration value to use the same canonical instance inside a virtual machine, even if the value is serialized and deserialized. This is why you can use if (day == Days.SUNDAY) with impunity in java if Days is an enum type. Doing this for your own activities is certainly possible, but takes care. Read effective Java from Josh Bloch for details and tips.