Casos em que objetos desnecessários são criados
Usando new String()
As strings a, b e c contêm todas a string “hi”. No entanto, como os endereços referenciados por essas três strings são diferentes, ocorre o desperdício de alocar memórias separadas para os mesmos dados.
<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa53d14aa-6abf-40f0-8441-435647d172fa%2FUntitled.png?table=block&id=f168859c-367c-4924-a4c6-5e04788fab67&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1146;" width="2000" height="1146"></span>
Portanto, ao declarar strings, em vez de usar a palavra-chave new, é melhor declará-las como literais.
O código acima usa apenas uma instância. Além disso, ao usar esse método, garante-se que todos os códigos que usam o literal de string “hi” dentro da mesma JVM reutilizem o mesmo objeto. Isso ocorre devido a uma característica do pool de constantes do Java.
Usando new Boolean()
O código acima cria uma instância Boolean por meio de um construtor que recebe uma string como parâmetro. Boolean só pode ser true ou false, e criar uma instância toda vez é um desperdício de memória. Portanto, é melhor usar o método de fábrica estático Boolean.valueOf().
Usando String.matches()
Se o custo de criação for alto, é melhor armazená-lo em cache e reutilizá-lo, mas nem sempre sabemos o custo do objeto que estamos criando. Por exemplo, se você quiser escrever um método que verifica se uma determinada string é um número romano válido, usar expressões regulares é a maneira mais fácil.
No entanto, String.matches() é um método problemático em termos de desempenho. A instância Pattern para expressões regulares criada internamente por esse método é usada uma vez e descartada, tornando-se um alvo para coleta de lixo imediatamente. À medida que a frequência de uso da expressão regular aumenta, o custo de criar e descartar a mesma instância Pattern aumenta. Portanto, é melhor armazenar em cache a instância Pattern com antecedência e reutilizá-la sempre que o método isRomanNumeral() for chamado.
**Observação**
Em todos os exemplos acima, onde objetos desnecessários são armazenados em cache, eles são criados como objetos imutáveis. Isso ocorre porque só assim podemos reutilizá-los com segurança. No entanto, existem casos em que a ideia de reutilizar objetos imutáveis é contrária à intuição.
Um adaptador (visualização) é um objeto que delega o trabalho real para um objeto back-end e atua como uma segunda interface. Como o adaptador precisa apenas gerenciar o objeto back-end, um adaptador é suficiente para cada objeto back-end.
Por exemplo, o método keySet() da interface Map retorna uma exibição Set que contém todas as chaves no objeto Map. Os usuários podem pensar que uma nova instância Set é criada sempre que o método keySet() é chamado, mas na verdade, a implementação do JDK retorna a mesma instância Set mutável toda vez.
Isso ocorre porque, embora a instância Set retornada seja mutável, todas as funções que ela executa são as mesmas e todas as instâncias Set representam a instância Map. Portanto, não há problema em keySet() criar várias exibições de objeto, mas não há necessidade nem benefício em fazê-lo.
Portanto, se modificarmos a instância names1, a instância names2 também será afetada.
No entanto, pessoalmente, acredito que o valor de retorno do método keySet() deve usar cópia defensiva para retornar uma nova instância toda vez. Se a instância Set recebida pelo método keySet() também estiver sendo usada em outro lugar e houver código que modifique o estado dessa instância, não poderemos ter certeza do valor da instância Set e da instância Map atualmente em uso.
Além disso, a menos que o ambiente use keySet() com muita frequência, a criação de uma nova interface Set a cada vez não terá um impacto significativo no desempenho. Em vez disso, acho que é melhor tornar a interface Set um objeto imutável para que a manutenção seja estável.
Autoboxing
Autoboxing é uma técnica que converte automaticamente tipos primitivos e tipos wrapper quando os programadores os misturam. No entanto, o autoboxing apenas torna a distinção entre tipos primitivos e tipos wrapper obscura, mas não a elimina completamente.
Logicamente, não há problema, mas o código é muito ineficiente em termos de desempenho. A causa disso está nos tipos de sum e i dentro do loop for.
O tipo de sum é Long e o tipo de i é long. Em outras palavras, a cada iteração do loop, quando o tipo long i é adicionado a sum, uma nova instância Long é criada. Como resultado, devemos usar tipos primitivos em vez de tipos wrapper e ter cuidado para evitar autoboxing desnecessário.
Partes que não devem ser mal interpretadas
Não devemos interpretar erroneamente a ideia de evitar a criação desnecessária de objetos como simplesmente evitar a criação de objetos porque eles são caros.
Em particular, nas JVMs atuais, criar e coletar pequenos objetos criados desnecessariamente não é uma tarefa muito onerosa. Portanto, a menos que seja um objeto com alto custo, como uma conexão de banco de dados, não crie um pool de objetos personalizado.
Além disso, lembre-se de que o dano causado pela reutilização de objetos em situações em que a cópia defensiva é necessária é muito maior do que o dano causado pela criação repetida de objetos desnecessários. Os efeitos colaterais da criação repetida afetam apenas a forma e o desempenho do código, mas se a cópia defensiva falhar, ela levará diretamente a bugs e problemas de segurança.
Comentários0