Na programação Java, alguns conhecimentos não podem ser aprendidos apenas por meio de especificações de idiomas ou documentação da API padrão. Neste artigo, tentarei coletar alguns dos idiomas mais usados, especialmente aqueles que são difíceis de adivinhar.
Coloquei todo o código neste artigo em locais públicos. Você pode copiar e modificar qualquer trecho de código de acordo com suas preferências, sem nenhuma credenciais.
Implementar iguals ()
classe pessoa {nome da string; Int Aniversário do ano; byte [] cru; public boolean é igual (objeto obj) {if (! obj instância de pessoa) retorna false; Pessoa outra = (pessoa) obj; retornar name.equals (outros.name) && aniversárioyear == outros.birthyear && Arrays.equals (Raw, outros.raw); } public int hashCode () {...}} O parâmetro deve ser do objeto de tipo, não de uma classe periférica.
Foo.Equals (NULL) deve retornar falso e não pode lançar o NullPointerException. (Observe que a instância nula de qualquer classe sempre retorna falsa, para que o código acima possa ser executado.)
A comparação dos domínios do tipo básico (por exemplo, int) é usada ==, e a comparação de domínios de matriz de tipos básicos é usada por matrizes.equals ().
Ao substituir Equals (), lembre -se de substituir o hashcode () de acordo, para ser consistente com o igual ().
Referência: java.lang.object.equals (objeto).
Implementar hashcode ()
classe pessoa {string a; Objeto B; byte c; int [] d; public int hashCode () {return a.hashcode () + b.hashcode () + c + arrays.hashcode (d); } public boolean é igual (objeto o) {...}} Quando os objetos x e y tiverem x.equals (y) == true, você deve garantir que x.hashcode () == y.hashcode ().
De acordo com a proposição inversa, se x.hashcode ()! = Y.hashcode (), então x.equals (y) == false devem ser verdadeiros.
Você não precisa garantir que x.hashcode ()! = Y.hashcode () quando x.equals (y) == false. No entanto, isso melhora o desempenho da tabela de hash se você puder fazer o maior tempo possível.
A implementação legal mais simples do hashCode () é simplesmente retornar 0; Embora essa implementação esteja correta, isso fará com que as estruturas de dados como o Hashmap sejam executadas muito lentamente.
Implementar compareto ()
a classe Pessoa implementa comparável <Person> {String FirstName; String lastName; int aniversário; // Compare pelo primeiro nome, quebre os laços por último nome, finalmente quebre os laços com a data de nascimento public int compareto (pessoa outra) {if (primeironame.compareto (outros.firstname)! = 0) retornar primeironame.compareto (outros.firstname); caso contrário, if (lastName.compareto (outros.lastName)! = 0) return lastName.compareto (outros.lastName); senão se (data de nascimento <outro.birthate) retornar -1; senão se (data de nascimento> outros.birthate) retornar 1; caso contrário, retorne 0; }} Sempre implemente a versão genérica comparável em vez do tipo primitivo comparável. Porque isso pode salvar o volume de código e reduzir o aborrecimento desnecessário.
Apenas se preocupe com os sinais (negativos/zero/positivos) que retornam o resultado, seu tamanho não importa.
Comparator.compare () A implementação é semelhante a esta.
Implementar clone ()
A classe valores implementa clonável {string abc; duplo foo; int [] barras; Data contratada; valores públicos clone () {try {valores result = (valores) super.clone (); resultado.bars = resultado.bars.clone (); resultado.hired = resultado.hired.clone (); resultado de retorno; } catch (clonenotsupportEdException e) {// impossível jogue novo assertionError (e); }}} Use Super.Clone () para tornar a classe de objeto responsável pela criação de novos objetos.
Os domínios do tipo básico foram copiados corretamente. Novamente, não precisamos clonar tipos imutáveis, como String e Biginteger.
Realize manualmente cópia profunda de todos os campos de tipos não primitivos (objetos e matrizes).
A classe clonável é implementada e o método clone () nunca deve lançar uma clonenotsupportedException. Portanto, você precisa pegar essa exceção e ignorá -la ou envolvê -la com uma exceção desmarcada.
É bom e legal implementar manualmente o método clone () sem usar o método object.Clone ().
Use StringBuilder ou StringBuffer
// junção (["a", "b", "c"]) -> "a e b e c" string junção (list <strs> strs) {stringbuilder sb = new stringbuilder (); booleano primeiro = true; for (string s: strs) {if (primeiro) primeiro = false; else sb.append ("e"); sb.append (s); } Return sb.toString ();} Não use concatenação de string duplicada como esta: s += item, porque sua eficiência de tempo é O (n^2).
Ao usar o StringBuilder ou StringBuffer, você pode usar o método Append () para adicionar texto e usar o método ToString () para obter o texto inteiro conectado.
É dada prioridade ao StringBuilder, pois é mais rápido. Todos os métodos do StringBuffer são sincronizados e você geralmente não precisa de um método sincronizado.
Gerar números inteiros aleatórios em um intervalo
Rand Rand = novo aleatório (); // entre 1 e 6, incluindo o diceroll () {return rand.nextint (6) + 1;} Sempre use o método Java API para gerar um número aleatório na faixa de números inteiros.
Não tente usar o Math.abs (Rand.NextInt ()) %N para esses usos incertos, porque seus resultados são tendenciosos. Além disso, seu valor de resultado pode ser negativo, como quando Rand.NextInt () == Integer.min_Value.
Use iterator.remove ()
filtro void (list <string> list) {for (iterator <string> iter = list.iterator (); iter.hasnext ();) {string item = iter.next (); if (...) iter.remove (); }}O método Remone () atua no método de entrada retornada recentemente do método Next (). Cada entrada pode usar apenas o método Remow () uma vez.
Retornar string
String reverse (string s) {return stringbuilder (s) .versevers (). Tostring ();}Esse método provavelmente deve ser adicionado à biblioteca padrão Java.
Inicie um tópico
Os três exemplos a seguir fazem a mesma coisa de maneiras diferentes.
Como implementar o Runnnable:
void startathread0 () {new Thread (new MyRunnable ()). start ();} classe MyRunnable implementos runnable {public void run () {...}}Como herdar thread:
void startathread1 () {new mythread (). start ();} classe mythread estende thread {public void run () {...}}Como herdar thread anonimamente:
void startathread2 () {new thread () {public void run () {...}} .start ();}Não chame o método run () diretamente. O método Thread.start () é sempre chamado, o que cria um novo thread e faz com que o thread recém -criado seja chamado de execução ().
Use Try-Finally
Exemplo de E/S.
void writeStuff () lança ioexception {outputStream out = new FileOutputStream (...); tente {out.write (...); } finalmente {out.close (); }}Exemplo de bloqueio:
vazio dowithlock (trava) {Lock.acquire (); tente {...} finalmente {Lock.Release (); }} Se a declaração antes da tentativa não executar e uma exceção for lançada, o bloco de declaração finalmente não será executado. Mas não importa o quê, neste exemplo, não há necessidade de se preocupar com a liberação de recursos.
Se a instrução no Block Try Declaração lançar uma exceção, a execução do programa saltará para o bloco de declaração finalmente para executar o maior número possível de instruções e depois para fora desse método (a menos que esse método tenha outro bloco de declaração periférico finalmente).
Leia dados de bytes do fluxo de entrada
InputStream em = (...); tente {while (true) {int b = in.read (); if (b == -1) quebra; (... Processo B ...)}} Finalmente {in.close ();}O método read () retorna o próximo número de bytes lido no fluxo (0 a 255, incluindo 0 e 255) ou retorna -1 quando o final do fluxo é atingido.
Leia os dados do bloco do fluxo de entrada
InputStream em = (...); tente {byte [] buf = new Byte [100]; while (true) {int n = in.read (buf); if (n == -1) quebra; (... Processar buf com deslocamento = 0 e comprimento = n ...)}} finalmente {in.close ();}Lembre -se de que o método read () não preenche necessariamente todo o BUF, então você deve considerar a duração do retorno na lógica de processamento.
Leia o texto de um arquivo
BufferredReader in = new BufferredReader (new InputStreamReader (new FileInputStream (...), "utf-8")); tente {while (true) {string line = in.readline (); if (line == null) quebra; (... linha de processo ...)}} finalmente {in.close ();} A criação do objeto BufferErader parece ser muito detalhado. Isso ocorre porque Java trata bytes e caracteres como dois conceitos diferentes (isso é diferente de C).
Você pode usar qualquer tipo de inputStream em vez de FileInputStream, como o soquete.
BufferredReader.readline () retorna nulo quando o final do fluxo é atingido.
Para ler um caractere de cada vez, use o método leitor.read ().
Você pode usar outras codificações de caracteres sem UTF-8, mas é melhor não fazê-lo.
Escreva texto em um arquivo
PrintWriter Out = new PrintWriter (new OutputStreamWriter (new FileOutputStream (...), "UTF-8")); tente {out.print ("hello"); out.print (42); out.println ("mundo!");} finalmente {out.close ();} A criação de objetos de PrintWriter parece ser muito detalhada. Isso ocorre porque Java trata bytes e caracteres como dois conceitos diferentes (isso é diferente de C).
Assim como o System.out, você pode usar o print () e o println () para imprimir vários tipos de valores.
Você pode usar outras codificações de caracteres sem UTF-8, mas é melhor não fazê-lo.
Valor de verificação defensiva
int fatorial (int n) {if (n <0) lançar novas ilegalargumentException ("indefinido"); caso contrário, se (n> = 13) lança nova aritmeticexception ("Overflow de resultado"); else if (n == 0) retornar 1; caso contrário, retorne n * fatorial (n - 1);} Não pense que os valores de entrada são positivos, pequenos o suficiente etc. para detectar essas condições explicitamente.
Uma função bem projetada deve ser capaz de executar corretamente para todos os valores de entrada possíveis. Certifique -se de que todas as situações sejam levadas em consideração e que não haja saída errada (como transbordamento).
Objetos de teste preventivo
int findIndex (list <string> list, string alvo) {if (list == null || Target == null) lança novo nullPointerException (); ...}Não pense que os parâmetros do objeto não serão nulos. Para detectar explicitamente essa condição.
Índice de matriz de detecção preventiva
Void Frob (byte [] B, Int Index) {if (b == null) lança novo NullPointerException (); if (índice <0 || índice> = b.length) lança novo indexOutOfBoundSexception (); ...}Não pense que o índice da matriz dado não cruzará os limites. Para detectá -lo explicitamente.
Intervalo de matriz de detecção preventiva
void frob (byte [] b, int off, int len) {if (b == null) lança novo nullPointerException (); if (off <0 || off> b.Length || len <0 || b.Length - Off <len) lança novo indexOutOfBoundSexception (); ...}Não pense que o intervalo de matriz fornecido (por exemplo, a partir de desativado, a leitura de elementos) não vai além dos limites. Para detectá -lo explicitamente.
Preencha os elementos da matriz
Usando loops:
// preencha cada elemento da matriz 'a' com 123byte [] a = (...); para (int i = 0; i <a.length; i ++) a [i] = 123;
Métodos (preferenciais) para usar a biblioteca padrão:
Arrays.fill (a, (byte) 123);
Copie um elemento de matriz em um intervalo
Usando loops:
// copie 8 elementos da matriz 'a' começando no deslocamento 3 // para matar 'b' a partir do deslocamento 6, // assumindo 'a' e 'b' são distintos Arraysbyte [] a = (...); byte [] b = (...); para (int i = 0; i <8; i ++) b [6 + i] = a [3 + i];
Métodos (preferenciais) para usar a biblioteca padrão:
System.arraycopy (a, 3, b, 6, 8);
Redimensionar a matriz
Use loops (dimensionando):
// Faça a matriz 'a' maior para newlenbyte [] a = (...); byte [] b = novo byte [newlen]; para (int i = 0; i <A.Length; i ++) // sobe até o comprimento de a b [i] = a [i]; a = b;
Use loops (reduz o tamanho):
// Faça a matriz 'a' menor para newlenbyte [] a = (...); byte [] b = novo byte [newlen]; para (int i = 0; i <b.length; i ++) // sobe até o comprimento de b [i] = a [i]; a = b;
Métodos (preferenciais) para usar a biblioteca padrão:
a = Arrays.copyof (a, newlen);
Embalando 4 bytes em um int
int packbigendian (byte [] b) {return (b [0] & 0xff) << 24 | (b [1] e 0xff) << 16 | (B [2] e 0xFF) << 8 | (b [3] & 0xff) << 0;} int packLittleendian (byte [] b) {return (b [0] & 0xff) << 0 | (B [1] & 0xff) << 8 | (B [2] e 0xFF) << 16 | (b [3] & 0xff) << 24;}Decompor int em 4 bytes
byte[] unpackBigEndian(int x) { return new byte[] { (byte)(x >>> 24), (byte)(x >>> 16), (byte)(x >>> 8), (byte)(x >>> 0) };} byte[] unpackLittleEndian(int x) { return new byte[] { (byte)(x >>> 0), (byte)(x >>> 8), (byte) (x >>> 16), (byte) (x >>> 24)};}Sempre use o operador de deslocamento direito não assinado (>>>) para embrulhar os bits, não use o operador aritmético de deslocamento direito (>>).