Acredito que haja dois usos relacionados de canônico: formas e instâncias.
Uma forma canônica significa que os valores de um tipo específico de recurso podem ser descritos ou representados de várias maneiras, e uma delas é escolhida como a forma canônica preferida. (Essa forma é canonizada , como os livros que entraram na Bíblia, e as outras formas não.) Um exemplo clássico de uma forma canônica são caminhos em um sistema de arquivos hierárquico, onde um único arquivo pode ser referenciado de várias maneiras :
myFile.txt # in current working dir
../conf/myFile.txt # relative to the CWD
/apps/tomcat/conf/myFile.txt # absolute path using symbolic links
/u1/local/apps/tomcat-5.5.1/conf/myFile.txt # absolute path with no symlinks
A definição clássica da representação canônica desse arquivo seria o último caminho. Com caminhos locais ou relativos, você não pode identificar globalmente o recurso sem informações contextuais. Com caminhos absolutos, você pode identificar o recurso, mas não pode dizer se dois caminhos se referem à mesma entidade. Com dois ou mais caminhos convertidos em suas formas canônicas, você pode fazer tudo acima, além de determinar se dois recursos são iguais ou não, se isso é importante para o seu aplicativo (resolva o problema de aliasing ).
Observe que a forma canônica de um recurso não é uma qualidade dessa forma particular; pode haver várias formas canônicas possíveis para um determinado tipo, como caminhos de arquivo (digamos, lexicograficamente antes de todos os caminhos absolutos possíveis). Um formulário é apenas selecionado como o formulário canônico para um motivo de aplicação específico, ou talvez arbitrariamente para que todos falem a mesma língua.
Forçar objetos em suas instâncias canônicas é a mesma ideia básica, mas em vez de determinar uma "melhor" representação de um recurso, ele escolhe arbitrariamente uma instância de uma classe de instâncias com o mesmo "conteúdo" da referência canônica e, em seguida, converte todas as referências a objetos equivalentes para usar a única instância canônica.
Isso pode ser usado como uma técnica para otimizar o tempo e o espaço. Se houver várias instâncias de objetos equivalentes em um aplicativo, ao forçar todos eles a serem resolvidos como a única instância canônica de um valor específico, você pode eliminar todos, exceto um de cada valor, economizando espaço e possivelmente tempo, pois agora você pode comparar aqueles valores com identidade de referência (==) em oposição à equivalência de objeto ( equals()método).
Um exemplo clássico de otimização de desempenho com instâncias canônicas é o recolhimento de strings com o mesmo conteúdo. Chamar String.intern()duas strings com a mesma sequência de caracteres certamente retornará o mesmo objeto String canônico para aquele texto. Se você passar todas as suas strings através desse canonicalizador, você saberá que strings equivalentes são, na verdade, referências a objetos idênticos, ou seja, aliases
Os tipos de enum em Java 5.0+ forçam todas as instâncias de um determinado valor de enum a usar a mesma instância canônica em uma VM, mesmo se o valor for serializado e desserializado. É por isso que você pode usar if (day == Days.SUNDAY)impunemente em java se Daysfor um tipo enum. Certamente é possível fazer isso em suas próprias aulas, mas tome cuidado. Leia Effective Java de Josh Bloch para obter detalhes e conselhos.