O objetivo deste documento é auxiliar o programador no desenvolvimento de projetos utilizando a linguagem Java.
Este documento é aplicável a todos os projetos de desenvolvimento de software realizados em Empresas que utilizam a abordagem de Orientação a Objeto (OO).
Este documento apresenta o Padrão de Codificação da Linguagem JAVA.
Os padrões para codificação de programas visam garantir que todos os desenvolvedores envolvidos no desenvolvimento e/ou manutenção de sistemas, entenderão com facilidade o objetivo e funcionamento de cada programa.
Os padrões devem proporcionar:
Através do padrão adotado estaremos portanto disponibilizando:
Mesmo tentando estabelecer padrões rígidos e detalhados, em diversas situações não será possível aplicar integralmente o padrão geral, seja por limitações de espaço em tela, por particularidades do programa ou pela facilidade de operação que um desvio de padrão poderá conferir ao programa. Em todo caso, porém, um desvio no padrão definido nesse documento deverá ser discutido previamente com o analista responsável.
PAPÉIS/ÁREAS FUNCIONAIS | RESPONSABILIDADES |
Programadores | Desenvolver o Código-Fonte da aplicação de acordo com as instruções do documento de padronização. |
Este documento foca em regras para desenvolvimento de softwares coorporativos que exijam padronização.
Este documento apresenta regras que ajudarão a desenvolver e melhorar o estilo e estrutura dos seus códigos Java. Ele foca em pontos comuns de erro cometidos pelos desenvolvedores de software que diretamente afetam a qualidade do código. É importante lembrar que a qualidade do software esta diretamente associada à forma de manutenção, performance e portabilidade de problemas, os quais podemos prevenir e/ou capturar, reduzindo o tempo gasto no futuro através de debug ou otimização de código, através da utilização desse documento.
O uso deste documento também ajudará a novos desenvolvedores entenderem a padronização e o nível de expertise código. Eles não verão somente regras associadas com a qualidade na programação Java, mas o "entendimento" atrás das regras utilizadas.
Muitas regras e definições usadas neste documento contem informações redundantes. Isto foi feito com o intuito de poder ser usado sem a necessidade de ler regras anteriores.
Qualquer pessoa envolvida em projetos que estejam usando ou planeje usar Java, como linguagem de desenvolvimento para a construção de aplicações. Esse documento é diretamente técnico para lideres e desenvolvedores.
Será assumido um conhecimento básico da linguagem Java.
Este documento agrupa seus pontos em tópicos específicos. Os pontos estão em formas de regras ou definições. As regras devem "quase" nunca serem quebradas, em quanto às definições podem ser violadas com mais freqüências. Caso alguns tipos de padronização não façam sentido no projeto em desenvolvimento, esses devem ser ignorados e podem ser evoluídos durante o processo de desenvolvimento.
O compilador Java requer que todo arquivo fonte tenha a extensão "java"
Exemplo:
javac MyClass.java
Nota: Para sistemas que suportam somente nome de arquivo 8.3, a extensão "jav" é usada.
O compilador java gera arquivos binários com a extensão "class".
Exemplo:
Este exemplo roda MyClass.class:
java MyClass
Nota: Para sistemas que suportam somente nome de arquivo 8.3, a extensão "cla" é usada.
Cada sub-nível dos nomes do package é mapeado para um nome de diretório no sistema operacional, portanto esse nomes devem ser suportados pela estrutura de arquivos do S.O. Use somente caracteres alpha e numéricos nesses nomes.
Exemplo:
Considere o package:
package mystuff.myclasses;
Ele será mapeado para o determinado diretório no S.O.:
...\mystuff\myclasses
onde “...” é o caminha criado para os fontes Java.
Java permite classes e/ou interfaces sejam agrupadas em packages. Packages são hierarquicamente um modo de organizar classes. Serviços ou classes que compõe um simples programa deve ser colocado em seu próprio package. Veja por exemplo o package de classes do Java.
Nota: Todos os packages desenvolvidos na Empresa deverão seguir a nomenclatura
com.[nome_da_empresa_abreviado].[nome_do_projeto].[outras_identificacoes]
.
Exemplo:
Imagine que você precise criar um package que contenha classes para acessar o banco de dados do projeto
XPTO, da empresa ACME. Essa nomenclatura provavelmente ficaria:
com.acme.xpto.persistencia
Uma referencia em ciclo é onde uma classe A tem uma variável que é instancia de uma classe do tipo B e B tem uma variável que é instancia de uma classe do tipo A.
Exemplo:
package com.acme.dados; // Java1 referencia Java2 e vice-versa class Java1 { Java2 aJava2; Java1() { aJava2 = new Java2(); } void method1() { aJava2.method1(); } } class Java2 { Java1 aJava1; Java2() { aJava1 = new Java1(); } void method1() { aJava1.method1(); } }
Se este relacionamento é requerido, então ambas as classes ficam no mesmo arquivo fonte. Se ambas as classes precisam ser publicas, então coloque cada classe em arquivos separados, mas usando o mesmo package.
Em geral, deve ser somente uma classe por arquivo. Isso permite que o arquivo fonte combine com o nome da classe, portanto seja mais fácil de localizar o arquivo fonte. Dessa forma, classes relacionadas devem ser colocadas no mesmo package, mas não no mesmo arquivo.
Caso existam várias classes no mesmo arquivo, o nome do arquivo tem que ser o mesmo da única classe publica existente nesse arquivo. As outras classes precisam ser privadas ou classes somente visíveis para o package.
Exemplo:
Para o arquivo fonte MyClass.java:
public class MyClass { ... } class InternalClassA { ... } class InternalClassB { ... }
Importe classes e não packages.
Exemplo:
import mypackage.MyParameterClass; import otherpackage.OtherClass; class MyClass { ... public void method( MyParameterClass oX ) { ... } ... }
Isso reduz a chance onde duas classes dividem o mesmo nome, e você use a classe errada.
Os métodos devem ser o menor possível, para possibilitar legibilidade e manutenibilidade. Considere
mover if, while e for
para métodos privados. Métodos grandes não são aceitos em OOP.
Declaração de múltiplas variáveis em um único statement é confuso.
Exemplo:
package com.acme.projeto.dados; class VDT { private int index, index1[]; // VIOLACAO public void method () { int aaa, bbb[]; // VIOLACAO int ccc; int ddd; } }
Reparar:
Use declaração de statements separados para variáveis de tipos diferentes.
package com.acme.projeto.dados; class VDTFixed { private int index; // INDICADO private int index1[ ]; // INDICADO public void method () { int aaa; // INDICADO int bbb[ ]; // INDICADO int ccc; int ddd; } }
Uma grande quantidade de parâmetros indica complexidade para interfacear a chamada a objetos e deve ser evitado. Propomos uma limitação de 5 (cinco), o numero de parâmetros recebidos por um método.
Se essa formatação for seguida por todos, isso facilitará a leitura do código por outras pessoas.
Exemplo:
package com.acme.format; public class OSPL { int method (int a, int b) { int i = a + b; return i; // VIOLACAO } }
Reparar:
package com.acme.format; public class OSPLFixed { int method (int a, int b) { int i = a + b; //INDICADO return i; // INDICADO } }
Uma vez que um arquivo fonte mude o controle das ações, todas as modificações requerem algum fixamento de erro ou aprovação de mudança de design. Este arquivo alterado deve ter seu objetivo modificado, explicando qualquer alteração, adição ou revisão que tenha ocorrido no arquivo. Suas descrições devem ser colocadas na seção de revisões.
Exemplo:
/** Pequena descrição do objetivo. * * * @copyright (C) ACME Corp. * * @security unclassified * * @author I. M. Smart * @date 01/01/96 * @revision DCR001 * date 10/01/96 by: B. A. Feigenbaum * reason Adicionar a instancia de um contador * * @revision PTR001 * date 10/10/96 by: I. M. Smart * reason Adicionar construtor default * * @revision DCR002 * date 10/20/96 by: B. A. Feigenbaum * reason Remover o proprietario de endereço **/ public class Account { // instance count static int count; // account id int id; // account value double value; // account owner String name; // default constructor public MyClass() { // count myself count++; value = 0.0; name = ""; } // constructor public MyClass( String name, double value ) { // count myself count++; value = value; name = name; } // constructor public MyClass( String name ) { // count myself count++; value = 0.0; name = name; } // destructor public finialize() { // discount myself count--; } ... }
Veja exemplo de prefácios para padronização sugerido para classe, métodos e campos.
Qualquer comentário começa com blocos /** para ser processado em documentação através da utilização de ferramentas como javacdoc.
Essas linhas devem seguir as seguintes definições para classe:
tag
Função
Exemplo:
Prefacio da Classe:
/* (C) Copyright by ACME Corp. * All Rights Reserved. */ /** Descrição da classe. *mais texto se necessário. * * @author X. Y. Zzzzzzzzzzzzzzz * @date dd/mm/yyyy * @see OutraClasse * @revision 001 * date dd/mm/yyyy * author X. Y. Zzzzzzzzzzzzzzzzzzzz * reason pequena descrição **/
/** * Breve descrição * Descrição da função * Incluir a descrição sobre o algoritmo caso não trivial * * @return descrição do valor de retorno * @param arg1 descrição do arg1 *: :: * @param argN descrição do argN * @exception full.exception.ExceptionName1 significado da exception1 *: : * @exception full.exception.ExceptionNameN significado da exceptionN * * @see AnotherClass#AnotherMethod * @author X. Y. Zzzzzzzzzzzzzzz * @date dd/mm/yyyy **/ modifiers returnType name(argType1 arg1, ..., argTypeN argN) throws ExceptionName1, ..., ExceptionNameN { // corpo do método }
/** * Descrição do campo. **/
Notas:
Todos os comentários devem ser feitos em português usando //. /* somente serão usados para cunho de debug.
Sempre incluir pelo menos um espaço em branco após o // introdução do comentário. Isso melhora a legibilidade.
Nota: Comentários devem ser identificados com o código fonte e nunca colocar "fim" no final das linhas.
As várias partes de cada entrada em uma classe ou interface deve estar alinhada. Tipos, variáveis/métodos, devem começar na mesma coluna. Com isso fica mais fácil achar os diferentes elementos.
A endentação e feita automaticamente pelo WSAD, Eclipse e outras IDEs Java existentes no mercado. Use a endentação de 4 caracteres.
Exemplo:
public class MyClass { // class variables // instance counter private static int iCount = 0; // instance variables // a value 1 private int iVariable1; // a value 2 private int iVariable2; // a value 3 private int iVariable3; // an object 4 private ALongTypeNameXXX oVariable4; // methods // a method 1 public int method1() { : } // a method public int aLongMethodNameThatKeepsGoingAndGoing() { : } // arg1 description // arg2 description // arg3 description public void method2( int arg1, LongName arg2, char arg3 ) { : } }
O operador ternário é somente para atribuições "puras".
Exemplo:
class MyClass { int iX ; void changeX (boolean bCondition) { x = (bCondition == true) ? 0 ? 1 ; } }
import com.acme.*; class Application { private Portable portable; public static void main( String[] args ) { // select platform here Application a = new Application( System.os.equals("Linux") ? new LinuxPortable() : new WinPortable() ); a.doIt(); } public Application( Portable p ) { portable = p; } public void doIt( ) { : // o mesmo para todas as plataformas portable.nonPortableMethod(); : } }
O uso do default no case faz o fluxo de controle explícito quando não existe uma combinação para o label em questão. Isso torna mais fácil para os desenvolvedores determinarem quando um label usado no case deve ser adicionado ou removido inadvertidamente.
Statements de expressões condicionais em estruturas de controle devem ser explicitadas. Em Java, expressões condicionais devem conter valores dos tipos char, byte, short, int.
Exemplo:
int counter = 100; Object o = ...; // Explicitando os estados condicionais das expressões: while(counter != 0) ... if(o != null) ... // isto é ilegal: // integers não são booleanos while(counter) ... // sem ponteiros na linguagem if(!o) ...
Endente com 4 espaços em branco dentro da estrutura de controle, delineando entre o fechamento da
estrutura de controle e seus blocos de statements. A codificação é muito mais fácil de ler se o loop, case e
statements de condição estão organizados. Todos os statements após um if, else, while, do, for, switch, ou case
devem estar endentados. Todos os statements dentro de um bloco (ex:, {e}) devem estar endentados.
A endentação sugerida é para 4 caracteres. Isto permite uma fácil separação visual, mesmo para significantes aninhamentos sem precisar endentar muito para a direita. Para linhas continuas endente oito espaços em branco ou alinhe o texto continuo com o similar texto da linha anterior.
// endente seus blocos de controle... int j; for(j = 0; j < 10; j++) { int i; for( i = 0; i < 10; i++ ) { System.out.println("I = " + i + ", J = '+j+ ", I * J = " + (i * j)); } } if(i == 0) { : } else { : }
int i; for(i = 0; i < 10; i++) { System.out.println("I = " + i); }
Existem vários outros estilos de organizar essas estruturas, mas a seguinte é a sugerida pelo NÉKI Group.
Use o seguinte estilo para statements if:
Exemplo:
// Para varios blocos de statement if(expr) { statements } else { statements }
Use o seguinte estilo para statements for:
Exemplo:
// Para vários blocos de statement for( expr; expr; expr ) { statements }
// Para vários blocos de statement while( expr ) { statements }
// Para vários blocos de statement do { statements } while( expr );
switch( expr ) { case 1: { // statements break; } case 2: { // statements break; } : case N: { // statements break; // requerido mesmo se vazio default: { // statements break; } }
try { // código para ser tentado } catch( Exception e ) { // código fixado } finally { // código para ser limpo }
// Para vários blocos de statement type doSomething() { statements } --ou -// para simples statements de métodos type doSomething() { statement }
Nota:
Se um statement break não for especificado em cada bloco case, então todas as linhas de códigos associadas ao bloco que seguem o case serão executadas. Isto geralmente não é a intenção do desenvolvedor.
Quando usar signed number seja cuidadoso para planejar os limites da faixa e os possíveis overflows.
Considere as faixas:
Tipo | Faixa |
---|---|
byte | -128 até +127 |
short | -32,768 até +32,767 |
int | -2,147,483,648 até +2,147,483,647 |
long | -9,223,372,036,854,775,808 até +9,223,372,036,854,775,807 |
Por exemplo, se você adiciona 1 para um byte com um valor corrente de 127 você pode ter um resultado de 128,
não +128. Considere:
byte i = 127; : i++; System.out.println("i is " + (i < 100 : "less" : "greater") + "than 100"); :
i is less than 100
.
Uma digitação comum enquanto escrevendo código é usar "=" ao invés de "==" em operações de igualdade. Colocando constantes no lado esquerdo da comparação, isso ira fazer com que o compilador de uma mensagem de erro. Por exemplo, o compilador java dará uma mensagem de erro para: if(false = var) mas não para: if(var = false).
Exemplo:
package com.acme.projetos.dados; public class CLS { public void testMethod (int something) { if (something == 5) {} // VIOLACAO } }
package com.acme.projetos.dados; public class CLSFixed { public void testMethod (int something) { if (5 == something) {} // INDICADO } }
Se um incremento ou a condição de checagem é verificada fora do loop, estes não devem ser usados.
Se a condição e o incremento estão no corpo do loop, esta sendo usado um estilo pobre.
Exemplo:
package com.acme.projetos.dados; public class PCIF { void method (int i) { for (; i < 1000; ) { // VIOLACAO: incremento não é feito no loop i++; } } }
package com.acme.projetos.dados; public class PCIFFixed { void method (int i) { while (i < 1000) { // INDICADO i++; } } }
Atribuições em muitos tipos de statements condicionais em Java, são ilegais porque eles são fáceis de causar acidentes com os seus tipos. Seguindo estas regras ajuda a manter a conformidade e evitar erros.
Exemplo:
package com.acme.projetos.dados; public class ASI { public int foo (boolean b) { int ret = 0; if (b = true) { // VIOLACAO ret = 3; } return ret; } }
package com.acme.projetos.dados; public class ASIFixed { public int foo (boolean b) { int ret = 0; if (b == true) { // INDICADO ret = 3; } return ret; } }
Statements "for"que são seguidos imediatamente por ponto e virgula, não é uma boa pratica.
Exemplo:
package com.acme.projetos.dados; public class FEB { void method () { for (int i = 0; i < 10; i++) ; // VIOLACAO System.out.println (i); // este statement fora do loop } }
O 'println ()'será executado uma unica vez após o loop.
Reparar:
Remova o ponto e virgula no fim do loop.
package com.acme.projetos.dados; public class FEBFixed { void method () { for (int i = 0; i < 10; i++) // INDICADO System.out.println (i); // este statement esta dentro do loop } }
Uma variável de controle de um "for" loop deve somente ser modificada na inicialização e expressão de controle do statement "for" loop. Modificando-a no corpo do loop faz o código difícil de entender e pode contribuir para erros.
Exemplo:
package com.acme.projetos.dados; public class FLVA { int method () { int sum = 0; for (int i = 0; i < 100; i++) { i += 3; // VIOLACAO sum += i; } } }
package com.acme.projetos.dados; public class FLVA { int method () { int sum = 0; for (int i = 0; i < 100; i += 4) { //INDICADO sum += i; } return sum; } }
Se um statement "if" estiver seguido imediatamente por um ponto e virgula, não é uma boa pratica.
Exemplo:
package com.acme.projetos.dados; public class IEB { void method (int i) { if (i < 0) ; // VIOLACAO System.out.println("negative i"); // Isto sempre acontecera } }
O 'println ()'sempre acontecera independente do valor de 'i'.
Reparar:
Remova o ponto e virgula desnecessario.
package com.acme.projetos.dados; public class IEBFixed { void method (int i) { if (i < 0) // INDICADO System.out.println("negative i"); } }
Isso faz o código torne mais legível e evita alguns comentários. Java não tem limite para o nome de classes, métodos ou variáveis. Seguem algumas regras sugeridas para compor os nomes:
MAX_VALUE
.
a | Para array object |
b | Para byte |
c | Para char |
d | Para double |
dt | Para date |
e | Para exception object |
f | Para float |
i | Para int |
i, j, k, ... | Para índice de for loop |
l | Para long |
o | Para qualquer object |
s | Para string object |
v | Para uso geral |
get campo | get() |
set campo | set(type arg) |
get length | length() |
condição | is() |
Conversão de formato | to() |
Gerenciar ações | on() |
Tipo de classe | Padrão do nome | Uso |
---|---|---|
Servlets | Serv....... | Para o uso com servlets |
Services | ... BO | Para classes que representam regra de negócio |
Batch | Bt.... | Para aplicações batch, classes que tem o método mais e são executadas off-line |
Structure | ...VO | Para classes que são estrutura de dados, que são passadas entre os servlets, JSP, Sv e Db |
DB | ... DAO | Para classes que acessam os banco de dados |
JSP | ..... .jsp; | Para os arquivos JSP’s, deve ser adotado a mesma convenção que as classes java |
Session Beans | SL…. | Para session beans |
Entity Beans | EC | Para entity beans |
com.acme.infra
.
class ClassName { // base value int iVariable1; int iVariable2; int iVariable3; // object MyClass oMyClass; public ClassName( int newVariable1, int newVariable2, int newVariable3 ) { variable1 = newVariable1; variable2 = newVariable2; variable3 = newVariable3; } }
Redefinições de nomes em diferentes escopos, somente traz confusões para quem estiver lendo o código. Se uma declaração da variável for removida do escopo aninhado, então o código deve inadvertidamente alterar o conteúdo da variável que esta no escopo acima, trazendo conseqüências inesperadas.
Exemplo:
void member() { double x = 0.1; for(int i = 100; i < 0; i--) { // esqueça esse tipo de declaração int x = 1000; : // será uma divisão inteiro; não float como deve ser esperado x /= i; } // resposta é 0.1 System.out.println("The result is " + x); }
Usando os labels i, j, k e assim por diante como variáveis de loop é uma convenção que é resultado de tradição. O uso desses labels devem ser uniformes por todo os códigos do projeto, tornando assim fácil e rápido a identificação de variáveis de controle do loop.
O tempo de vida de um variável de controle no statement for é a duração do loop. Se você necessita de um tempo maior do que o declarado no controle do loop, declare a variável de controle fora do loop, mas seja cuidadoso com isso, lembre, declare a variável dentro do escopo do loop, sempre que possível.
Exemplo:
class SomeClass { : public void someMethod() { : // i existe até o fim do for for( int i = 0; i < 100; i++ ) loop{ : if(/* some condition */) { break; : } // erro -i não esta declarado aqui if( i < 100 ) { /* some action */; : } : } }
class SomeClass { : public void someMethod() { // i existe até o fim do someMethod int i; : for( i = 0; i < 100; i++ ) { : if( /* some condition */ ) { break; : } // OK -i existe if( i < 100 ) { /* some action */; : } : } }
Todos os nomes para classes e interface devem começar com letra maiúscula e os nomes subseqüentes devem iniciar também.
Exemplo:
class PointXY { : } class Human { : } interface MemberOfPool { : } PointXY oPointXY; Human oHuman; MemberOfPool oMemberOfPool; Integer i; Color oColor;
Se você conhece a quantidade de objetos a serem armazenadas, use array, caso contrario use:
Os colchetes devem ser colocados a direita após o tipo da variável e nunca após o nome.
class SomeClass { : // aSomeField é um array do tipo inteiro public int[] aSomeField = new int[10]; // aSomeVector é um vector public Vector aSomeVector = new Vector(); { : } : }
Constantes inteiras são long se você colocar no fim da declaração o "L" ou "l". É preferível usar o "L" ao invés de "l", pois o "l" pode ser confundido com o numero um.
Comece o nome do método com letra minúscula e coloque letra maiúscula para todas as palavras subseqüentes.
Exemplo:
boolean isEmpty() { ... } File findFile() { ... }
Métodos são identificados pelo objeto no qual ele esta agindo. Não existe a necessidade de identificar a classe no nome do método.
MyClass myClass; // foo myClass.foo();
myClass.fooMyClass(); --ou -- myClass.myClassFoo();
static long max( long x, long y ); static double max( double x, double y );
static long maxl( long x, long y ); static double maxd( double x, double y );
O uso de static e final no modificador de acesso de uma variável faz com que seja imutável seu valor, definindo assim uma constante.
Exemplo:
class MyConstants { // constante String public static final String JOHN_DOE = "John Doe"; // constante int public static final int JOHN_DOE_ID = 100; : }
: String myName = "Barry Feigenbaum"; int myID = -1; if(myName.equals(MyConstants.JOHN_DOE)) { myId = MyConstants.JOHN_DOE_ID; : if(myID < 0) { // Realiza algo } : }
São usualmente utilizados para expressar efeitos que indiquem o objetivo do método:
prefixos | Tipo de método |
---|---|
get | Prefixo para retornar um atributo de um método. |
set | Prefixo para modificar o atributo de um objeto. |
query | Prefixo para retornar um valor de um objeto que não deve ser modificado. |
to | Prefixo para conversão de métodos como toString(). |
on | Prefixo para métodos que gerenciam eventos, como onButtonPress(). |
is | Prefixo para checar tipo/estado como isValid(). Esses métodos retornam um valor booleano. |
newInstance; instance; getInstance | Nome para métodos de factory. |
class ClassName { // variaveis int iVariable1; int iVariable2; // acesso public int getVariable1() { return variable1; } // atribuição public void setVariable1( int newVariable1 ) { variable1 = newVariable1; } // public boolean isReady() { return variable1 == variable2; } }
O nome dos parâmetros deve indicar seus objetivos. Nomes consistentes são fáceis de serem distinguidos dos atributos.
Exemplo:
Use o qualificador this (new in Java Beans)
public class SomeClass { private int field1; : private int fieldN; : public SomeClass( int newfield1, ..., newfieldN ) { // this.setfield1 é o atributo // newfield1 é o argumento this.setfield1 = newfield1; : this.setfieldN = newfieldN; } : }
A aparência da classe é importante. Apesar da IDE Java gerenciar a geração do código, é importante termos a idéia da formação da estrutura de uma classe.
//TODO:
Exemplo:
Sugestão da estrutura de uma classe
class <name extends <superclass> implements <interfaces> { // public atributos e métodos visíveis public static final data members public static data members public data members public constructors public finialize public static methods public methods
// Atributos e métodos visíveis somente para o package static final data members static data members data members constructors static methods methods // package visibilidade protected para atributos e métodos protected static final data protected static data protected data protected constructors protected static methods protected methods
// visibilidade protected para atributos e métodos private protected static final data private protected static data private protected data private protected constructors private protected static methods private protected methods // visibilidade private para atributos e métodos private static final data members private static data members private data members private constructors private static methods private methods }
Criar classes que modelem entidades no seu limite de domínio, não existe a necessidade de abranger várias soluções em uma única classe.
Não crie classes ou métodos que estejam prontos para serem usados. Entenda se existe a real necessidade de declara-los como publico, disponibilizando seu uso.
Cada classe deve ser atômica. Uma classe deve ser única. Não misture vários objetivos dentro de uma classe. Se combinações são necessárias, use interface para especificar as combinações.
Exemplo:
Definição de interface:
package persistence; import java.io.IOException; import java.io.DataOutputStream; import java.io.DataInputStream; public interface Persistent { // saves object to file public boolean storeOn( String f ) throws IOException; // saves object to stream public boolean storeOn( DataOutputStream s ) throws IOException; // gets object from file public boolean loadFrom( String f ) throws IOException; // gets object from stream public boolean loadFrom( DataInputStream s ) throws IOException; }
import persistence.Persistent; import application.AppClass; public class MyClass implements Persistent { private int value1; // AppClass implementa Persistent tambem private AppClass class1; : public boolean storeOn( String f ) throws IOException { DataOutputStream s = new DataOutputStream( BufferedOutputStream( FileOutputStream( f ) ) ); boolean result = storeOn( s ); s.close(); return result; } public boolean storeOn( DataOutputStream s ) throws IOException { // put int s.writeInt( value1 ); if( class != null ) { // indicate AppClass present s.writeBoolean( true ); // put instance class1.stroreOn( s ); else // indicate AppClass missing s.writeBoolean( false ); } public boolean loadFrom( String f ) throws IOException { DataInputStream s = new DataInputStream( BufferredInputStream( FileInputStream( f ) ) ); boolean result = loadFrom( s ); s.close(); return result; } public boolean loadFrom( DataInputStream s ) throws IOException; { // get int value1 = s.readInt( ); if( class1 != null ) { // do cleanup on current state class1.finalize( ); // only if AppClass saved if( s.readBoolean( ) ) { // get empty instance class1 = new AppClass(); // fill it in class1.loadFrom(s); else class1 = null; } } }
Se possível, prover uma classe utilitária que provenha à implementação de uma
interface padrão. Isto permite qualquer classe que implemente a interface seja usada
como uma classe utilitária. Isso aumenta o reuso.
--usando interfaces (modo preferencial) -// class MyClass implements AnInterface { private AnInterfaceImplementation implementation; public MyClass() { implementation = new AnInterfaceImplementation(); } public void doIt( ) { // implementação padrão para reuso implementation.doIt(); } } --uso da implementação (se não existe nenhuma outra superclass) -- // class MyClass implements AnInterface extends AnInterfaceImplementation { : } // uso do código class Application { public static void main(String[] args) { AnInterface x = new MyClass(); : x.doIt(); } }
Atributos nas subclasses com o mesmo nome dos atributos da superclasses, "escondem" os atributos existentes na superclasse. Métodos na subclasse com o mesma assinatura dos métodos existentes na superclasse sobrescrevem os métodos existentes na superclasse.
Exemplo:
package demo; public class Base { public int field1 = 1; public int getField1( ) { return field1; } } --outro arquivo fonte-
package demo; public class Derived extends Base { public int field1 = 2; public int getField1( ) { return field1; } } --outro arquivo fonte -import demo.*; class Application { public static void main( String arg[] ) { Base b = new Base( ); Derived d = new Derived( ); // imprime: 1,2 System.out.println( b.field1 + "," + d.field1 ); // imprime: 1,2 System.out.println( b.getField1( ) + "," + d.getField1( ) ); b = d; // imprime: 1,2 System.out.println( b.field1 + "," + d.field1 ); // imprime: 2,2 System.out.println( b.getField1( ) + "," + d.getField1( ) ); } } <source><![CDATA[ </p> <p> Diferente deste exemplo, atributos são normalmente declarados como private com métodos públicos de acesso. Isto poderia resultar em resultados inesperados. <br/> <source><![CDATA[ package demo; public class Base { private int field1 = 1; public int getField1( ) { return field1; } } --outro arquivo fonte-- package demo; public class Derived extends Base { private int field1 = 2; public int getField1() { return field1; } } --outro arquivo fonte -import demo.*; class Application { public static void main(String arg[]) { Base b = new Base(); Derived d = new Derived(); // erro de compilação //System.out.println(b.field1 + "," + d.field1); // imprime: 1,2 System.out.println(b.getField1() + "," + d.getField1()); b = d; // erro de compilação //System.out.println(b.field1 + "," + d.field1); // imprime: 2,2 System.out.println(b.getField1() + "," + d.getField1()); } }
Dada a superclasse B e a subclasse D, uso de "super" em D é o mesmo que a expressão "((B)this)", mas deve usar o "super".
Exemplo:
class Base { public int field1 = 1; } class Derived extends Base { public int field1 = 2; public void x() { super.field1 = this.field1; (use assim) // prints: true System.out.println(((Base)this).field1 == this.field1);(Não use) } }
Isto evita confusão e ajuda a prevenir erros lógicos.
Exemplo:
package com.acme.dados; public class MPC { void method (int i, int j) {} // VIOLACAO void j() {} private int i = 0; }
Reparar:
package com.acme.dados; public class MPCFixed { void method (int first, int second) {} // INDICADO void j() {} private int i = 0; }
Para qualquer classe que tenha uma superclasse(que não seja Object), sempre será necessário explicitar a inicialização da superclasse. Atributos que precisam ser inicializados devem ser feito no construtor.
Isso auxilia o reuso de construtores complexos para construtores simples(com poucos ou nenhum argumento).
Exemplo:
class Person { String name; String address; int age; // construtor padrão Person() { // reuso de construtor this( "--Unknown --", "--Unknown --", -1 ); } // construtor Person( String name, String address, int age ) { name = name; address = address; age = age; } } class Employee extends Person { int _id; // construtor padrão Employee() { // reuso de construtor (não use) this( "--Unknown --", "--Unknown --", -1, -1 ); --ou -// melhor forma (use essa) super( ); id = -1; } Employee( String name, String address, int age, int id ) { // explicite a inicialização da superclasse super( name, address, age ); id = id; } }
Atributos estáticos podem somente ser inicializados uma única vez na classe. Isto é feito quando a classe é carregada. Atribua um valor inicial para a declaração ou inclua um bloco estático caso seja necessário algum processo para obter o valor inicial(ex: um loop).
Exemplo:
class MyClass { // inicialização estática static int s = 10; // inicialização dinâmica, implied = 0 int v; // inicialização dinâmica char c = ' '; MyClass( ) { // inicialização dinâmica v = 10; } } --ou -class MyClass { // inicialização estática, valor = null static int[] s; // inicialização dinâmica, valor = 0 int v; // inicialização dinâmica, valor = ' char c; }
Construtores são chamados freqüentemente. Portanto eles devem ser eficientes. Use construtores para inicialização de construtores. Funções extensivas devem ser deixadas para ações realizadas por outros métodos que são explicitamente chamados após a construção.
Exemplo:
class File { String name; int mode; : public static final int INPUT = 1; public static final int OUTPUT = 2; public static final int INOUT = 3; static final int CLOSED = 0; // apenas seta o parâmetro, não abre o arquivo public File( String newName ) { name = newName; mode = CLOSED; } // realizar fechamento protected void finalize( ) { close(); } public boolean open( int newMode ) { // limpa qualquer atividade anterior close( ); mode = newMode; // preenchimento para uma nova abertura } public boolean close( ) { if(mode != CLOSED) { mode = CLOSED; } } }
Prover um construtor padrão private para classes utilitárias Classes utilitárias somente contem métodos e variáveis estáticas. Estas classes devem conter um construtor private para evitar que a classe possam ser instanciadas.
Exemplo:
package com.acme.util; public class UCDC { // VIOLACAO: implica no construtor padrão ser "public" public static String s = "foo"; public static String getS() { return s; } }
package com.acme.util; public class UCDCFixed { private UCDCFixed () {} // INDICADO public static String s = "foo"; public static String getS() { return s; } }
Mudar a visibilidade de um método nos diferentes níveis hierárquicos de classes(subclasses) pode resultar em checagem de acesso inconsistente.
Somente é permitido em Java que subclasses mudem a visibilidades de métodos herdados (ex., private -protected -public) para uma visibilidade menos restrita (ex., public -protected -private).
Exemplo:
class Base { private void foo() { ... } } class Derived extends Base { public void foo() { ... } } class Application { static void func() { Derived d = new Derived(); Base b = d; // OK d.foo(); // erro de compilação b.foo(); } }
Crie todos os atributos como private e provenha métodos de acesso para eles. Isso permite uma maior flexibilidade para mudanças de implementação.
Exemplo:
class MyClass { // data member private MyType data; public MyType getData( ) { return data; } public void setData( MyType newData ){ data = newData; } }
Diferentes níveis de acesso podem ser conseguidos pela mudança de visibilidade nos tipos de acesso
dos métodos.
class MyClass { // data atributo private MyType data; // data atributo private MyType data2; public MyType getData( ) { return data; } protected void setData ( MyType newData ){ data = newData; } public MyType getData2( ) { return data2; } private void setData2( MyType newData2 ) { data2 = newData2; } }
Aqui o valor do atributo data é somente de leitura para todas as classes, exceto as subclasses enquanto data2 é somente de leitura para todas as classes.
Quando implementar métodos modificadores que recebem diferentes tipos de parâmetros, use o
setter padrão.
class MyClass { // data member private String data; public MyType getData( ) { return data; } protected void setData ( String newData ){ data = newData; } // use setter padrão public void setData(int newData ) { setData(new String(newData)); } }
Todos os acessos aos atributos de uma classe devem ser feitos através dos métodos acessores(setter e getter).
Concidere a criação de atributos de abstratos, especialmente em classes abstratas. Isso permite que implementações de atributos sejam mudadas nas subclasses.
Exemplo:
abstract class MyClass { // get public abstract MyType data( ); // set public abstract void data( MyType x ); } class MySubClass extends MyClass { AnotherType data; public MyType getData( ) { return ( MyType ) data; } public void setData( MyType x ) { data = ( AnotherType )x; } }
Mantenha o tempo de vida de um objeto o mais curto possível. Coloque a declaração do atributo no inicio dos blocos aninhados. Se um operador new é usado, após o uso do objeto atribua null para ele.
Java permite cinco tipos de visibilidade:
private
Somente métodos declarados na classe podem acessar esses atributos, esses atributos são encapsulados
pelo uso do private, e devem ser acessados através de métodos públicos de acesso.
Aninhamento em mais de dois níveis podem ser difíceis de compreender.
Exemplo:
package com.acme.dados; public class LEVEL { class Level1 { class Level2 { class Level3 { // VIOLACAO private boolean _isClosed = false; } private int _count = 0; } private int _size = 0; } private int _length = 0; }
package com.acme.dados; public class LEVELFixed { class Level1 { class Level2 { private int _count = 0; } private int _size = 0; } private int _length = 0; } Level3Fixed.java' Segundo arquivo ': package com.acme.dados; class Level3Fixed { private boolean _isClosed = false; }
Reusar o máximo. Códigos comuns a classes do mesmo tipo devem ser colocados em uma superclasse e essa passar a ser pai das outras classes. Se necessário às subclasses podem realizar override dos métodos herdados.
Exemplo:
Contraste:
import java.util.Vector; // de Integers class IntCollection { protected Vector elements; // bubble sort public void sort( ) { int i, j, size = elements.size(); // para cada elemento for( i = 0; i < size -1; i++ ) { // comparação for( j = i + 1; j < size; j++ ) { Integer t1 = (Integer)elements.elementAt( i ); Integer t2 = (Integer)elements.elementAt( j ); // if( t1.intValue() t2.intValue( ) ) { elements.setElement( i, t2 ); elements.setElement( j, t1 ); } } } } // adiciona elementos public void add( int x ) { elements.addElement( new Integer( x ) ); } } // de Strings class StringCollection extends Collection { protected Vector elements; // bubble sort public void sort( ) { int i, j, size = _elements.size( ); // for each element for( i = 0; i < size -1; i++ ) { // compare against all others for( j = i + 1; j < size; j++ ) { String t1 = (String)_elements.elementAt( i ); String t2 = (String)_elements.elementAt( j ); // check reorder needed if(t1.compareTo( t2 ) 0) { // swap elements elements.setElement( i, t2 ); elements.setElement( j, t1 ); } } } } // adiciona elemento public void add( int x ) { elements.addElement( x ); } }
Métodos comuns devem ser quebrados em métodos private ou mesmo protected.
Exemplo:
Compare:
import java.util.Vector; class List { private Vector elements; : // imprimir saída na stream public void print() { System.out.print("List("); for(int i = 0; i < elements.size(); i++) System.out.print((i == 0 ? "" : ", ") + elements.elementAt(i)); System.out.print(")"); } // imprimir erro na stream public void dump() { System.err.print("List("); for(int i = 0; i < elements.size(); i++) System.err.print((i == 0 ? "" : ", ") + elements.elementAt(i)); System.err.print(")"); } }
import java.util.Vector; import java.io.PrintStream; class List { private Vector elements; : protected void dumpOn(PrintStream s) { s.print("List("); for(int i = 0; i < elements.size(); i++) s.print((i == 0 ? "" : ", ") + elements.elementAt(i)); s.print(")"); } // imprime a saída em uma stream public void print() { dumpOn(System.out); } // imprime erro em uma stream public void dump() { dumpOn(System.err); } }
Para realizar chamada a métodos estáticos não instancie objetos.
Exemplo:
class MyClass { // class method public static void sfunc(); // instance method public void func(); } class Application { void func( MyClass x ) { // non-static method call x.func(); // use essa chamada MyClass.sfunc(); // não use essa chamada x.sfunc(); } }
Java permite a utitilzação de vários métodos com o mesmo nome, somente sendo necessário a troca
de assinatura. Isso é chamado de overloading.
Example:
class MyClass { : public void someMethod( String arg ) { // string specific processing } public void someMethod( Integer arg ) { // integer specific processing } public void someMethod( Number arg ) { // number specific processing } : }
Não use mais de um tipo de retorno e não retorne null para as referencias de array. É preferível
retornar sempre um array com tamanho zero.
Example:
class SomeClass { : public int[] someMethod( ) { : } : }
Exceptions também reduzem a quantidade de checagem de código retornado para verificação de
erros.
Nota: A clausula finally reduz código duplicado, como fechamento de arquivos. Considere:
import java.io.*; class SomeClass { public void doIt(String name)throws Exception,FileNotFoundException { FileInputStream fis; // declaração de IOException // e FileNotFoundException fis = new FileInputStream(name); : } } --ou -public void doIt(String name) { try { FileInputStream fis; fis = new FileInputStream(name); : } catch(IOException ex) { // gerencia exception aqui } } }
É sempre uma boa idéia inserir gerenciamento de erro dentro de cada bloco "catch".
Exemplo:
package com.acme.dados; public class AECB { public void method () { try { System.in.read (); } catch (java.io.IOException e) { // VIOLACAO } } }
package com.acme.dados; public class AECBFixed { public void method () { try { System.in.read (); } catch (java.io.IOException e) { System.out.println("Descriptive error"); // INDICADO } } }
Referencias em Java são inicializadas com null se não explicitado nenhum valor.
Exemplo:
import java.util.HashTable; class SomeClass { static HashTable dictionary; int value; : public SomeClass(String key, int value) { dictionary.put(key, (Object)new Integer(_value = value)); } : public void printKey(String key) { : System.out.println("Value of " + key + " is " + (Integer)(dictionary.get(key)).intValue()); : } : }
Será lançada uma NullPointerException caso não seja achada uma chave durante a utilização do get(). Uma
alternativa é:
// : public void printKey(String key) { : Integer i = _dictionary.get(key); System.out.println("Value " + key + " is " + (i != null ? i.intValue() "undefined"); : } // :
Sempre que possível, use uma classe equivalente para o tipo primitivo em questão. Por exemplo: Integer para int, Character para char, etc. Pois dessa forma você terá mais funções e poderão ser utilizados como objetos.
Atributos de uma classe devem sempre ser objetos ou classe de wrapper( Integer, Double, etc ).
Exemplo
class SomeClass { : void someMethod() { // array de ints int[] intArray = new int[10] // array de Integers Integer[] integerArray = new Integer[10]; // vector de Objects (regardless of name) Vector integerVector = new Vector(); intArray[0] = 1; // erro de compilação intArray[1] = new Integer(1); // erro de compilação integerArray[1] = 1; integerArray[1] = new Integer(1); // erro de compilação integerVector.addElement(1); integerVector.addElement(new Integer(1)); integerVector.addElement(new Integer(1).toString()); } : }
Se um dos operandos do sinal (+) for uma String, então ambos precisam ser. Java automaticamente convertera o outro operador que não for String. O resultado dessa conversão pode ser definido. Use parêntesis para controlar essa definição. Veja os exemplos:
Exemplo:
: String x; // objeto de algum tipo Object o = new Blivit(...); // x é "33" : gerado como Integer(1 + 2).toString() + "3" x = 1 + 2 + "3"; // x é "123": gerado como ("1" + Integer(2).toString()) + Integer(3).toString() x = "1" + 2 + 3; // x is "123": gerado como ("1" + Integer(2).toString()) + Character('3').toString() x = "1" + 2 + '3'; // x é "15" : gerado como "1" + Integer(2 + 3).toString() x = "1" + (2 + 3); // x é "23" : gerado como ("" + Integer(2).toString()) + "3" x = "" + 2 + "3"; // erro de compilação, + não defindo para os referente tipos x = o + 1; // erro de compilação, + não defindo para os referente tipos x = 1 + o; // x é "Blivit(...)1" : gerado como o.toString() + Integer(1).toString() x = o.toString() + 1; // x é "1Blivit(...)" : gerado como "1" + o.toString() x = "1" + o; :
Java aceita array multidimensional, eles armazenam objetos do mesmo tipo, mas não precisa ter o mesmo tamanho.
Exemplo:
: int[][] aai = new int[100][50]; --equivalente -int[][] aai = new int[100][]; // aai.length == 100 for(int temp = 0; temp < aai.length; temp++) // aai[*].length == 50 aii[temp] = new int[50]; :
: int[][] aai = new int[100][]; // aai.length == 100 for(int temp = 0; temp < aai.length; temp++) // aai[0].length == 1 // aai[1].length == 2 // aai[2].length == 3 aii[temp] = new int[temp + 1]; : // aai[99].length == 100
class SomeClass { public static void initAII(int[][] aii) { // check dimension 1 for(int i = 0; i < aii.length; i++) // check each dimension 2 for(int j = 0; j < aii[i].length; j++) aii[i][j] = i * j; } }
Isso é uma boa pratica e alguns drivers requerem que isso seja feito, para auxiliar na "limpeza" dos recursos.
Exemplo
class Foo { ... Connection conn = null; Statement stmt = null try { conn = DriverManager.getConnection( url, user, pwd ); Statement stmt = con.createStatement( ); ... // sua lógica de negócio aqui ... } catch (SQLException e) { e.printStackTrace(); } finally { // limpa todos os recursos usados no statement stmt.close(); // limpa todos os recursos usados e fecha a sessão conn.close(); } }
Concatenação de String podem refletir em impactos na performance. Lembre String são objetos imutáveis, dessa forma, a concatenação resulta na criação de objetos temporários. Uma solução para isso é utilizar o java.lang.StringBuffer.
O operador de negação prejudica a legibilidade do código.
Exemplo:
package com.acme.dados; public class DUN { boolean method (boolean a, boolean b) { // VIOLACAO if (!a) return (!a && !b); else return !b; } }
package com.acme.dados; public class DUNFixed { boolean method (boolean a, boolean b) { if (a) return !b; else return (! (a || b) ); } }
Valores estáticos podem ser modificados por outros usuários da classe, entretanto um atributo
estático pode com isso conter valor diferente ao esperado pelos usuários da classe.
Exemplo:
package com.acme.dados; public class NFS { static int max = 10; int size = max; // VIOLACAO }
Atribuições a parâmetros de métodos que são do tipo "referencia" podem proporcionar erro, pois
altera o valor no método chamador.
Example.
package com.acme.dados; class AFP { void method (int[] a) { a[0] = 0; // VIOLACAO } }
package com.acme.dados; class AFPFixed { void method (int[] a) { int[] copy = new int [a.length]; System.arraycopy (a, 0, copy, 0, a.length); copy[0] = 0; // INDICADO } }
Exemplo
package com.acme.dados; abstract class ASFI { // VIOLACAO abstract void method(); final static String ID = "final static string"; }
package com.acme.dados; interface ASFIFixed { // INDICADO void method(); String ID = "MISC_ASFI"; // Não precisa definir "final static", isto esta implícito }
O objetivo do construtor é inicializar um objeto. Se o método puder ser acessado por uma subclasse,
poderá ser feito um override, gerando um resultado não esperado.
Exemplo:
package com.acme.dados; public class CTOR { public CTOR () { _size = readSize(); // VIOLACAO } public int readSize () { return fis.read (); } private FileInputStream fis = new FileInputStream ("data.out"); private int _size; }
package com.acme.dados; public class CTORFixed { public CTORFixed () { _size = readSize (); } private int readSize () { // INDICADO return fis.read (); } private FileInputStream fis = new FileInputStream ("data.out"); private int _size; }
Suponha que você tenha uma variável 'str'. Se você chamar str.equald("literal"), então você precisa primeiro ter certeza que a variável não esta null. Se você utiliza a chamada "literal".equals(str), você não precisa se preocupar em verificar se a variável esta null, isso torna o código mais conciso.
Exemplo:
package com.acme.dados; public class ISEM { public boolean isHello (String str) { return (s != null && s.equals("Hello")); // VIOLACAO } }
package com.acme.dados; public class ISEMFixed { public boolean isHello (String str) { return "Hello".equals (str); // INDICADO } }
Copiando um literal para dentro de um objeto String "gasta" tempo e é redundante
Exemplo:
package com.acme.dados; class ACDO { void method () { System.out.println (_s); } private String _s = new String ("ACDO"); // VIOLACAO }
package com.acme.dados; class ACDOFixed { void method () { System.out.println (_s); } private String _s = "ACDO"; // INDICADO }
Java prove as classes de wrapping para tipos primitivos. Todas essas classes provém o método
estático toString() para converter tipos primitivos em String. Mas essas classes fornecem métodos estáticos
que evitam a criação desnecessária de objetos.
Exemplo:
package com.acme.dados; public class AUTP { String foobar (int x) { return new Integer (x).toString (); // VIOLACAO } }
package com.acme.br; class AUTPFixed { String foobar (int x) { return Integer.toString (x); // INDICADO } }
O objeto Date contem muitos campos, e com isso utiliza muito espaço. Caso seja necessário utilizar
um array de objetos Date, substitua pelo uso de um array de long, onde somente serão armazenados os valores
"long" que representam as datas.
Exemplo:
package com.acme.dados; import java.util.Date; public class DUD { Date d[]; // VIOLACAO }
Reparar:
O uso de um array do tipo long e mais eficiente do que um array do tipo Date.
package com.acme.dados; import java.util.Date; public class DUD { long d[]; // INDICADO }
Objetos que representem coleções estáticas(Vector, Hashtable, etc) podem armazenar vários outros
objetos, fazendo com que possa ocorrer memory leaks. Se você coloca vários pequenos objetos em uma
coleção estática, esses objetos serão referenciados por essa coleção por toda a "vida" do programa se você
esquecer de remove-los da coleção quando tiver terminado o uso com esses objetos. Se você tiver removido
todas as referencias para esses pequenos objetos, poderá ser difícil ver que ainda existe referencia feita pela
coleção.
Exemplo
package com.acme.dados; import java.util.Vector; public class STV { public static Vector vector = new Vector (); // VIOLACAO void addToVector () { Object o = new Object (); vector.add (o); // este objeto temporariamente não será liberado } }
package com.acme.dados; import java.util.Vector; public class STVFixed { public static void addToVector () { // verifica o tamanho máximo do Vector antes de chamar 'add()'. if (vector.size() < MAX_SIZE) { // INDICADO Object o = new Object (); vector.add(o); } else { System.err.println("vector MAX_SIZE excedido."); } } public static Vector vector = new Vector (5); // INDICADO public static final int MAX_SIZE = 100; }
Alguns compiladores rodam mais rápido dessa forma, além de ser mais conciso o código.
Exemplo:
package com.acme.dados; public class AAS { void method () { int i = 0; i = i -1; // VIOLACAO } }
package com.acme.dados; public class AAS { void method () { int i = 0; i -= 1; } }
A menos que o compilador otimize-o, a condição do loop será calculada para cada iteração sobre o
loop. Se o valor de condição não sofre mudança, ele será executado mais rápido do que a chamada de um
método fora do loop.
Exemplo:
package com.acme.dados; import java.util.Vector; class CEL { void method (Vector vector) { for (int i = 0; i < vector.size (); i++) // VIOLACAO { System.out.println(i); } } }
package com.acme.dados; class CELFixed { void method (Vector vector) { int size = vector.size (); for (int i = 0; i < size; i++) // INDICADO { System.out.println(i); } } }
O operador condicional prover uma simples expressão que executa um ou outro statement, baseado
em uma expressão boleana. Este resultado é uma expressão mais compacta.
Exemplo
package com.acme.dados; public class IF { public int method (boolean isDone) { if (isDone) // VIOLACAO return 0; else return 10; } }
public class IFFixed { public int method (boolean isDone) { return isDone ? 0 : 10; } }
O operador condicional prover uma simples expressão que executa um ou outro statement, baseado
em uma expressão boleana. Este resultado é uma expressão mais compacta.
Exemplo:
package com.acme.dados; public class IFAS { void method(boolean isTrue) { if (isTrue) // VIOLACAO _value = 0; else _value = 1; } private int _value = 0; }
package examples.Regras.opt; public class IFASFixed { void method(boolean isTrue) { _value = (isTrue ? 0 : 1); // INDICADO } private int _value = 0; }
O uso de startsWith() executa várias funções para preparar a comparação do prefixo com outra
string, as quais se tornariam desnecessárias quando apenas se que comparar um caractere com outro.
Exemplo:
package com.acme.dados; public class PCTS { private void method(String s) { if (s.startsWith("a")) // VIOLACAO System.out.println("starts with a."); } }
package examples.Regras.opt; public class PCTSFixed { private void method(String s) { if (s.length () > 0 && s.charAt(0) == 'a') // INDICADO System.out.println("starts with a."); } }
Métodos "synchronized" são "caros", a invocação desses métodos dentro de um loop não é
recomendada.
Exemplo:
package com.acme.dados; public class SYN { public synchronized void method () { } private void test () { for (int i = 0; i < 100; i++) { method (); // VIOLACAO } } }
O uso de blocos "try/catch" dentro de loops pode representar perda de performance do código
executado.
Exemplo:
package com.acme.dados; import java.io.FileInputStream; public class TRY { void method (FileInputStream fis) { for (int i = 0; i < size; i++) { try { // VIOLACAO _sum += fis.read (); } catch (Exception e) {} } } private int _sum; }
package com.acme.dados; import java.io.FileInputStream; public class TRY { void method (FileInputStream fis) { try { // INDICADO for (int i = 0; i < size; i++) { _sum += fis.read (); } } catch (Exception e) {} } }
Todas as classes são direta ou indiretamente herança da classe Object. Também, quaisquer subclasses
são implicitamente do mesmo tipo da sua superclasses e das interfaces que ela implementa. Entretanto,
operações de cast para superclasses ou interfaces implementadas são desnecessárias.
Exemplo:
package com.acme.dados; class UNC { String _id = "UNC"; } class Dog extends UNC { void method () { Dog dog = new Dog (); UNC animal = (UNC)dog; // VIOLACAO Object o = (Object)dog; // VIOLACAO } }
class DogFixed extends UNC { void method () { Dog dog = new Dog(); UNC animal = dog; // INDICADO Object o = dog; // INDICADO } }
Quando varáveis são acessadas freqüentemente, considere de onde elas são acessadas. É uma
variável estática, local, ou a instancia de objeto? Variáveis estáticas e instancias, custam duas ou três vezes
mais tempo para serem acessadas do que variáveis locais.
Exemplo:
package com.acme.dados; public class USV { void addSum (int[] values) { for (int i=0; i < value.length; i++) _sum += value[i]; // VIOLACAO. } private int _sum; }
package com.acme.dados; public class USVFixed { void addSum (int[] values) { int sum = _sum; // variavel local temporaria. for (int i=0; i < value.length; i++) sum += value[i]; _sum = sum; } private int _sum; }
Por favor, dê a sua opinião a respeito do conteúdo apresentado. Correções, sugestões ou qualquer outra informação relevante é de suma importância para nós e será muito bem vinda.
Contato: owner@hotwork.dev.java.net