En la programación de Java, algunos conocimientos no se pueden aprender solo a través de especificaciones de lenguaje o documentación de API estándar. En este artículo, intentaré recopilar algunos de los modismos más utilizados, especialmente aquellos que son difíciles de adivinar.
Puse todo el código en este artículo en lugares públicos. Puede copiar y modificar cualquier fragmento de código de acuerdo con sus preferencias, sin ninguna credencial.
Implementar igual ()
Persona de clase {nombre de cadena; int birthdayyear; byte [] crudo; public boolean iguales (objeto obj) {if (! obj instancia de persona) return false; Persona otra = (persona) obj; return name.equals (other.name) && birthrucyYear == OTRO.BIRTHYEAR && Arrays.equals (raw, other.raw); } public int hashcode () {...}} El parámetro debe ser de objeto tipo, no de una clase periférica.
foo.equals (nulo) debe devolver falso y no puede lanzar nullpointerexception. (Tenga en cuenta que la instancia nula de cualquier clase siempre devuelve falso, por lo que el código anterior se puede ejecutar).
La comparación de dominios de tipo básico (por ejemplo, int) se usa ==, y la comparación de dominios de matriz de tipo básico es utilizada por Arrays.Equals ().
Al sobrescribir iguales (), recuerde sobrescribir hashcode () en consecuencia, para ser consistente con iguales ().
Referencia: java.lang.object.equals (objeto).
Implementar hashcode ()
persona de clase {cadena a; Objeto B; byte c; int [] d; public int hashcode () {return a.hashcode () + b.hashcode () + c + arrays.hashcode (d); } public boolean igual (objeto o) {...}} Cuando los objetos x e y tienen x.equals (y) == verdadero, debe asegurarse de que x.hashcode () == y.hashcode ().
De acuerdo con la proposición inversa, si x.hashcode ()! = Y.hashcode (), entonces x.equals (y) == falso debe ser verdadero.
No necesita garantizar que x.hashcode ()! = Y.hashcode () cuando x.equals (y) == false. Sin embargo, esto mejora el rendimiento de la tabla hash si puede hacerlo el mayor tiempo posible.
La implementación legal más simple de HashCode () es simplemente devolver 0; Aunque esta implementación es correcta, esto hará que las estructuras de datos como el hashmap se ejecuten muy lentamente.
Implementar comparación ()
La persona de clase implementa comparable <Oll> {String FirstName; Cadena lastname; int cumpleaños; // Compare por FirstName, Break lazos de LastName, finalmente rompa los lazos por Birthdate public int Compareto (Person Other) {if (FirstName.Compareto (OTRO.FIRSTNAME)! = 0) return FirstName.compareto (OTRO.FIRSTNAME); else if (lastname.compareto (otro.lastname)! = 0) return lastname.compareto (other.lastName); else if (birthdate <otros.birthdate) return -1; else if (birthdate> other.birthdate) regreso 1; else regrese 0; }} Siempre implementa la versión genérica comparable en lugar de tipo primitivo comparable. Porque esto puede guardar el volumen del código y reducir la molestia innecesaria.
Solo se preocupan por los signos (negativos/cero/positivo) que devuelven el resultado, su tamaño no importa.
La implementación de comparador.compare () es similar a esta.
Implementar clone ()
Los valores de clase implementan clonables {String ABC; doble foo; int [] barras; Fecha contratada; Public Values clone () {try {valores resultado = (valores) super.clone (); result.bars = result.bars.clone (); result.hired = result.hired.clone (); resultado de retorno; } catch (clonenotsupportedException e) {// imposible tirar nueva afirmación (e); }}} Use super.clone () para hacer que la clase de objetos sea responsable de crear nuevos objetos.
Los dominios de tipo básico se han copiado correctamente. Nuevamente, no necesitamos clonar tipos inmutables como String y BigInteger.
Realice manualmente una copia profunda de todos los campos de tipos no propianos (objetos y matrices).
La clase clonable se implementa, y el método Clone () nunca debe arrojar una clonenotsupportedException. Por lo tanto, debe detectar esta excepción e ignorarla, o envolverla con una excepción sin control.
Es bien y legal implementar manualmente el método Clone () sin usar el método Object.Clone ().
Use StringBuilder o StringBuffer
// Join (["A", "B", "C"]) -> "A y B y C" String Join (List <String> Strs) {StringBuilder sb = new StringBuilder (); booleano primero = verdadero; for (cadena s: strs) {if (primero) primero = false; else sb.append ("y"); sb.append (s); } return sb.ToString ();} No use la concatenación de cadena duplicada como esta: s += item, porque su eficiencia de tiempo es O (n^2).
Al usar StringBuilder o StringBuffer, puede usar el método append () para agregar texto y usar el método toString () para obtener el texto completo conectado.
Se da prioridad a StringBuilder ya que es más rápido. Todos los métodos de StringBuffer están sincronizados, y generalmente no necesita un método sincronizado.
Generar enteros aleatorios en un rango
Rand rand = new Random (); // entre 1 y 6, incluido INST DICEROLL () {return rand.nextint (6) + 1;} Siempre use el método API Java para generar un número aleatorio en el rango de enteros.
No intente usar Math.Abs (Rand.NextInt ()) %N para estos usos inciertos, porque sus resultados son sesgados. Además, su valor de resultado puede ser negativo, como cuando 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 (); }}El método remove () actúa sobre la entrada recientemente devuelta del método Next (). Cada entrada solo puede usar el método Remout () una vez.
Cadena de retorno
String reverse (string s) {return new StringBuilder (s) .Reverse (). ToString ();}Este método probablemente debería agregarse a la biblioteca estándar Java.
Empiece un hilo
Los siguientes tres ejemplos hacen lo mismo de diferentes maneras.
Cómo implementar Runnnable:
void startathread0 () {new Thread (new Myrunnable ()). Start ();} class MyRunnable Implements runnable {public void run () {...}}Cómo heredar el hilo:
void startathread1 () {new MyThread (). Start ();} MyThread extiende el hilo {public void run () {...}}Cómo heredar el hilo de forma anónima:
void startathread2 () {new Thread () {public void run () {...}} .start ();}No llame al método Run () directamente. Siempre se llama el método Thread.Start (), que crea un nuevo hilo y hace que el hilo recién creado llame Run ().
Use el try-finalmente
Ejemplo de transmisión de E/S:
void writeStuff () lanza ioexception {outputStream out = new FileOutputStream (...); intente {out.write (...); } finalmente {out.close (); }}Ejemplo de bloqueo:
void dowithlock (bloqueo de bloqueo) {Lock.acquire (); intente {...} Finalmente {Lock.Release (); }} Si la declaración antes de la prueba no se ejecuta y se lanza una excepción, entonces el bloque de la declaración finalmente no se ejecutará. Pero no importa qué, en este ejemplo, no hay necesidad de preocuparse por la liberación de recursos.
Si la instrucción en el bloque de instrucción de prueba lanza una excepción, la ejecución del programa saltará al bloque de la declaración finalmente para ejecutar tantas declaraciones como sea posible, y luego saltar de este método (a menos que este método tenga otro bloque de instrucción finalmente periférico).
Leer datos de byte de la secuencia de entrada
InputStream in = (...); try {while (true) {int b = in.read (); if (b == -1) ruptura; (... proceso b ...)}} Finalmente {in.close ();}El método Read () devuelve el siguiente número de bytes leídos de la transmisión (0 a 255, incluidas 0 y 255), o devuelve -1 cuando se alcanza el final de la corriente.
Leer datos de bloque de la secuencia de entrada
InputStream in = (...); try {byte [] buf = new byte [100]; while (true) {int n = in.read (buf); if (n == -1) ruptura; (... procesar buf con offset = 0 y longitud = n ...)}} Finalmente {in.close ();}Recuerde que el método Read () no necesariamente llena el BUF completo, por lo que debe considerar la longitud de la devolución en la lógica de procesamiento.
Leer texto de un archivo
BufferedReader in = new BufferedReader (new InputStreamReader (new FileInputStream (...), "UTF-8")); Try {while (true) {string line = in.readline (); if (línea == nulo) ruptura; (... Línea de proceso ...)}} Finalmente {in.close ();} La creación del objeto BufferedReader parece ser muy detallado. Esto se debe a que Java trata bytes y personajes como dos conceptos diferentes (esto es diferente de C).
Puede usar cualquier tipo de InputStream en lugar de FileInputStream, como Socket.
BufferedReader.Readline () devuelve nulo cuando se alcanza el final de la transmisión.
Para leer un personaje a la vez, use el método Reader.read ().
Puede usar otras codificaciones de personajes sin UTF-8, pero es mejor no hacerlo.
Escribir texto a un archivo
PrintWriter out = new PrintWriter (nuevo OutputStreamWriter (nuevo FileOutputStream (...), "UTF-8")); intente {out.print ("hola"); out.print (42); out.println ("World!");} Finalmente {out.close ();} La creación de objetos PrintWriter parece ser muy detallado. Esto se debe a que Java trata bytes y personajes como dos conceptos diferentes (esto es diferente de C).
Al igual que System.out, puede usar print () e println () para imprimir múltiples tipos de valores.
Puede usar otras codificaciones de personajes sin UTF-8, pero es mejor no hacerlo.
Valor de verificación defensiva
int factorial (int n) {if (n <0) tirar nueva ilegalargumentException ("indefinida"); else if (n> = 13) tirar nueva arithmeticException ("desbordamiento de resultados"); más si (n == 0) return 1; else return n * factorial (n - 1);} No piense que los valores de entrada son positivos, lo suficientemente pequeños, etc. para detectar estas condiciones explícitamente.
Una función bien diseñada debe poder ejecutar correctamente para todos los valores de entrada posibles. Asegúrese de que todas las situaciones se tengan en cuenta y que no haya una salida incorrecta (como el desbordamiento).
Objetos de prueba preventiva
int FindIndex (List <String> List, String Target) {if (list == NULL || Target == NULL) Lanzar nueva nullPointerException (); ...}No piense que los parámetros del objeto no serán nulos. Para detectar explícitamente esta condición.
Índice de matriz de detección preventiva
void froB (byte [] b, int index) {if (b == null) tirar nueva nullpointerException (); if (index <0 || índice> = B.Length) tire nuevo indexOUTOFBoundSexception (); ...}No piense que el índice de matriz dado no cruzará los límites. Para detectarlo explícitamente.
Intervalo de matriz de detección preventiva
void frob (byte [] b, int off, int len) {if (b == null) tirar nueva nullpointerException (); if (off <0 || off> b.length || len <0 || b.length - off <len) tirar nuevo índiceuToFboundsexception (); ...}No piense que el intervalo de matriz dado (por ejemplo, a partir de apagado, leyendo elementos LEN) no irá más allá de los límites. Para detectarlo explícitamente.
Llenar elementos de matriz
Uso de bucles:
// llenar cada elemento de la matriz 'a' con 123 byte [] a = (...); para (int i = 0; i <a.length; i ++) a [i] = 123;
Métodos (preferenciales) para usar la biblioteca estándar:
Matrizs.fill (a, (byte) 123);
Copiar un elemento de matriz en un rango
Uso de bucles:
// Copiar 8 elementos de la matriz 'a' que comienza en el desplazamiento 3 // a la matriz 'b' que comienza en el desplazamiento 6, // suponiendo que 'a' y 'b' son matrices distintos [] a = (...); byte [] b = (...); para (int i = 0; i <8; i ++) b [6 + i] = a [3 + i];
Métodos (preferenciales) para usar la biblioteca estándar:
System.ArrayCopy (a, 3, b, 6, 8);
Matriz de cambio de tamaño
Use bucles (escalar):
// hacer una matriz 'a' más grande a newlenbyte [] a = (...); byte [] b = new byte [newlen]; para (int i = 0; i <a.length; i ++) // sube a la longitud de a b [i] = a [i]; a = b;
Use bucles (reduzca el tamaño):
// hacer una matriz 'a' más pequeña a newlenbyte [] a = (...); byte [] b = new byte [newlen]; para (int i = 0; i <b.length; i ++) // sube a la longitud de b b [i] = a [i]; a = b;
Métodos (preferenciales) para usar la biblioteca estándar:
a = arrays.copyOf (a, newlen);
Empacar 4 bytes en un int
int PackBigendian (byte [] b) {return (b [0] & 0xff) << 24 | (B [1] y 0xff) << 16 | (B [2] y 0xff) << 8 | (b [3] y 0xff) << 0;} int Packlittleendian (byte [] b) {return (b [0] & 0xff) << 0 | (B [1] y 0xff) << 8 | (B [2] y 0xff) << 16 | (b [3] y 0xff) << 24;}Descomponer int en 4 bytes
byte [] impackbigendian (int x) {return new byte [] {(byte) (x >>> 24), (byte) (x >>> 16), (byte) (x >>> 8), (byte) (x >>> 0)};} byte [] impacklittleendian (int x) {return byte >>> 8), (byte) (x >>> 16), (byte) (x >>> 24)};}Siempre use el operador de desplazamiento derecho sin firmar (>>>) para envolver los bits, no use el operador de cambio de derecha aritmético (>>).