Escolar Documentos
Profissional Documentos
Cultura Documentos
Este artigo trata sobre criação e destruição de objetos: quando e como criá-los ou evitar criá-los, como garantir que são
destruídos no tempo correto e como gerenciar qualquer ação de cleanup que precisa preceder a destruição de um objeto.
Uma segunda vantagem dos static factory methods sobre os construtores é eles não precisarem criar um novo objeto
cada vez que são chamados, permitindo um maior controle das instâncias criadas ou até mesmo fazer um cache de
objetos. Assim é possível, por exemplo, garantir que duas instâncias são iguais - a.equals(b) - se e somente se a ==
b, e então os clientes podem utilizar esta segunda forma para testar igualdade, ganhando performance.
Uma terceira vantagem dos static factory methods é eles poderem retornar um objeto de qualquer subtipo do seu tipo de
retorno, aumentando a flexibilidade. Desta forma, estas subclasses podem não constar na API, tornando-a mais
compacta e simples de entender. Esta técnica é utilizada em frameworks baseadas em interfaces, que são tipos de
retorno naturais para estes métodos, e ainda garante que o cliente também estará utilizando as interfaces, o que é outra
boa prática. É até possível criar soluções mais elaboradas, como a encontrada na JCE (Java Cryptography Extension),
em que as classes são instanciadas de acordo com o parâmetro passado para o método.
A principal desvantagem do uso de static factory methods substituindo os construtores é que não é possível herdar uma
classe sem que ela tenha um construtor visível externamente. Porém isto pode ser visto como uma certa vantagem, uma
vez que favorece composição ao invés de herança.
Uma segunda desvantagem é que eles não são facilmente distinguidos de outros métodos estáticos, ficando mais difícil
entender na documentação da API como instanciar uma classe que utiliza static factory methods ao invés de
construtores. Algumas convenções estão sendo criadas para endereçar esse problema, sendo que dois nomes são comuns
ajudam outras classes, tal como acontece na java.lang.Math. Essas classes não foram criadas para serem
instanciadas, mas se não for colocado nenhum construtor o compilador automaticamente criará um, permitindo que
sejam criados os objetos. Para evitar isso, inclua um construtor privado. Note que simplesmente declarar a classe
abstrata não funcionaria, pois daria a impressão de que ela foi criada para ser estendida e as subclasses seriam
instanciáveis. Como dito anteriormente, se a classe possuir apenas um construtor privado, não será possível estendê-la.
instância de String funcionalmente idêntica a qualquer objeto criado pelo construtor. Portanto, o correto seria
fazer simplesmente o seguinte:
Para outros casos, podem ser utilizados static factory methods ao invés de construtores para atingir este objetivo, como
no exemplo da classe Boolean acima, cujo método valueOf retorna um objeto já existente.
Outra situação seria quando dentro de um método que é chamado várias vezes você cria objetos como o Calendar ou
TimeZone, mas na verdade seus valores são fixos durante o ciclo de vida do programa e bastaria que fossem criados
quando é inicializada a classe (em um bloco static) ou quando são usados pela primeira vez.
Porém, essa dica não deve ser levada ao extremo. Objetos pequenos cujo construtor possui pouco processamento não
representam muito overhead de performance, então não há problema em criá-los se isso irá melhorar a clareza ou
simplicidade do código. Já para casos como a conexão em um banco de dados, cuja criação tem um custo elevado,
compensa até utilizar mecanismos como um pool de objetos que são reutilizados.
É importante ressaltar também que é melhor criar um novo objeto, mesmo sem necessidade, do que reutilizar um objeto
quando deveria ser criado um novo, pois isso poderá levar a sérios bugs no programa.
que ele não será mais utilizado. Uma maneira melhor de implementar o método pop seria:
solucionável com um WeakHashMap. Mas na maioria dos casos é necessário recorrer a uma thread que faça a
limpeza dos objetos já não utilizados regularmente ou então remover os registros mais antigos conforme são
Considerando que a especificação da linguagem Java não garante a execução do método finalize, e muito menos o
tempo em que ela irá ocorrer, é importante não depender da execução deste método. É melhor criar um método que
deva ser explicitamente chamado pelo cliente quando o objeto não estiver mais sendo usado, como o close() da
classe InputStream. O finalize deve ser implementado apenas como uma garantia a mais para a liberação de
recursos, não como a principal solução.
Outro motivo para evitar este método é que se ocorrer qualquer exceção dentro do mesmo ela é simplesmente ignorada,
podendo deixar outros objetos em estado corrompido sem nem ao menos avisar.
Um uso legítimo de finalizadores é no caso de métodos nativos delegando tarefas a objetos nativos, pois estes estão fora
do escopo do garbage collector e por isso não são recolhidos automaticamente por ele. Porém, se o objeto retém
recursos críticos, é necessário ter um método explícito para liberá-los.
Outro ponto importante é que, diferentemente dos construtores, não existe uma chamada automática do
método finalize da superclasse de uma classe. Portanto, ao fazer override deste método, o correto é finalizar a
public class Foo {
private final Object finalizerGuardian = new Object() {
protected void finalize() throws Throwable {
// Finaliza o objeto Foo
}
};
... // restante da classe Foo
}
Este artigo trata sobre os métodos não finais da classe Object. É responsabilidade do programador sobrescrevê-los em
suas classes de forma adequada, garantindo o bom funcionamento de outras classes que os utilizam.
aspecto sem violar esta regra ou a de simetria no equals. Na API do Java, por exemplo, a classe Timestamp viola
a regra de simetria ao herdar a Date, conforme explicado na sua documentação. Quando acontecer um caso
semelhante, é melhor utilizar composição ao invés de herança.
vi. Se o campo for uma referência a objeto e a classe, realize a comparação do campo chamando
o equals recursivamente e chame o hashCode recursivamente. Se for necessária uma comparação mais complexa,
utilize uma "representação canônica" do campo e chame o método de hashCode nela. Se o valor do campo for nulo,
retorne 0
vi. Se o campo for uma array, trate cada elemento como um campo separado e combine os valores como descrito no
método 2.b
b. Combine o hash code c, obtido no passo a, na variável result, da seguinte forma: result = 37 * result
+ c;
3. Retorne a variável result
4. Revise o método hashCode e equals verificando se instâncias iguais estão retornando o mesmo hash code, de
forma a obedecer ao contrato. É imprescindível que qualquer campo não derivado que não é utilizado no equals não
seja utilizado também no hashCode, para não violar a segunda regra do contrato.
A multiplicação no passo 2.b faz com que a ordem dos campos seja considerada, e o 37 foi escolhido por ser um
número ímpar primo. Números pares não devem ser utilizados porque caso a multiplicação fique muito grande seria
perdida informação, pois multiplicação por 2 é equivalente a uma operação de shift.
Para melhorar a performance, se a classe for imutável pode ser considerada a possibilidade de guardar o valor do hash
code no objeto ao invés de recalculá-lo toda vez. Uma má idéia é excluir campos significativos do cálculo, pois isso
poderá impactar a performance no uso de hash tables posteriormente.
Item 9: Sempre sobrescreva o toString
O método original toString, herdado da classe Object, consiste no nome da classe seguido por um sinal de arroba
e a representação hexadecimal do hash code. O contrato para esse método diz que a string retornada deve ser uma
representação informativa e concisa e deve ser fácil para uma pessoa ler. Uma boa implementação deste método torna a
classe mais agradável de ser usada, pois ele é chamado automaticamente quando o objeto é passado em um println,
ao usar o operador de concatenação (+) ou juntamente com o assert.
Quando viável, o método toString deve retornar todas as informações relevantes contidas no objeto. Mas pode ser
criado um tipo de resumo que identifique o objeto quando ele for muito grande ou tiver alguma informação que não seja
possível representar por uma string. Idealmente, o valor retornado é auto-explicativo.
No caso de classes de valores, uma boa opção é documentar o formato do retorno deste método e prover um construtor
que receba uma string neste formato para criar o objeto, tornando possível que outros programadores traduzam
facilmente de objeto para string e vice-versa. Porém, ao fazer isso torna-se perigoso mudar este formato, pois outras
classes provavelmente estarão dependendo dele. Qualquer que seja a decisão, é importante deixar claro na
documentação o formato ou então a possibilidade de mudança desta representação.
Independentemente de especificar ou não o formato, é sempre uma boa idéia ter métodos que acessem diretamente toda
a informação retornada pelo método toString, evitando que os outros programadores sejam forçados a obtê-la
através de parse da string, o que reduz a performance e está mais sujeito a erros.
inclui o método clone e o método da classe Object tem acesso protected , não sendo possível chamar o método
pelo simples fato de ter a interface implementada. O que a Cloneable faz na verdade é determinar o comportamento
da implementação original do método clone: se ela tiver sido implementada, será feita uma cópia campo a campo do
objeto, caso contrário, será lançada uma CloneNotSupportedException.
O contrato para a implementação deste método é bem vago, dando apenas algumas sugestões. De forma geral, a cópia
de um objeto envolve a criação de uma nova instância da classe (sem utilizar o construtor) que pode ser seguida de uma
cópia dos dados internos.
Se você sobrescrever o método clone de uma classe que não seja final, você deve retornar o objeto criado através
de super.clone, pois desta forma as subclasses podem fazer o mesmo e obter um objeto de sua própria classe, que
- sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) - isto implica ainda que só poderá ser lançada uma
exceção em um dos lados da igualdade se também for lançada no outro
Este artigo contém orientações sobre o uso de classes e interfaces, tornando-as mais usáveis, robustas e flexíveis.
Métodos
Este artigo discute vários aspectos da criação de métodos, visando usabilidade, robustez e flexibilidade.
Item 28: Escreva comentários de documentação (javadoc) para todos os elementos expostos da API
O javadoc provê um meio efetivo de documentar sua API e mantê-la sempre atualizada, devendo ser usado amplamente.
Todas as classes, interfaces, construtores, métodos ou declaração de campos exportados devem ser precedidos por um
comentário.
A documentação de um método deve descrever sucintamente o contrato entre o método e seu cliente. Ela deve dizer o
que o método faz ao invés de como faz. Deve enumerar as pré-condições (através da @throws, @param ou descrição),
pós-condições e efeitos colaterais. Também deve estar indicada a segurança em relação a threads. Devem ser incluídas
as tags @param para cada parâmetro, @return se o tipo de retorno não for void e @throws para as exceções que
podem ser lançadas (checked ou unchecked), seguidas pela descrição do parâmetro ou retorno ou da condição em que a
exceção é lançada.
É permitido usar HTML para uma melhor formatação do texto no javadoc.
A primeira sentença de cada comentário de javadoc é a descrição resumida do elemento. Tenha cuidado para que ela não
contenha um ponto (por exemplo em uma abreviação), pois isso quebraria a sentença antecipadamente. Se for
necessário ele deve ser substituído por "."
Há um mecanismo de reuso de documentação automática caso ela não seja fornecida para um método, utilizado no caso
de interfaces ou superclasses. Ele é prático para evitar manter várias cópias de um mesmo comentário, mas não permite
que seja alterado, sendo necessário reescrever todo comentário caso o do método da classe em questão seja mais
especializado.
Algumas vezes a API é muito complexa e a documentação gerada pelo javadoc não é suficiente, sendo necessário
incluir um link para uma documentação mais completa.
Programação em geral
Este artigo é dedicado aos detalhes práticos da linguagem Java. Ele discute o tratamento de variáveis locais, o uso de
bibliotecas, o uso dos tipos de dados, e dois recursos extralingüísticos: reflection e métodos nativos. Por fim, ele discute
otimizações e convenções de nomes.
seus nomes todos em maiúsculas com as palavras separadas por underscore. Os métodos normalmente utilizam verbos
ou frases verbais como nome. Métodos que retornem um boleano usam o prefixo "is" seguido de um adjetivo ou
substantivo, ex: isDigit. Já os que retornam o resultado de uma função ou um atributo do objeto são nomeados com
substantivos com ou sem o prefixo "get". O uso do get só é obrigatório se a classe for um JavaBean. Havendo um
método para atribuir o valor a um atributo do objeto utiliza-se o prefixo set. Métodos que convertem o tipo de um objeto
são chamados toTipo. Métodos que retornam uma "visualização" de um tipo diferente do objeto recebido normalmente
são chamados de asTipo. Métodos que retornam um primitivo com o mesmo valor do objeto em que foram chamados
recebem o nome de tipoValue. Nomes comuns para static factories são valueOf e getInstance.
Variáveis locais seguem as mesmas convenções de métodos e campos, mas abreviações são permitidas.
Exemplos:
Pacote: com.sun.medialib, com.sun.jdi.event
Classe ou interface: Timer, TimerTask, KeyFactorySpi, HttpServlet
Método ou campo: remove, ensureCapacity, getCrc
Valor constante: VALUES, NEGATIVE_INIFINITY
Variáveis locais: i, xref, houseNumber
Exceções
Quando bem usadas, as exceções podem melhorar a legibilidade, confiabilidade e sustentabilidade do código. Mas
quando usadas incorretamente, podem causar o efeito oposto. Este artigo fornece orientações sobre seu uso.
Item 39: Use exceções apenas para condições excepcionais
As exceções, como o próprio nome indica, devem ser usadas apenas em condições excepcionais; nunca para controle de
fluxo comum.
Seu uso costuma ter um impacto negativo na performance, uma vez que é custoso criar, lançar e pegar uma exceção,
além de atrapalhar as otimizações feitas automaticamente pela JVM.
O uso de exceções para controle de fluxo também mascara o objetivo do código e aumenta a probabilidade de um bug
passar desapercebido, ficando mais difícil encontrar a causa do erro posteriormente.
Lembre-se disso ao criar sua API, para não forçar o cliente da classe a usar exceções desnecessariamente. Uma classe
que possui um método que depende de um certo estado normalmente deve incluir também um método separado para
testar esse estado, indicando se é apropriado ou não chamar o primeiro método. Um exemplo desta técnica é a
classe Iterator, que possui os métodos next e hasNext.Outra alternativa é que o método que depende do estado
retorne um valor especial, como null, quando chamado em um objeto em estado inapropriado. Esta segunda
abordagem é a mais adequada para casos em que o estado do objeto pode mudar no intervalo entre a chamada do
método teste e o método que depende do estado, ou que a performance seja crítica. Em outras situações, é melhor o
método teste, pois é mais legível e é mais fácil detectar e corrigir um uso inapropriado.
Item 40: Use checked exceptions para condições recuperáveis e runtime exceptions para erros de programação
Há três tipos de throwables na linguagem Java: checked exceptions, runtime exceptions e errors.
Utilize checked exceptions para condições em que se espera que a classe que chamou o método possa recuperar-se.
O lançamento da exceção é um indício ao usuário da API que aquela determinada condição é um dos retornos possíveis
da chamada do método e assim você força que ele insira código para tratar a exceção ou propagá-la.
Existem dois tipos de unchecked throwables: runtime exceptions e errors. Eles são idênticos em seus comportamentos:
ambos são throwables que não precisam ser tratados. Quando um programa lança algum deles, normalmente é um caso
impossível de recuperar; continuar executando apenas pioraria a situação. Portanto, se o programa não incluir um bloco
catch para tratar esta exceção, a thread em que ela acontece é interrompida mostrando uma mensagem de erro.
Utilize runtime exceptions para indicar erros de programação. Normalmente são usadas em violações de pré-condições,
ou seja, quando o cliente não adere ao contrato estabelecido na API.
Já os erros são utilizados pela JVM para indicar uma deficiência de recursos, falhas ou outras condições que tornam
impossível que a execução continue. Ainda que isso não seja determinado na especificação, é uma convenção bem
aceita, portanto você não deve criar novas classes de erros; utilize subclasses de RuntimeException para todos os
unchecked throwables.
É possível definir throwables que não são subclasses de Exception, RuntimeException ou Error. Seu
comportamento fica igual ao de checked exceptions, portanto é melhor herdar da classe Exception, que é o mais
comum.
Resumindo, se você acha que a situação provavelmente é recuperável, utilize checked exception, caso contrário, utilize
runtime exception.
Além disso, ao criar novas classes de exceções, lembre-se de que é possível incluir métodos para retornar informações
para o usuário, o que é uma prática muito melhor do que simplesmente obrigá-lo a obter os dados dentro da String que a
representa. Isso é especialmente importante nas checked exceptions, para que a causa do problema seja identificada
mais facilmente e contornada.
Threads