El objetivo de este curso es ayudarlo a usar Java de manera más efectiva. Se discuten algunos temas avanzados, incluida la creación de objetos, la concurrencia, la serialización, la reflexión y otras características avanzadas. Este curso guiará su viaje de competencia de Java.
1. Introducción
En la clasificación de lenguaje de programación de Tiobe, el lenguaje Java desarrollado por Sun en 1995 es uno de los lenguajes de programación más utilizados del mundo. Como lenguaje de programación general, Java Language es muy atractivo para los ingenieros de desarrollo de software debido a su poderoso juego de herramientas y su entorno de tiempo de ejecución, sintaxis simple, soporte de plataforma rica (escrito a la vez, ejecutar en todas partes) y soporte comunitario extremadamente activo.
En esta serie de artículos, se cubre el contenido avanzado relacionado con Java, por lo que se supone que el lector ya tiene conocimiento del lenguaje básico. Este no es un manual de referencia completo, sino una guía exhaustiva para llevar sus habilidades al siguiente nivel.
Este curso contiene una gran cantidad de fragmentos de código. Para hacer comparaciones, otras partes proporcionarán ejemplos de Java 7 y Java 8 al mismo tiempo.
2. Ejemplo de construcción
Como lenguaje orientado a objetos, la creación de objetos es quizás uno de los conceptos más importantes en el lenguaje Java. Un constructor juega un papel importante en la inicialización de las instancias de objetos, y Java proporciona una variedad de formas de definir constructores.
2.1 Método de construcción implícito (generado)
Java no permite que no se declaren constructores al definir clases, y esto no significa que no haya constructor. Veamos la definición de la siguiente clase:
paquete com.javacodeeks.Advanced.Construction; public class NoConstructor {}Esta clase no define un constructor, pero el compilador Java generará implícitamente uno para ello, lo que nos permite usar la nueva palabra clave para crear nuevas instancias de objetos.
noconstructor final noConstructorInstance = new noConstructor ();
2.2 Método de construcción sin parámetros
El constructor sin parámetros es la forma más fácil de reemplazar la compilación de Java y generar constructores mediante declaraciones explícitas.
paquete com.javacodeeks.Advanced.Construction; public class noargConstructor {public noargConstructor () {// constructor Body aquí}}Al crear una nueva instancia de objeto usando la nueva palabra clave, se llamará al constructor anterior.
2.3 Método de construcción similar a los parámetros
El método de construcción de parámetros es el más interesante y ampliamente utilizado, y la creación de nuevas instancias se personaliza especificando parámetros. El siguiente ejemplo define un constructor con dos parámetros.
paquete com.javacodeeks.Advanced.Construction; public class ConstructorWitharGuments {public constructorwitharGuments (final de cadena arg1, cadena final arg2) {// cuerpo constructor aquí}}En este escenario, cuando se usa la nueva palabra clave para crear una instancia, se deben proporcionar dos parámetros definidos en el método de construcción al mismo tiempo.
Constructorwitharguments final constructorWitharGuments = new ConstructorWitharGuments ("arg1", "arg2");Curiosamente, los constructores pueden llamarse entre sí a través de esta palabra clave. En la práctica, se recomienda encadenar a los constructores múltiples mediante el uso de esto para reducir la duplicación de código y tener una sola entrada de inicialización basada en el objeto. Como ejemplo, el siguiente código define un constructor con solo un parámetro.
Public ConstructorWitharGuments (Final String Arg1) {this (arg1, null);}2.4 Inicializar el bloque de código
Además de la construcción de métodos, Java también proporciona la lógica para inicializar mediante la inicialización de bloques de código. Aunque este uso es raro, no es perjudicial saber más al respecto.
paquete com.javacodeeks.advanced.construction; public class InitializationBlock {{// Código de inicialización aquí}}Por otro lado, la inicialización de bloques de código también puede considerarse como métodos de construcción implícitos sin parámetros. En una clase específica, se pueden definir múltiples bloques de código de inicialización, y se llaman en el orden en que están en el código cuando se ejecutan, como se muestra en el siguiente código:
paquete com.javacodeeks.advanced.construction; public class InitializationBlocks {{// Código de inicialización aquí} {// código de inicialización aquí}}Realmente inicializando los bloques de código no es reemplazar el constructor, pero pueden aparecer simultáneamente. Pero recuerde que el código de inicialización se ejecutará antes de que la llamada del método del constructor sea pronto.
paquete com.javacodeeks.Advanced.Construction; public class InitializationBlock yConstructor {{// Código de inicialización aquí} public inicializaciónBlock yConstructor () {}}2.5 Asegúrese de la construcción de valores predeterminados
Java proporciona una garantía de inicialización definitiva, y los programadores pueden usar el resultado de inicialización directamente. Las instancias no inicializadas y las variables de clase (estadísticas) se inicializarán automáticamente a los valores predeterminados correspondientes.
Escriba el valor predeterminado
boolean
byte0
corto
int0
long0l
char/u0000
flotante0.0f
Doble0.0d
Referencia de objetos nulo
Tabla 1
Usamos el siguiente ejemplo para verificar los valores predeterminados en la tabla anterior:
paquete com.javacodegeeks.Advanced.Construction; Inicialización de clase pública condefaults {boolean boolean privado; byte byte privado; privado corta corta breve; Private int intmember; Privado largo largo largo; Charmember de char privado; Membré flotante privado; Doble DoubeMembrember privado; Referencia de objetos privados; Public InitializationWithDefaults () {System.out.println ("booleanMember =" + booleanMember); System.out.println ("bytemember =" + bytemember); System.out.println ("Shortember =" + ShortMember); System.out.println ("intmember =" + intmember); System.out.println ("longmember =" + longmember); System.out.println ("charmember =" + caracteres.codePointat (new Char [] {Charmember}, 0)); System.out.println ("floatmember =" + floatmember); System.out.println ("doubeMember =" + DoubEmember); System.out.println ("referencemember =" + referenceMember); }}Después de instanciar el objeto usando la nueva palabra clave:
Inicialización final condefaults InicializaciónWithDefaults = nueva inicializaciónWithDefaults (),,
Puede ver el resultado de salida de la consola de la siguiente manera:
booleanMember = falseByTemember = 0ShortMember = 0intMember = 0longMember = 0Charmember = 0floatMember = 0.0DoUsblemember = 0.0ReferencEmember = NULL
2.6 Visibilidad
El constructor sigue las reglas de visibilidad de Java y puede determinar si el constructor puede llamarse en otras clases a través de modificadores de control de acceso.
Visibilidad del paquete de modificador Visibilidad de la subclase visibilidad pública
visible visible visible visible visible visible visible
Visible Visible protegido e invisible visible invisible
<No modificador> visible, no visible, no visible
Invisible invisible privado Invisible Invisible Tabla 2
2.7 Reciclaje de basura
Java (JVM para ser preciso) tiene un mecanismo automático de recolección de basura. En pocas palabras, cuando se crea un nuevo objeto, asignará automáticamente sus intrínsecos; Luego, cuando el objeto ya no se haga referencia, se destruirá automáticamente y la memoria correspondiente se reciclará.
La recolección de basura Java adopta un mecanismo de reciclaje generacional y se basa en la suposición de que "la mayoría de los objetos tienen vida corta" (es decir, no se recitarán poco después de que se cree el objeto, por lo que pueden ser destruidos de manera segura). La mayoría de los programadores habitualmente creen que la creación de objetos en Java es muy ineficiente, por lo que deben evitar la creación de nuevos objetos tanto como sea posible. De hecho, este entendimiento es incorrecto. La sobrecarga de crear objetos en Java es bastante bajo y rápido. La enorme sobrecarga de la generación real son los objetos de supervivencia a largo plazo innecesarios, por lo que eventualmente se emigrarán a la vejez y causarán que ocurra el parto.
2.8 finalizadores de objetos
Hemos hablado sobre los temas relacionados con los métodos de construcción y la inicialización de objetos, pero no hemos mencionado su lado negativo: la destrucción de objetos. Principalmente porque Java utiliza mecanismos de recolección de basura para manejar el ciclo de vida de los objetos, destruir objetos innecesarios y liberar memoria requerida se convierte en responsabilidad de la recolección de basura.
Sin embargo, Java todavía proporciona otra característica similar a un finalizador de destructor, que asume la responsabilidad de limpiar múltiples recursos. El finalizador generalmente se ve como algo peligroso (porque puede traer una variedad de efectos secundarios y problemas de rendimiento). Por lo general, no se requiere finalizador, así que trate de evitarlo (excepto en escenarios raros que contienen una gran cantidad de objetos nativos). La sintaxis de try-with recursos y la interfaz de autoclosa introducida en Java 7 se pueden usar como alternativas a los finalizadores, y se puede escribir el siguiente código conciso:
Prueba (Final InputStream in = files.newinputStream (ruta)) {// código aquí}3. Inicialización estática
Arriba aprendimos sobre la construcción e inicialización de instancias de clase. Además, Java también admite la construcción de inicialización a nivel de clase, llamada inicialización estática. La inicialización estática es similar al bloque de código de inicialización descrito anteriormente, excepto que hay modificaciones adicionales de palabras clave estáticas. Cabe señalar que la inicialización estática solo se realizará una vez cuando la clase esté cargada. Los ejemplos son los siguientes:
Similar a la inicialización de bloques de código, se pueden definir múltiples bloques de inicialización estática en una clase, y su posición en la clase determina el orden en que se ejecutan en la inicialización. Los ejemplos son los siguientes;
paquete com.javacodeeks.advanced.construction; public class staticInitializationblocks {static {// código de inicialización estática aquí} estático {// código de inicialización estática aquí}}Debido a que los bloques de inicialización estática pueden activarse mediante múltiples hilos que se ejecutan en paralelo (cuando la clase se carga inicialmente), el tiempo de ejecución JVM asegura que el código inicializado se ejecute solo una vez de manera segura de subprocesos.
4. Modo constructor
Una variedad de patrones de constructor (creador) fáciles de entender se han introducido a la comunidad de Java a lo largo de los años. A continuación aprenderemos algunos de los más populares: modo singleton, modo de clase auxiliar, modo de fábrica e inyección de dependencia (también conocida como inversión de control).
4.1 Modo Singleton
Singleton es una larga historia pero un modelo controvertido en la comunidad de desarrollo de software. El concepto central del patrón Singleton es garantizar que en cualquier momento se cree una clase determinada solo un objeto. Si bien suena simple, hay mucha discusión sobre cómo crear objetos de manera correcta y segura de hilo. El siguiente código muestra una versión simple de la implementación del patrón Singleton:
paquete com.javacodeeks.advanced.construction.patterns; public class Naivesingleton {instancia privada de naivessingleton; privado naivesingleton () {} public static naivesingleton getInstance () {if (instance == null) {instancia = new NaivesingLeton (); } instancia de retorno; }}Hay al menos un problema con el código anterior: se pueden crear múltiples objetos en un escenario de concurrencia multiproceso. Una forma razonable de implementar (pero no la carga retrasada) es utilizar la propiedad Static`final` de la clase. como sigue:
Propiedad final de la clase.package com.javacodeeks.advanced.construction.patterns; public class Eagersingleton {private static final Eagersingleton instancia = new EagersingLeton (); Private EagersingLeton () {} public static Eagersingleton getInstance () {instancia de retorno; }}Si no desea desperdiciar recursos valiosos y desea que se creen objetos singleton solo cuando realmente sean necesarios, entonces debe usar un método de sincronización explícito. Este método puede reducir la concurrencia en entornos de múltiples subprocesos (se describirán más detalles sobre la concurrencia de Java en las mejores prácticas avanzadas de 9 concurrencias avanzadas).
paquete com.javacodeeks.advanced.construction.patterns; public class Lazysingleton {instancia privada de lazysingleton estática; Private LazySingleton () {} public static sincronizado Lazysingleton getInstance () {if (instance == null) {instancia = new LazySingLeton (); } instancia de retorno; }}Hoy en día, los patrones singleton ya no se consideran una buena opción en muchos escenarios porque hacen que el código sea menos fácil de probar. Además, la generación del modo de inyección de dependencia también hace que el modo singleton sea innecesario.
4.2 Herramientas/clases auxiliares
El patrón de clase de herramientas/clase de ayuda es bastante popular entre los desarrolladores de Java. Su concepto principal es utilizar clases no institalizadas (al declarar constructores privados), finales opcionales (más detalles sobre la declaración de clases finales se introducirán en Java Advanced 3 clases e diseño de interfaz) y métodos estáticos. Los ejemplos son los siguientes:
paquete com.javacodeeks.advanced.construction.patterns; public final class helperClass {private helperClass () {} public static void helpermethod1 () {// cuerpo de método aquí} public static void helpermethod2 () {// cuerpo aquí}}Muchos desarrolladores experimentados creen que este patrón hará de las clases de herramientas un contenedor para varios métodos irrelevantes. Debido a que algunos métodos no tienen una ubicación adecuada pero deben ser utilizadas por otras clases, se colocarán por error en la clase de herramientas. Este diseño también debe evitarse en la mayoría de los escenarios: siempre habrá mejores formas de reutilizar el código, manteniendo el código claro y conciso.
4.3 Modelo de fábrica
El modelo de fábrica ha demostrado ser una herramienta extremadamente poderosa para los desarrolladores, y hay muchas maneras de implementarlo en Java: métodos de fábrica y fábricas abstractas. El ejemplo más fácil es usar el método estático para devolver una instancia de una clase específica (método de fábrica), como sigue:
paquete com.javacodeeks.advanced.construction.patterns; libro de clase pública {Libro privado (título de cadena final) {} public static Book Newbook (Título de cadena final) {return New Book (título); }}Aunque el uso de este método puede mejorar la legibilidad del código, a menudo es controvertido que es difícil dar a los escenarios más ricos del método de fábrica de Newbook. Otra forma de implementar el patrón de fábrica es usar interfaces o clases abstractas (fábricas abstractas). De la siguiente manera, definimos una interfaz de fábrica:
interfaz pública bookFactory {book newbook ();}Dependiendo de la galería de imágenes, podemos tener muchas implementaciones de Newbook diferentes:
Public Class Library implementa BookFactory {@Override public Book Newbook () {return new PapelBook (); }} public class KindLelibrary implementa BookFactory {@Override public Book Newbook () {return new KindleBook (); }}Ahora, diferentes implementaciones de BookFactory bloquean las diferencias en libros específicos, pero proporcionan un método general de Newbook.
4.4 Inyección de dependencia
Los diseñadores de clases consideran la inyección de dependencia (también conocida como inversión de control) como una buena práctica de diseño: si algunas instancias de clase dependen de las instancias de otras clases, aquellas instancias que dependen deben ser (inyectadas) a través de métodos constructores (o métodos, políticas, etc.), en lugar de crear por la instancia misma. Echemos un vistazo al siguiente código:
paquete com.javacodeeks.advanced.construction.patterns; import java.text.dateFormat; import java.util.date; Public Class dependiente {Format de Final Final de privado = DateFormat.getDateInstance (); formato de cadena public (fecha de fecha final) {return format.format (fecha); }}La clase dependiente requiere una instancia de la clase DateFormat y se obtiene mediante dateFormat.getDateInStance () al instanciar el objeto. Una mejor manera debería hacer lo mismo construyendo los parámetros del método:
paquete com.javacodeeks.advanced.construction.patterns; import java.text.dateformat; import java.util.date; Public Class dependiente {Formato privado de FinalFormat; Public Dependiente (Formato final de Fecha Format) {this.format = format; } formato de cadena public (fecha final) {return format.format (fecha); }}En el ejemplo anterior, todas las dependencias de la instancia de clase se proporcionan externamente, lo que facilita el ajuste de dateFormat y el código de prueba fácil de escribir.