![translation](https://cdn.durumis.com/common/trans.png)
Esta é uma postagem traduzida por IA.
Selecionar idioma
Texto resumido pela IA durumis
- Criar instâncias de String ou Boolean usando a palavra-chave new é um desperdício de memória, portanto, é melhor declará-las como literais ou usar o método Boolean.valueOf().
- O método String.matches() usa expressões regulares, o que pode causar problemas de desempenho, portanto, é melhor armazenar em cache a instância Pattern para reutilização.
- Quando um objeto de visualização é retornado, como o método keySet(), é mais seguro usar uma cópia defensiva para retornar um novo objeto.
Quando criar objetos desnecessários
Usando new String()
String a = new String("hi");
String b = new String("hi");
As strings a, b e c conterão todas a string "hi". No entanto, como os endereços referenciados por essas três strings são todos diferentes, ocorre um desperdício de alocação de memória separada para os mesmos dados.
Portanto, ao declarar strings, você deve usar literais em vez da palavra-chave new.
String a = "hi";
String b = "hi";
O código acima usa apenas uma instância. Além disso, usando esse método, garante que todos os códigos que usam o literal de string "hi" dentro da mesma JVM reutilizarão o mesmo objeto. Isso ocorre devido à característica do pool de constantes Java.
Usando new Boolean()
O código acima cria uma instância Boolean usando o 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()
É melhor usar cache e reutilizar se o custo de criação for alto, mas nem sempre podemos saber o custo do objeto que estamos criando. Por exemplo, se quisermos escrever um método que verifica se uma string fornecida é um numeral romano válido, usar uma expressão regular é a maneira mais fácil, como esta.
public static boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
No entanto, String.matches() é um método problemático em termos de desempenho. A instância Pattern criada internamente por esse método para expressões regulares é usada uma vez e descartada, tornando-se um alvo de coleta de lixo imediatamente. Se essa expressão regular for usada repetidamente com alta frequência, o custo de criação e descarte da mesma instância Pattern aumentará. Portanto, é melhor armazenar em cache a instância Pattern com antecedência e reutilizá-la sempre que o método isRomanNumeral() for chamado.
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile(
"^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
public static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
Observação
Em todos os exemplos acima, criamos todos os objetos desnecessários em cache como objetos imutáveis. Isso ocorre porque é seguro reutilizá-los. No entanto, existem casos em que a ideia de reutilizar como um objeto imutável é contrária à intuição.
Um adaptador (visualização) é um objeto que delega o trabalho real para um objeto de back-end e atua como uma segunda interface. O adaptador só precisa gerenciar o objeto de back-end, portanto basta criar um adaptador para cada objeto de back-end.
Por exemplo, o método keySet() da interface Map retorna uma visualização Set que contém todas as chaves dentro do objeto Map. Os usuários podem pensar que uma nova instância Set é criada a cada chamada do método keySet(), mas ao observar o conteúdo real da implementação do JDK, uma instância Set mutável é retornada sempre.
Isso ocorre porque, embora a instância Set retornada seja mutável, as funções executadas são todas as mesmas e todas as instâncias Set representam a instância Map. Portanto, não importa se o keySet() criar várias visualizações de objetos, mas não há necessidade nem benefício.
public class UsingKeySet {
public static void main(String[] args) {
Map menu = new HashMap<>();
menu.put("Burger", 8);
menu.put("Pizza", 9);
Set names1 = menu.keySet();
Set names2 = menu.keySet();
names1.remove("Burger");
System.out.println(names1.size()); // 1
System.out.println(names2.size()); // 1
}
Portanto, se você modificar a instância names1 como acima, a instância names2 também será afetada.
No entanto, pessoalmente, acredito que o valor retornado pelo método keySet() deve usar uma cópia de segurança defensiva e retornar um novo objeto a cada vez. Se a instância Set recebida pelo método keySet() também estiver sendo usada em outro lugar e houver código que altere o estado dessa instância, não poderemos ter certeza sobre o valor da instância Set e da instância Map atualmente em uso.
Além disso, a menos que o keySet() seja usado excessivamente, a interface Set não ser criada a cada vez não terá um impacto mortal no desempenho. Acredito que é melhor criar a interface Set como um objeto imutável para que ela seja mantida e manutenida de forma estável.
Autoboxing
Autoboxing é uma técnica que converte automaticamente tipos básicos e tipos wrapper para frente e para trás quando um programador mistura os dois. No entanto, o autoboxing apenas confunde a distinção entre tipos básicos e tipos wrapper, não a elimina completamente.
public static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
Não há problemas com a lógica, mas o código é muito ineficiente em termos de desempenho. Isso ocorre devido ao tipo sum e ao tipo i no loop for.
O tipo sum é Long e o tipo i é long. Ou seja, o tipo long i cria uma nova instância Long a cada vez que é adicionado a sum durante a iteração do loop. Como resultado, você deve usar tipos básicos em vez de tipos wrapper e ter cuidado para que o autoboxing não seja usado sem querer.
Pontos a serem lembrados
Evite criar objetos desnecessários não significa simplesmente que você deve evitar criar objetos porque o custo de criação de objetos é alto.
Especialmente no JVM atual, criar e coletar objetos pequenos gerados desnecessariamente não é uma tarefa muito pesada. Portanto, a menos que seja um objeto muito caro, como uma conexão de banco de dados, não crie um pool de objetos personalizado.
Além disso, lembre-se que o dano causado ao reutilizar objetos quando uma cópia de segurança defensiva é necessária é muito maior do que o dano causado ao criar objetos desnecessários repetidamente. Os efeitos colaterais da criação repetida afetam apenas a forma do código e o desempenho, mas o fracasso da cópia de segurança defensiva leva diretamente a bugs e problemas de segurança.