Este é um bom artigo que descreve dois motivos já mencionados nas respostas acima:
- Segurança : o sistema pode distribuir bits confidenciais de informações somente leitura sem se preocupar com a alteração.
- Desempenho : dados imutáveis são muito úteis para tornar as coisas mais seguras.
E esse provavelmente é o comentário mais detalhado nesse artigo. Tem a ver com o pool de strings em Java e questões de segurança. É sobre como decidir o que entra no pool de strings. Supondo que as duas seqüências de caracteres sejam iguais se a sequência de caracteres for a mesma, teremos uma condição de corrida para quem chegar lá primeiro e, juntamente com isso, problemas de segurança. Caso contrário, o conjunto de cadeias conterá cadeias redundantes, perdendo a vantagem de tê-lo em primeiro lugar. Basta ler por si mesmo, sim?
Estender String causaria estragos com iguais e estagiários. JavaDoc diz que é igual a:
Compara essa sequência com o objeto especificado. O resultado é verdadeiro se, e somente se, o argumento não for nulo e for um objeto String que represente a mesma sequência de caracteres que esse objeto.
Supondo que java.lang.String
não fosse final, a SafeString
poderia ser igual a String
e vice-versa; porque eles representariam a mesma sequência de caracteres.
O que aconteceria se você se inscrevesse intern
em um SafeString
- entraria SafeString
no conjunto de cadeias de caracteres da JVM? Os ClassLoader
objetos e todos os objetos às quais as SafeString
referências retidas seriam bloqueados durante a vida útil da JVM. Você obteria uma condição de corrida sobre quem poderia ser o primeiro a estagiar uma sequência de personagens - talvez você SafeString
ganhasse, talvez um String
, ou talvez um SafeString
carregado por um carregador de classe diferente (portanto, uma classe diferente).
Se você vencesse a corrida na piscina, este seria um verdadeiro singleton e as pessoas poderiam acessar todo o seu ambiente (sandbox) através de reflexão e secretKey.intern().getClass().getClassLoader()
.
Ou a JVM poderia bloquear esse furo, certificando-se de que apenas objetos String concretos (e nenhuma subclasse) fossem adicionados ao pool.
Se igual foi implementado de forma que SafeString
! = String
Então SafeString.intern
! = String.intern
, E SafeString
teria que ser adicionado ao pool. A piscina se tornaria uma piscina em <Class, String>
vez de <String>
e tudo o que você precisaria para entrar na piscina seria um novo carregador de classe.