Item 83: Utilize a inicialização preguiçosa com parcimônia
O que é inicialização preguiçosa? Definição: Adiar a inicialização de um campo até que ele seja acessado pela primeira vez. Benefícios: Evita inicializações desnecessárias se o campo nunca for usado. Aplicações: Usada para campos estáticos e de instância. Práticas Recomendadas e Exemplos Inicialização Normal (Preferível) Simples e eficaz. Exemplo: private final FieldType field = computeFieldValue(); Use inicialização normal para a maioria dos campos, salvo necessidade especial. Inicialização Preguiçosa com Getter Sincronizado Quando usar: Para resolver circularidades de inicialização. Exemplo: private FieldType field; synchronized FieldType getField() { if (field == null) { field = computeFieldValue(); } return field; } 3. Prática da Classe Portadora (Para Campos Estáticos) Quando usar: Inicialização preguiçosa eficiente para campos estáticos. Exemplo: private static class FieldHolder { static final FieldType field = computeFieldValue(); } static FieldType getField() { return FieldHolder.field; } Vantagem: Inicializa a classe apenas quando o campo é acessado, com custo mínimo após a inicialização. 4. Prática de Verificação Dupla (Para Campos de Instância) Quando usar: Para desempenho em inicialização preguiçosa em campos de instância. Exemplo: private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) { // Primeira verificação (sem bloqueio) synchronized (this) { result = field; if (result == null) { // Segunda verificação (com bloqueio) field = result = computeFieldValue(); } } } return result; } 5. Prática de Verificação Única (Inicialização Repetida Permitida) Quando usar: Campos que podem tolerar inicializações repetidas. Exemplo private volatile FieldType field; FieldType getField() { if (field == null) { // Verificação única field = computeFieldValue(); } return field; } 6. Prática Ousada de Verificação Única Quando usar: Apenas se tolerar inicializações extras e se o tipo do campo for um primitivo diferente de long ou double. Exemplo: private FieldType field; FieldType getField() { if (field == null) { // Sem volatile field = computeFieldValue(); } return field; } Considerações Gerais Trade-offs: Inicialização preguiçosa minimiza o custo inicial, mas pode aumentar o custo de acesso ao campo. Avalie com medições de desempenho. Sincronização em Multithread: Fundamental para evitar bugs graves. Utilize práticas seguras (e.g., volatile, bloqueios). Uso Preferencial: Campos Estáticos: Prática da classe portadora. Campos de Instância: Verificação dupla. Inicialização Repetida Permitida: Verificação única. Resumo Final Inicialize normalmente sempre que possível. Use inicialização preguiçosa apenas quando necessário para desempenho ou para resolver problemas de circularidade
O que é inicialização preguiçosa?
- Definição: Adiar a inicialização de um campo até que ele seja acessado pela primeira vez.
- Benefícios: Evita inicializações desnecessárias se o campo nunca for usado.
- Aplicações: Usada para campos estáticos e de instância.
Práticas Recomendadas e Exemplos
- Inicialização Normal (Preferível) Simples e eficaz.
Exemplo:
private final FieldType field = computeFieldValue();
Use inicialização normal para a maioria dos campos, salvo necessidade especial.
- Inicialização Preguiçosa com Getter Sincronizado Quando usar: Para resolver circularidades de inicialização.
Exemplo:
private FieldType field;
synchronized FieldType getField() {
if (field == null) {
field = computeFieldValue();
}
return field;
}
3. Prática da Classe Portadora (Para Campos Estáticos)
- Quando usar: Inicialização preguiçosa eficiente para campos estáticos.
Exemplo:
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
static FieldType getField() {
return FieldHolder.field;
}
Vantagem: Inicializa a classe apenas quando o campo é acessado, com custo mínimo após a inicialização.
4. Prática de Verificação Dupla (Para Campos de Instância)
- Quando usar: Para desempenho em inicialização preguiçosa em campos de instância.
Exemplo:
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // Primeira verificação (sem bloqueio)
synchronized (this) {
result = field;
if (result == null) { // Segunda verificação (com bloqueio)
field = result = computeFieldValue();
}
}
}
return result;
}
5. Prática de Verificação Única (Inicialização Repetida Permitida)
- Quando usar: Campos que podem tolerar inicializações repetidas.
Exemplo
private volatile FieldType field;
FieldType getField() {
if (field == null) { // Verificação única
field = computeFieldValue();
}
return field;
}
6. Prática Ousada de Verificação Única
- Quando usar: Apenas se tolerar inicializações extras e se o tipo do campo for um primitivo diferente de long ou double.
Exemplo:
private FieldType field;
FieldType getField() {
if (field == null) { // Sem volatile
field = computeFieldValue();
}
return field;
}
Considerações Gerais
Trade-offs:
- Inicialização preguiçosa minimiza o custo inicial, mas pode aumentar o custo de acesso ao campo.
- Avalie com medições de desempenho.
Sincronização em Multithread:
- Fundamental para evitar bugs graves.
- Utilize práticas seguras (e.g., volatile, bloqueios).
Uso Preferencial:
- Campos Estáticos: Prática da classe portadora.
- Campos de Instância: Verificação dupla.
- Inicialização Repetida Permitida: Verificação única.
Resumo Final
- Inicialize normalmente sempre que possível.
- Use inicialização preguiçosa apenas quando necessário para desempenho ou para resolver problemas de circularidade