Oi Leonardo, tudo bem?
Antes de mais nada, acho válido entendermos como funciona essas otimizações por baixo dos panos para que a explicação fique mais clara.
Primeiro, quando declaramos uma String Object, ou seja, uma string criada a partir do operador new()
, como por exemplo String obj = new String("Java")
, estamos criando um novo objeto String num lugar chamado heap memory, que é basicamente, o lugar onde a JVM deixa guardado todos os objetos que vão sendo criados ao decorrer do nosso programa.
Já quando declaramos uma String Literal, ou seja, uma string com sintaxe literal como String literal = "Java"
, antes de mais nada é feita uma busca pela string declarada "Java"
num lugar onde strings são armazenadas chamada string pool - que também fica dentro da heap memory - então, toda vez que uma string literal é criada, a JVM checa se essa string já existe na string pool. Caso ela exista, é retornado a referência para a string, mas caso ela não exista, uma nova string object é inicializada e adicionada na string pool para que futuramente, se outra string literal for criada, não seja necessário inicializar um novo objeto e possamos pegá-la diretamente da string pool.
Aqui temos uma imagem que ilustra isso:
Como podemos ver, se declararmos duas Strings literais a e b e formos compará-las com ==
, estamos comparando dois valores que referenciam o mesmo lugar - ou melhor, a mesma string - por isso é retornado true! Diferente da String object c que foi criada no heap memory e continua apontando para lá sem nunca ter checado a string pool.
Só respondendo brevemente a sua primeira pergunta, a comparação ainda é feita através das referências, o que acontece é que na segunda vez que você declarou a string literal "New test"
, a JVM simplesmente foi até a string pool, achou a string "New test"
que foi declarada anteriormente e passou a apontar para essa mesma referência.
Espero ter ajudado, qualquer dúvida estamos à sua disposição!
Bons estudos!!