Prefacio
Lo revisé en marzo de 2016 y revisé por qué necesito optimizar el código en función de mi propio trabajo y mi experiencia diaria de aprendizaje. Antes de enmendar, mi declaración fue así:
Al igual que una ballena que come camarones, tal vez comer uno o dos camarones no es muy efectivo para las ballenas, pero si comes demasiado camarones, las ballenas estarán naturalmente llenas. La optimización del código es la misma. Quizás una o dos optimizaciones son de poca importancia para mejorar la eficiencia de ejecución del código, pero siempre que pueda prestar atención a la optimización del código en todas partes, generalmente es muy útil para mejorar la eficiencia de funcionamiento del código.
Esta opinión, en la vista actual, es una razón para la optimización del código, pero no es completamente correcta. Hoy, con el desarrollo de la tecnología mecánica, los servidores a menudo tienen 8 núcleos, 16 núcleos y CPU de 64 bits, y la eficiencia de ejecución del código es muy alta. StringBuilder reemplaza a StringBuffer y ArrayList reemplaza el vector, lo que mejora la eficiencia de operación del código por minúscula. Incluso si nota cada punto del proyecto, no puede ver ningún cambio obvio en la operación del código.
Creo que el papel más importante de la optimización del código debería ser: evitar errores desconocidos. Durante el código que se ejecuta en línea, a menudo ocurren muchos errores inesperados, porque el entorno en línea y el entorno de desarrollo son muy diferentes, y el posicionamiento de errores a menudo es una razón muy pequeña. Sin embargo, para resolver este error, primero debemos autoverificar, luego empaquetar el archivo de clase para ser reemplazado, suspender el negocio y reiniciar. Para un proyecto maduro, el último tiene un gran impacto, lo que significa que los usuarios no pueden acceder a la aplicación durante este período. Por lo tanto, al escribir código, prestar atención a varios detalles de la fuente, pesar y usar las mejores opciones evitará enormemente errores desconocidos y reducirá en gran medida la carga de trabajo a largo plazo.
Los objetivos de la optimización del código son:
1. Reduce el volumen del código
2. Mejorar la eficiencia de la operación del código
Algunos de los contenidos de este artículo provienen de Internet, y otros provienen del trabajo diario y el estudio. Por supuesto, esto no es importante. Lo importante es si los detalles de esta optimización de código son realmente útiles. Entonces este artículo se actualizará durante mucho tiempo. Mientras encuentre detalles de optimización de código que valga la pena compartir, este artículo se actualizará de vez en cuando.
Detalles de optimización de código
(1) Intente especificar el modificador final de la clase y el método tanto como sea posible
Las clases con modificadores finales no son derivables. En la API de Java Core, hay muchos ejemplos de aplicación final, como java.lang.string, y toda la clase es definitiva. Especificar un modificador final para una clase puede evitar que la clase sea heredada, y especificar un modificador final para un método puede evitar que el método se anule. Si una clase se especifica como final, todos los métodos de esa clase son finales. El compilador Java buscará oportunidades para en línea todos los métodos finales. En línea es de gran importancia para mejorar la eficiencia de la carrera de Java. Para más detalles, consulte la optimización de tiempo de ejecución de Java. Este movimiento puede mejorar el rendimiento en un promedio del 50%.
(2) Intente reutilizar objetos
Especialmente para el uso de objetos de cadena, StringBuilder/StringBuffer debe usarse en su lugar cuando se produce la concatenación de cadenas. Dado que las máquinas virtuales de Java no solo necesitan pasar tiempo generando objetos, también es posible que también necesiten pasar el tiempo recolectando y procesando estos objetos en el futuro, sino que generar demasiados objetos tendrá un gran impacto en el rendimiento del programa.
(3) Use las variables locales tanto como sea posible
Los parámetros pasados al llamar al método y las variables temporales creadas en la llamada se almacenan en la pila, que es más rápida. Otras variables, como variables estáticas, variables de instancia, etc., se crean en el montón, que es más lento. Además, a medida que las variables creadas en la pila están terminadas, estos contenidos se han ido y no se requieren una recolección de basura adicional.
(4) Cierre el flujo en el tiempo
Durante la programación de Java, tenga cuidado al realizar la conexión de la base de datos y las operaciones de transmisión de E/S. Después de su uso, cierre a tiempo para liberar recursos. Debido a que el funcionamiento de estos objetos grandes causará una sobrecarga del sistema grande, y si no tiene cuidado, conducirá a graves consecuencias.
(5) Minimizar cálculos repetidos de variables
Para aclarar un concepto, incluso si solo hay una oración en el método, todavía se consume, incluida la creación de marcos de pila, proteger el sitio al llamar al método y restaurar el sitio al llamar al método. Entonces, por ejemplo, la siguiente operación:
for (int i = 0; i <list.size (); i ++) {...}Se recomienda reemplazarlo con:
for (int i = 0, longitud = list.size (); i <longitud; i ++) {...}De esta manera, cuando list.size () es muy grande, reduce mucho consumo
(6) Trate de adoptar una estrategia de carga perezosa, es decir, cree cuando sea necesario
Por ejemplo:
Cadena str = "aaa"; if (i == 1) {list.add (str);}Se recomienda reemplazarlo con:
if (i == 1) {string str = "aaa"; list.add (str);}(7) Use anormalidades con precaución
La anormalidad no es buena para el rendimiento. Para lanzar una excepción, primero debe crear un nuevo objeto. El constructor de la interfaz de lanzamiento llama al método de sincronización local llamado FillinStackTrace (). El método FillinStackTrace () verifica la pila y recopila información de trazas de llamadas. Mientras se lance una excepción, la máquina virtual Java debe ajustar la pila de llamadas porque se crea un nuevo objeto durante el procesamiento. Las excepciones solo se pueden usar para el manejo de errores y no se deben usar para controlar el flujo del programa.
(8) No use la prueba ... Catch ... en el bucle, debe colocarse en la capa más externa
Según las opiniones presentadas por los internautas, creo que vale la pena discutir esto
(9) Si se puede estimar la longitud del contenido que se puede agregar, especifique la longitud inicial para las clases de colección y herramientas implementadas en una matriz en la capa subyacente.
Por ejemplo, ArrayList, Linkedllist, StringBuilder, StringBuffer, Hashmap, Hashset, etc. Tome StringBuilder como ejemplo:
StringBuilder () // Asignar 16 caracteres de espacio de Space StringBuilder (int tamaño) // Asignar 16 caracteres de espacio de Space StringBuilder (String Str) // Asignar 16 caracteres predeterminados + Str.length () espacio de caracteres
Su capacidad de inicialización se puede establecer a través del constructor de la clase (aquí nos referimos no solo al StringBuilder anterior), lo que puede mejorar significativamente el rendimiento. Por ejemplo, StringBuilder, la longitud representa el número de caracteres que el StringBuilder actual puede mantener. Porque cuando StringBuilder alcanza su capacidad máxima, aumentará su propia capacidad a 2 veces y agregará 2. Siempre que StringBuilder alcance su capacidad máxima, tendrá que crear una nueva matriz de caracteres y copiar el contenido de la matriz de caracteres antiguo a la nueva matriz de caracteres; esta es una operación que requiere mucho rendimiento. Solo imagine, si puede estimar que 5000 caracteres se almacenan en la matriz de personajes sin especificar la longitud, el poder de 2 más cercano a 5000 es 4096, y los 2 agregados a cada expansión es independientemente de los 2, entonces: entonces:
Basado en 4096, solicite matrices de personajes de tamaño 8194, que suman matrices de personajes de tamaño 12290 a la vez. Si puede especificar matrices de caracteres de 5000 de tamaño al principio, ahorrará más del doble del espacio para copiar los 4096 caracteres originales en la matriz de caracteres nuevos.
Esto no solo desperdicia el espacio de memoria, sino que también reduce la eficiencia de la operación del código. Por lo tanto, no es incorrecto establecer una capacidad de inicialización razonable para las clases de recopilación y herramientas implementadas en la matriz subyacente, que traerá resultados inmediatos. Sin embargo, tenga en cuenta que las colecciones como HASHMAP que se implementan en Arrays + Listas vinculadas no deben establecer el tamaño inicial igual que su tamaño estimado, porque la posibilidad de un solo objeto conectado a una tabla es casi 0. Se recomienda establecer el tamaño inicial en N potencia de 2. Si puede estimar que hay 2,000 elementos, establecerlo en New Hashmap (128) y New HaShMAP (256).
(10) Al copiar una gran cantidad de datos, use el comando System.ArrayCopy ()
(11) Operaciones de cambio de multiplicación y uso de división
Por ejemplo:
for (val = 0; val <100000; val += 5) {a = val * 8; b = val / 2;}El uso de operaciones de turno puede mejorar en gran medida el rendimiento, porque en la parte inferior de la computadora, la operación de posicionamiento es la más conveniente y más rápida, por lo que se recomienda modificarlo para:
for (val = 0; val <100000; val += 5) {a = val << 3; b = val >> 1;}Aunque la operación de cambio es rápida, puede dificultar el código de entender, por lo que es mejor agregar los comentarios correspondientes.
(12) No cree constantemente referencias de objetos en el bucle
Por ejemplo:
for (int i = 1; i <= count; i ++) {object obj = new Object (); }Este enfoque hará que la referencia del objeto de conteo existente en la memoria. Si el recuento es grande, consumirá memoria. Se recomienda cambiarlo a:
Object obj = null; para (int i = 0; i <= count; i ++) {obj = new Object ();}De esta manera, solo hay una referencia de objeto de objeto en la memoria. Cada vez que se usa el nuevo objeto (), la referencia del objeto del objeto apunta a un objeto diferente, pero solo hay un objeto en la memoria, que guarda enormemente el espacio de la memoria.
(13) Según la consideración de la eficiencia y la verificación de tipo, la matriz debe usarse tanto como sea posible. ArrayList debe usarse solo si no se puede determinar el tamaño de la matriz.
(14) Intente usar HashMap, ArrayList y StringBuilder. A menos que sea necesario para la seguridad de los subprocesos, no se recomienda usar hashtable, vector y stringbuffer. Los últimos tres tienen una sobrecarga de rendimiento debido al uso de mecanismos de sincronización.
(15) No declare las matrices como final pública estática final
Debido a que esto no tiene sentido, solo define la referencia como final estática, y el contenido de la matriz aún se puede cambiar a voluntad. Declarar la matriz como pública es una vulnerabilidad de seguridad, lo que significa que la matriz puede ser cambiada por clases externas
(16) Intente usar casos individuales en ocasiones adecuadas
El uso de singletons puede reducir la carga de carga, acortar el tiempo de carga y mejorar la eficiencia de carga, pero no todos los lugares son adecuados para solteros. En pocas palabras, los singletons son principalmente aplicables a los siguientes tres aspectos:
Controle el uso de recursos, controle la generación de instancias de control de acceso concurrentes de los recursos a través de la sincronización de subprocesos, a fin de lograr el propósito de ahorrar recursos para controlar el intercambio de datos y permitir la comunicación entre múltiples procesos o hilos no relacionados sin establecer asociaciones directas.
(17) Trate de evitar usar variables estáticas a voluntad
Debe saber que cuando una variable se hace referencia a un objeto definido como una estática, GC generalmente no recicla la memoria del montón ocupada por el objeto, como:
Clase pública A {PRIVADO ESTÁTICO B B = NUEVO B (); }En este momento, el ciclo de vida de la variable estática B es el mismo que el de la Clase A. Si la Clase A no está desinstalado, el objeto B señalado por referencia B residirá en la memoria hasta que el programa termine
(18) Borre las sesiones ya no necesarias en el tiempo
Para borrar sesiones que ya no están activas, muchos servidores de aplicaciones tienen un tiempo de espera de sesión predeterminado, generalmente 30 minutos. Cuando el servidor de aplicaciones necesita guardar más sesiones, si no hay memoria insuficiente, el sistema operativo transferirá parte de los datos al disco. El servidor de aplicaciones también puede arrojar algunas sesiones inactivas al disco de acuerdo con el algoritmo MRU (más utilizado recientemente), e incluso puede arrojar excepciones de memoria insuficientes. Si la sesión debe ser arrojada al disco, primero debe ser serializada. En grupos a gran escala, la serialización de los objetos es costoso. Por lo tanto, cuando la sesión ya no es necesaria, el método Invalidate () de httpsession debe llamarse a tiempo para borrar la sesión.
(19) Una colección que implementa la interfaz RandomAccess, como ArrayList, debe usar el más común para el bucle en lugar de foreach loop para atravesar
Esto es recomendado por JDK a los usuarios. La explicación de la API JDK de la interfaz RandomAccess es: la implementación de la interfaz RandomAccess se utiliza para indicar que admite un acceso aleatorio rápido. El objetivo principal de esta interfaz es permitir que los algoritmos generales cambien su comportamiento, para que pueda proporcionar un buen rendimiento cuando se aplica a listas de acceso aleatorias o continuas. La experiencia práctica muestra que si se acceden al azar las instancias de clase que implementan la interfaz RandomAccess, la eficiencia del uso de bucles ordinarios para los bucles será mayor que la de usar bucles foreach; Por el contrario, si se accede secuencialmente, será más eficiente usar Iterator. Puede usar códigos similares a los siguientes para hacer juicios:
if (list instanceOf randomAccess) {for (int i = 0; i <list.size (); i ++) {}} else {iterator <?> iterator = list.Iterable (); while (iterator.hasnext ()) {iterator.next ()}}El principio de implementación subyacente del bucle foreach es el iterador, consulte Java Syntax Sugar 1: Parámetros de longitud variable y principio de bucle foreach. Entonces, la segunda mitad de la oración "por el contrario, si se accede secuencialmente, el uso de Iterator será más eficiente" significa que las instancias de clase a las que se accede secuencialmente se atraviesan usando un bucle foreach.
(20) Use bloques de código sincrónicos en lugar de método de sincronización
Esto ya está claramente establecido en el bloque de método de bloqueo sincronizado del artículo en el módulo de múltiples subprocesos. A menos que se pueda determinar que todo el método debe sincronizarse, intente usar bloques de código sincronizados para evitar sincronizar esos códigos que no necesitan sincronizarse, lo que afecta la eficiencia de la ejecución del código.
(21) Declarar la constante como estática final y nombrarla en capital
De esta manera, estos contenidos se pueden poner en el grupo constante durante la compilación, evitando el cálculo de los valores constantes generados durante el tiempo de ejecución. Además, nombrar el nombre de una constante en capital también puede facilitar la distinción entre constantes y variables
(22) No cree algunos objetos no utilizados, no importe algunas clases no utilizadas
Esto no tiene sentido. Si "el valor de la variable local i no se usa" y "la importación java.util nunca se usa" aparece en el código, entonces elimine estos contenidos inútiles
(23) Evite usar la reflexión durante la operación del programa
Para obtener más información, ver Reflexión. La reflexión es una función muy poderosa proporcionada por Java a los usuarios. Las funciones poderosas a menudo significan baja eficiencia. No se recomienda utilizar el mecanismo de reflexión con frecuencia durante la operación del programa, especialmente el método Invoke del método. Si es realmente necesario, un enfoque sugerente es instanciar un objeto a través de la reflexión y ponerlo en memoria cuando se inicia el proyecto. El usuario solo se preocupa por la velocidad de respuesta más rápida al interactuar con el par, y no le importa cuánto tiempo tarda en comenzar el proyecto de pares.
(24) Use el grupo de conexión de la base de datos y el grupo de subprocesos
Ambas piscinas se utilizan para reutilizar objetos, el primero evita la apertura y el cierre frecuentes de las conexiones, y el segundo evita la creación frecuente y la destrucción de los hilos
(25) Use flujos de entrada y salida de Buffed para operaciones de IO
Las transmisiones de entrada y salida de buffered, a saber, BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream, que puede mejorar en gran medida la eficiencia de IO
(26) Use ArrayList para escenas con más inserción secuencial y acceso aleatorio, y use LinkedList para escenas con más deleción de elementos e inserción intermedia.
Esto se conoce al comprender los principios de ArrayList y LinkedList
(27) No dejes que hay demasiados parámetros formales en el método público
El método público es un método proporcionado al mundo exterior. Si le da a estos métodos demasiados parámetros formales, hay dos desventajas principales:
Violando la idea de programación orientada a objetos. Java enfatiza que todo es un objeto. Demasiados parámetros formales y no coincide con la idea de programación orientada a objetos. Demasiados parámetros inevitablemente conducirán a un aumento en la probabilidad de error en las llamadas de método.
En cuanto a cuántos "demasiados" se refieren a, 3 o 4. Por ejemplo, usamos JDBC para escribir un método InsertStudentInfo. Hay 10 campos de información para estudiantes que se insertarán en la tabla de estudiantes. Estos 10 parámetros se pueden encapsular en una clase de entidad como parámetros formales del método de inserción.
(28) Cuando las variables de cadena y las constantes de cadena se escriben frente a las constantes de cadena
Este es un truco relativamente común, si existe el siguiente código:
Cadena str = "123"; if (str.equals ("123")) {...}Se recomienda modificarlo a:
Cadena str = "123"; if ("123" .equals (str)) {...}Esto puede evitar principalmente excepciones de puntero nulo
(29) Sepa que en Java no hay diferencia entre si (i == 1) y si (1 == i), pero desde la perspectiva de los hábitos de lectura, se recomienda usar el primero
A veces, la gente pregunta si hay alguna diferencia entre "if (i == 1)" y "if (1 == i)"? Esto comienza con C/C ++.
En C/C ++, la condición de juicio "if (i == 1)" es válida, y se basa en 0 y no 0. 0 significa falso, y no 0 significa verdadero. Si hay tal código:
int i = 2; if (i == 1) {...} else {...}C/C ++ juzga que "i == 1" no es válido, por lo que está representado por 0, es decir, falso. Pero si:
int i = 2; if (i = 1) {...} else {...}Si el programador escribe accidentalmente "if (i == 1)" como "if (i = 1)", entonces habrá un problema. Asignar i a 1 dentro de IF. Si determina que el contenido en él no es 0, el verdadero devuelto, pero obviamente es 2, y el valor comparado es 1, el falso que debe devolverse. Es muy probable que esta situación ocurra en el desarrollo de C/C ++ y causará algunos errores difíciles de entender. Por lo tanto, para evitar las operaciones de asignación incorrectas de los desarrolladores en las declaraciones IF, se recomienda escribir la declaración IF como:
int i = 2; if (1 == i) {...} else {...}De esta manera, incluso si el desarrollador escribe accidentalmente "1 = I", el compilador C/C ++ puede verificarlo lo antes posible, porque podemos asignar un valor variable I a 1, pero no podemos asignar un valor constante 1 a i.
Sin embargo, en Java, la sintaxis "if (i = 1)" de C/C ++ es imposible de aparecer, porque una vez que se escribe esta sintaxis, Java compilará e informará un error de error "No puede convertirse de int a boolean". Sin embargo, aunque no hay diferencia semántica entre "if (i == 1)" y "if (1 == i)" en Java, sería mejor recomendar usar el primero en términos de hábitos de lectura.
(30) No use el método toString () en la matriz
Echemos un vistazo a lo que se imprime usando toString () para matrices:
int i = 2; if (1 == i) {...} else {...}apagar:
Yo@18A992F
La intención original es imprimir el contenido de la matriz, pero puede causar una excepción de puntero nulo porque la referencia de la matriz es nula. Sin embargo, aunque no tiene sentido para la matriz toString (), el contenido de la colección se puede imprimir para la colección toString (), porque la clase principal de la colección, el método toString () del objeto () se reescribe.
(31) No realice transformaciones de fuerza hacia abajo en los tipos de datos básicos que están fuera de rango
Esto nunca obtendrá el resultado deseado:
public static void main (string [] args) {long l = 12345678901234l; int i = (int) l; System.out.println (i);}Podemos esperar obtener algunos de ellos, pero el resultado es:
1942892530
Explicarlo. Long en Java es de 8 bytes con 64 bits, por lo que la representación de 12345678901234 en la computadora debería ser:
0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010
Un datos de tipo int es 4 bytes y 32 bits. Los primeros 32 bits de la cadena de datos binarios anteriores se toman del bit bajo:
0111 0011 1100 1110 0010 1111 1111 0010
Esta cadena de binario se representa como decimal 1942892530, por lo que es la salida de contenido en la consola anterior. De este ejemplo, podemos sacar dos conclusiones:
1. El tipo de datos predeterminado del tipo entero es int, Long L = 12345678901234l. Este número ha excedido el rango de INT, por lo que hay una L al final, lo que indica que este es un número de tipo largo. Por cierto, el tipo predeterminado de tipo de punto flotante es el doble, por lo que al definir Float, debe escribirse como "" Float F = 3.5f "
2. Luego, escriba otra oración "int ii = l + i;" e informará un error porque Long + int es un largo y no se puede asignar a int
(32) Los datos no utilizados en la clase de recolección pública deben eliminarse a tiempo
Si una clase de recolección es pública (es decir, no es una propiedad en un método), entonces los elementos en esta colección no se lanzarán automáticamente porque siempre hay referencias que les apuntan. Por lo tanto, si no se utilizan ciertos datos en la recopilación pública y no se eliminan, hará que la recopilación pública continúe creciendo, lo que hace que el sistema tenga el potencial de fuga de memoria.
(33) Convierta un tipo de datos básicos en una cadena. El tipo de datos básico.ToString () es la forma más rápida, seguida de String.ValueOf (Data), y Data +"" es la más lenta
Generalmente hay tres formas de convertir un tipo de datos básico. Tengo datos de tipo entero, es decir, toString (), string.valueOf (i) e i+"" de tres maneras. ¿Qué tan eficientes son las tres formas? Ver una prueba:
public static void main (string [] args) {int looptime = 50000; Entero i = 0; Long Starttime = System.CurrentTimemillis (); for (int j = 0; j <looptime; j ++) {string str = string.valueOf (i); } System.out.println ("String.ValueOf ():" + (System.CurrentTimemillis () - Starttime) + "MS"); starttime = system.currentTimemillis (); for (int j = 0; j <looptime; j ++) {string str = i.ToString (); } System.out.println ("Integer.ToString ():" + (System.CurrentTimemillis () - Starttime) + "MS"); starttime = system.currentTimemillis (); for (int j = 0; j <looptime; j ++) {string str = i+""; } System.out.println ("i + /" /":" + (System.CurrentTimemillis () - Starttime) + "MS");}El resultado de la ejecución es:
String.ValueOf (): 11MsInteger.ToString (): 5ms + "": 25ms
Por lo tanto, cuando convierte un tipo de datos básico en cadena en el futuro, debe dar prioridad al uso del método toString (). En cuanto a por qué, es muy simple:
String.valueOf() se llama método Integer.toString() , pero será corto juzgar Integer.toString() antes de llamar. Llamaré al I + "" La capa inferior usa la implementación de StringBuilder. Primero use el método Agéntico para empalmarlo y luego use el método ToString () para obtener la cadena.
En comparación con los tres, obviamente es el más rápido, el más rápido y el más lento
(34) Use la forma más eficiente de atravesar el mapa
Hay muchas formas de atravesar el mapa. Por lo general, lo que necesitamos para atravesar la clave y el valor en el mapa. El método recomendado y más eficiente es:
public static void main (string [] args) {hashmap <string, string> hm = new HashMap <String, String> (); hmmy ("111", "222"); Set <map.entry <string, string >> entrySet = Hm.EntrySet (); Iterator <map.entry <string, string >> iter = entryset.iterator (); while (iter.hasnext ()) {map.entry <string, string> entry = iter.next (); System.out.println (Entry.getKey () + "/t" + Entry.getValue ()); }} Si solo desea iterar sobre el valor clave de este mapa, sería más apropiado usar " Set<String> keySet = hm.keySet();"
(35) Se recomienda operar por separado para cerrar () de recursos
Significa, por ejemplo, tengo un código como este:
intente {xxx.close (); Yyy.close ();} capt (excepción e) {...}Se recomienda modificarlo a:
intente {xxx.close ();} capt (excepción e) {...} try {yyy.close ();} capt (excepción e) {...}Aunque es un poco problemático, puede evitar la fuga de recursos. Creemos que si no hay código modificado, si xxx.close () lanza una excepción, ingresará al bloque de captura. Yyy.close () no se ejecutará, y el recurso YYY no será reciclado y estará ocupado todo el tiempo. Con más códigos como este, puede hacer que el mango de recursos se filtre. Después de cambiar al siguiente método de escritura, se garantiza que XXX y YYY estarán cerrados sin importar qué.
(36) Para ThreadLocal, debe eliminar antes o después de usar
Actualmente, básicamente todos los proyectos utilizan la tecnología de agrupación de hilos, que es muy buena. Puede configurar dinámicamente el número de hilos y reutilizar hilos.
Sin embargo, si usa ThreadLocal en su proyecto, recuerde eliminarlo antes o después de su uso. Esto se debe a que la tecnología del grupo de subprocesos mencionada anteriormente es reutilizar un hilo, lo que significa que durante el código en funcionamiento, un hilo no será destruido y estará esperando el próximo uso. Echemos un vistazo a la referencia en la clase de hilo que contiene ThreadLocal.threadLocalMap:
/* Valores de threadlocal pertenecientes a este hilo. Este mapa se mantiene * por la clase ThreadLocal. */ThreadLocal.ThreadLocalMap ThreadLocals = null;
El hilo no se destruye significa que los datos en ThreadLocal.threadLocalMap del conjunto de subprocesos anterior todavía existe. Cuando el siguiente hilo reutiliza este hilo, es probable que lo que obtenga sean los datos del conjunto de subprocesos anterior en lugar del contenido que desea.
Este problema es muy oscuro. Una vez que ocurren errores causados por esta causa, es muy difícil encontrar este problema sin una experiencia relevante o una base sólida. Por lo tanto, debe prestar atención a esto al escribir código, lo que reducirá su carga de trabajo posterior.
(37) Recuerde reemplazar el número de diablo con una definición constante. La existencia del número de diablo reducirá en gran medida la legibilidad del código. Si las constantes de cadena usan definiciones constantes pueden depender de la situación.
(38) Cuando la asignación inicial de larga o larga, use L en lugar de L minúsculas, porque la letra L es muy fácil de confundir con el número 1, este punto es muy detallado y vale la pena señalar
(39) Todos los métodos de reescritura deben retener la anotación @Override
Hay tres razones para hacer esto:
(1) Está claro que este método se hereda de la clase principal
(2) métodos getObject () y get0bject (). La cuarta letra del primero es "O", y el cuarto padre e hijo del segundo es "0". Agregar la anotación @Override puede determinar inmediatamente si la reescritura es exitosa.
(3) Modificar la firma del método en la clase abstracta, y la clase de implementación informará inmediatamente un error de compilación.
(40) Se recomienda utilizar la clase de herramienta de objetos recientemente introducido en JDK7 para comparar objetos iguales, directamente a.equals (b), y existe el riesgo de excepciones de puntero nulo.
(41) No use "+" para empalmar las cadenas en el cuerpo del bucle, pero use StringBuilder para agregar continuamente
Permítanme hablar sobre la razón por la que no uso "+" para el empalme de cadena. Si tengo un método:
public String AppendStr (String Orist, String ... AppendStrs) {if (appendStrs == null || appendStrs.length == 0) {return oistr; } for (string appendStr: appendStrs) {oistr += appendstr; } return orrist;}Después de compilar este código, descompile el archivo .class usando javap -c para interceptar la parte clave:
Significa que cada vez que la máquina virtual se encuentra con el operador "+" para empalmar la cadena, nuevo un StringBuilder, luego llame al método de apéndice y finalmente llame al método toString () para convertir la cadena en el objeto ORISTR. Es decir, cuántas veces el bucle será nuevo StringBuilder (), que es un desperdicio de memoria.
(42) No capturen la clase de excepción de tiempo de ejecución definida en la biblioteca de clase Java heredada de RuntimeException
La eficiencia de manejo de excepciones es baja, la mayoría de las clases de excepción de tiempo de ejecución de RuntimeException pueden ser evitadas por completo por programadores, como:
(43) Evite usar instancias aleatorias mediante múltiples hilos. Aunque compartir la instancia es segura de hilo, causará la degradación del rendimiento debido a la competencia por la misma semilla. Después de JDK7, puede usar ThreadLocalRandom para obtener números aleatorios
Explique la razón por la cual la competencia por la misma semilla causa la degradación del rendimiento. Por ejemplo, eche un vistazo a la implementación del método NextInt () de la clase aleatoria:
public int NextInt () {return Next (32); }Se llama el siguiente método (int bits), que es un método protegido:
protegido int lo siguiente (int bits) {Long Oldseed, NextSeed; Semilla atomiclong = this.Seed; do {Oldseed = Seed.get (); nextSeed = (Oldseed * Multiplicador + Addend) & Mask; } while (! Seed.CompareAset (OldSeed, NextSeed)); return (int) (nextseed >>> (48 - bits));}Y la semilla aquí es una variable global:
/*** El estado interno asociado con este generador de números pseudorandom. * (Las especificaciones para los métodos en esta clase describen la actualidad * actual de este valor.) */ Semilla de Atomiclong final privada;
Cuando múltiples hilos obtienen números aleatorios al mismo tiempo, competirán por la misma semilla, lo que resulta en una disminución de la eficiencia.
(44) Las clases estáticas, las clases de singleton y las clases de fábrica establecen sus constructores en privado
Esto se debe a que no necesitamos nuevos afuera. Después de establecer el constructor en privado, nos aseguramos de que estas clases no produzcan objetos de instancia.
posdata
Excelente código proviene de cada pequeña optimización. Prestar atención a cada detalle no solo puede mejorar la eficiencia de ejecución del programa, sino también evitar muchos problemas desconocidos.
Los anteriores son 44 sugerencias de optimización de código Java introducidas por el editor. Espero que te sea útil. Si tiene alguna pregunta, déjame un mensaje y el editor le responderá a tiempo. ¡Muchas gracias por su apoyo al sitio web de Wulin.com!