Sou desenvolvedor Java há muito tempo e, finalmente, depois de me formar, tenho tempo para estudá-lo decentemente para fazer o exame de certificação ... Uma coisa que sempre me incomodou é que String é "final". Eu entendo isso quando leio sobre problemas de segurança e coisas relacionadas ... Mas, sério, alguém tem um exemplo verdadeiro disso?
Por exemplo, o que aconteceria se String não fosse final? Como se não estivesse em Ruby. Não recebi nenhuma reclamação da comunidade Ruby ... E estou ciente das StringUtils e classes relacionadas que você precisa implementar ou pesquisar na Web para implementar apenas esse comportamento (4 linhas de código) que você está disposto a.
java.io.File
não fossefinal
. Oh não é. Bugger.Respostas:
O principal motivo é a velocidade: as
final
classes não podem ser estendidas, o que permitiu ao JIT fazer todos os tipos de otimizações ao manipular strings - nunca é necessário verificar se há métodos substituídos.Outro motivo é a segurança do encadeamento: os imutáveis são sempre protegidos por encadeamento, porque um encadeamento precisa construí-los completamente antes de serem passados para outra pessoa - e após a construção, eles não podem mais ser alterados.
Além disso, os inventores do tempo de execução Java sempre quiseram errar no lado da segurança. Ser capaz de estender String (algo que costumo fazer no Groovy porque é muito conveniente) pode abrir uma lata inteira de worms se você não souber o que está fazendo.
fonte
final
como verificação rápida que um método não seja substituído durante a compilação de código.final
palavra-chave na classe. A matriz de caracteres que ele contém é final, tornando-o imutável. Tornar a própria classe final fecha o loop como o @abl menciona, pois uma subclasse mutável não pode ser introduzida.Há outra razão pela qual a
String
classe do Java precisa ser final: é importante para a segurança em alguns cenários. Se aString
classe não fosse final, o código a seguir estaria vulnerável a ataques sutis de um chamador malicioso:O ataque: um chamador malicioso pode criar uma subclasse de String,,
EvilString
ondeEvilString.startsWith()
sempre retorna true, mas onde o valor deEvilString
é algo ruim (por exemplo,javascript:alert('xss')
). Devido à subclasse, isso evitaria a verificação de segurança. Esse ataque é conhecido como vulnerabilidade de tempo de verificação no tempo de uso (TOCTTOU): entre o momento em que a verificação é concluída (que o URL inicia com http / https) e o momento em que o valor é usado (para construir o snippet html), o valor efetivo pode mudar. SeString
não fosse final, os riscos do TOCTTOU seriam generalizados.Portanto, se
String
não for final, fica complicado escrever um código seguro se você não puder confiar no seu chamador. Obviamente, essa é exatamente a posição em que as bibliotecas Java estão: elas podem ser invocadas por applets não confiáveis, para que não se atrevam a confiar no chamador. Isso significa que seria excessivamente complicado escrever código de biblioteca seguro, seString
não fosse final.fonte
char[]
interior da string, mas isso requer alguma sorte no tempo.char[]
dentro da string; portanto, eles não podem explorar a vulnerabilidade TOCTTOU.String
não fosse final. Desde que esse modelo de ameaça seja relevante algumas vezes, torna-se importante se defender. Desde que seja importante para miniaplicativos e outro código não confiável, isso é suficiente para justificar aString
finalização, mesmo que não seja necessário para aplicativos confiáveis. (Em qualquer caso, confiável aplicações já pode anular todas as medidas de segurança, independentemente se é final.)Caso contrário, os aplicativos Java multithread seriam uma bagunça (ainda pior do que realmente é).
IMHO A principal vantagem das seqüências finais (imutáveis) é que elas são inerentemente seguras para threads: elas não requerem sincronização (escrever esse código às vezes é bastante trivial, mas é mais ou menos distante disso). Se eles fossem mutáveis, garanta que coisas como kits de ferramentas do Windows multithread seriam muito difíceis, e essas coisas são estritamente necessárias.
fonte
final
classes não têm nada a ver com a mutabilidade da classe.Outra vantagem de
String
ser final é que ele garante resultados previsíveis, assim como a imutabilidade.String
, embora seja uma classe, deve ser tratado em alguns cenários, como um tipo de valor (o compilador até suportaString blah = "test"
). Portanto, se você criar uma string com um determinado valor, espera que ele tenha um comportamento herdado de qualquer string, semelhante às expectativas que você teria com números inteiros.Pense sobre isso: e se você subclasse
java.lang.String
e substituiu os métodosequals
ouhashCode
? De repente, duas "cordas" de teste não podiam mais se igualar.Imagine o caos se eu pudesse fazer isso:
alguma outra classe:
fonte
fundo
Existem três lugares onde final pode aparecer em Java:
A finalização de uma aula evita todas as subclasses da classe. A finalização de um método impede que subclasses do método o substituam. Tornar um campo final evita que seja alterado posteriormente.
Equívocos
Há otimizações que acontecem em torno dos métodos e campos finais.
Um método final facilita a otimização do HotSpot via inlining. No entanto, o HotSpot faz isso mesmo que o método não seja final, pois trabalha com a suposição de que não foi substituído até prova em contrário.Mais sobre isso no SO
Uma variável final pode ser otimizada agressivamente, e mais sobre isso pode ser lido na seção 17.5.3 do JLS . .
No entanto, com esse entendimento, deve-se estar ciente de que nenhuma dessas otimizações é sobre fazer uma classe final. Não há ganho de desempenho ao final da aula.
O aspecto final de uma classe também não tem nada a ver com imutabilidade. Pode-se ter uma classe imutável (como BigInteger ) que não é final ou uma classe que é mutável e final (como StringBuilder ). A decisão sobre se uma classe deve ser final é uma questão de design.
Design final
Strings são um dos tipos de dados mais usados. Eles são encontrados como chaves para mapas, armazenam nomes de usuário e senhas, são o que você lê de um teclado ou de um campo em uma página da web. As cordas estão em toda parte .
Mapas
A primeira coisa a considerar sobre o que aconteceria se você pudesse subclassificar String é perceber que alguém poderia construir uma classe String mutável que, de outra forma, pareceria uma String. Isso atrapalhava o Google Maps em todos os lugares.
Considere este código hipotético:
Esse é um problema com o uso de uma chave mutável em geral com um mapa, mas o que estou tentando entender é que, de repente, várias coisas sobre a quebra do mapa. A entrada não está mais no ponto certo no mapa. Em um HashMap, o valor do hash mudou (deveria ter) alterado e, portanto, não está mais na entrada correta. No TreeMap, a árvore agora está quebrada porque um dos nós está do lado errado.
Como o uso de uma String para essas chaves é tão comum, esse comportamento deve ser evitado, tornando a String final.
Você pode estar interessado em ler Por que o String é imutável em Java? para saber mais sobre a natureza imutável das Strings.
Cordas nefastas
Existem várias opções nefastas para Strings. Considere se eu criei uma String que sempre retornava verdadeira quando igual era chamado ... e passava isso para uma verificação de senha? Ou fez com que as atribuições para MyString enviassem uma cópia da String para algum endereço de email?
Essas são possibilidades muito reais quando você pode subclassificar String.
Otimizações de Java.lang String
Enquanto antes mencionei que final não faz String mais rápido. No entanto, a classe String (e outras classes em
java.lang
) fazem uso frequente da proteção de campos e métodos no nível do pacote para permitir que outrasjava.lang
classes possam mexer com os internos, em vez de passar pela API pública do String o tempo todo. Funções como getChars sem verificação de intervalo ou lastIndexOf que é usado por StringBuffer, ou o construtor que compartilha a matriz subjacente (observe que isso é uma coisa do Java 6 que foi alterada devido a problemas de memória).Se alguém fizesse uma subclasse de String, não seria possível compartilhar essas otimizações (a menos que fizesse parte
java.lang
também, mas esse é um pacote selado ).É mais difícil projetar algo para extensão
Projetar algo para ser extensível é difícil . Isso significa que você precisa expor partes de seus componentes internos para que outra coisa possa ser modificada.
Uma String extensível não poderia ter seu vazamento de memória corrigido. Essas partes precisariam ter sido expostas a subclasses e a alteração desse código significaria que as subclasses iriam quebrar.
O Java se orgulha da compatibilidade com versões anteriores e, ao abrir as classes principais para extensão, perde-se parte dessa capacidade de consertar as coisas, mantendo a computabilidade com subclasses de terceiros.
O Checkstyle possui uma regra que ela aplica (o que realmente me frustra ao escrever código interno) chamada "DesignForExtension", que impõe que todas as classes sejam:
O racional para o qual é:
Permitir que as classes de implementação sejam estendidas significa que as subclasses podem corromper o estado da classe em que se baseia e torná-lo de modo que várias garantias que a superclasse fornece sejam inválidas. Para algo tão complexo como String, é quase uma certeza que a mudança de parte dela vai quebrar alguma coisa.
Desenvolvedor hurbis
É parte de ser um desenvolvedor. Considere a probabilidade de que cada desenvolvedor crie sua própria subclasse String com sua própria coleção de utilitários . Mas agora essas subclasses não podem ser livremente atribuídas uma à outra.
Este caminho leva à loucura. Transmitindo para String em todo o lugar e verificando se a String é uma instância do seu classe String ou não, criando uma nova String com base nessa e ... apenas, não. Não.
Tenho certeza que você pode escrever uma classe bom string ... mas deixar de escrever várias implementações de cadeia para essas pessoas loucas que escrevem C ++ e tem que lidar com
std::string
echar*
e algo de impulso e sString , e todo o resto .Java String magic
Existem várias coisas mágicas que Java faz com Strings. Isso facilita o trabalho do programador, mas introduz algumas inconsistências no idioma. Permitir subclasses em String exigiria uma reflexão muito significativa sobre como lidar com essas coisas mágicas:
Literais de sequência (JLS 3.10.5 )
Ter um código que permita:
Isso não deve ser confundido com o boxe de tipos numéricos como Inteiro. Você não pode fazer
1.toString()
, mas você pode fazer"foo".concat(bar)
.O
+
operador (JLS 15.18.1 )Nenhum outro tipo de referência em Java permite que um operador seja usado nele. A String é especial. O operador de concatenação também trabalha no nível do compilador de modo que
"foo" + "bar"
se torna"foobar"
quando ele é compilado em vez de em tempo de execução.Conversão de Cadeia de Caracteres (JLS 5.1.11 )
Todos os objetos podem ser convertidos em Strings usando-os em um contexto de String.
Internação de strings ( JavaDoc )
A classe String tem acesso ao pool de Strings que permite ter representações canônicas do objeto que é preenchido no tipo de compilação com os literais String.
Permitir uma subclasse de String significaria que esses bits com a String que facilitam a programação se tornariam muito difíceis ou impossíveis de executar quando outros tipos de String forem possíveis.
fonte
Se String não fosse final, cada baú de ferramentas de programador conteria seu próprio "String com apenas alguns métodos auxiliares agradáveis". Cada um deles seria incompatível com todos os outros baús de ferramentas.
Eu considerei uma restrição tola. Hoje estou convencido de que foi uma decisão muito sólida. Mantém Strings para serem Strings.
fonte
final
em Java não é a mesma coisa quefinal
em c #. Nesse contexto, é a mesma coisa que c # 'sreadonly
. Veja stackoverflow.com/questions/1327544/…public final class String
.