JavaRush /Blog Java /Random-ES /Del 8 al 13: una descripción completa de las versiones de...

Del 8 al 13: una descripción completa de las versiones de Java. Parte 1

Publicado en el grupo Random-ES
Gatitos, hola a todos)) Entonces, hoy estamos en 2020 y queda muy poco hasta el lanzamiento de Java 14. Deberías esperar la versión final el 17 de marzo, analizaremos lo que hay de nuevo e interesante después del hecho, pero hoy me gustaría refrescar mi memoria sobre las versiones anteriores de Java. ¿Qué novedades nos trajeron? Echemos un vistazo. Comencemos la revisión con Java 8, ya que todavía es bastante relevante y se utiliza en la mayoría de los proyectos. Del 8 al 13: una descripción completa de las versiones de Java.  Parte 1 - 1Anteriormente, se lanzaban nuevas versiones cada 3 a 5 años, pero recientemente Oracle ha adoptado un enfoque diferente: "nuevo Java cada seis meses". Y así, cada seis meses vemos el lanzamiento de funciones. Sea bueno o malo, cada uno lo ve de manera diferente. Por ejemplo, esto no me gusta mucho, ya que las nuevas versiones no tienen muchas características nuevas, pero al mismo tiempo, las versiones crecen como hongos después de la lluvia. Parpadeé un par de veces en un proyecto con Java 8, y Java 16 ya fue lanzado (pero cuando sale raramente, se acumulan nuevas funciones y, al final, este evento es tan esperado, como un día festivo: todos están discutiendo el novedades y no puedes pasar por alto) . ¡Entonces empecemos!

Java 8

Interfaz funcional

¿Qué es esto? Una interfaz funcional es una interfaz que contiene un método (abstracto) no implementado. @FunctionalInterface es una anotación opcional que se coloca encima de dicha interfaz. Necesario para comprobar si cumple con los requisitos de una interfaz funcional (que tiene un solo método abstracto). Pero como siempre, tenemos algunas advertencias: los métodos predeterminados y estáticos no cumplen con estos requisitos. Por lo tanto, puede haber varios métodos de este tipo + uno abstracto, y la interfaz será funcional. También puede contener métodos de la clase Objeto que no afectan la definición de la interfaz como funcional. Agregaré algunas palabras sobre los métodos estáticos y predeterminados:
  1. Los métodos con el modificador predeterminado le permiten agregar nuevos métodos a las interfaces sin romper su implementación existente.

    public interface Something {
      default void someMethod {
          System.out.println("Some text......");
      }
    }

    Sí, sí, agregamos el método implementado a la interfaz y, al implementar este método, no puede anularlo, sino usarlo como heredado. Pero si una clase implementa dos interfaces con un método determinado, tendremos un error de compilación, y si implementa interfaces y hereda una clase con un determinado método idéntico, el método de la clase principal se superpondrá a los métodos de la interfaz y la excepción no funcionará.

  2. Los métodos estáticos en una interfaz funcionan igual que los métodos estáticos en una clase. No lo olvide: no puede heredar métodos estáticos , del mismo modo que no puede llamar a un método estático desde una clase descendiente.

Entonces, unas palabras más sobre interfaces funcionales y sigamos adelante. Aquí están las principales listas de IF (el resto son sus variedades):

    Predicado: toma algún valor T como argumento y devuelve un valor booleano.

    Ejemplo:boolean someMethod(T t);

  • Consumidor: toma un argumento de tipo T y no devuelve nada (nulo).

    Ejemplo:void someMethod(T t);

  • Proveedor: no toma nada como entrada, pero devuelve algún valor T.

    Ejemplo:T someMethod();

  • Función: toma un parámetro de tipo T como entrada y devuelve un valor de tipo R.

    Ejemplo:R someMethod(T t);

  • UnaryOperator: toma un argumento T y devuelve un valor de tipo T.

    Ejemplo:T someMethod(T t);

Arroyo

Las transmisiones son una forma de manejar estructuras de datos en un estilo funcional. Normalmente se trata de colecciones (pero puede utilizarlas en otras situaciones menos comunes). En un lenguaje más comprensible, Stream es un flujo de datos que procesamos como si trabajáramos con todos los datos al mismo tiempo, y no con fuerza bruta, como ocurre con for-each. Veamos un pequeño ejemplo. Supongamos que tenemos un conjunto de números que queremos filtrar (menos de 50), aumentarlos en 5 y enviar los primeros 4 números de los restantes a la consola. ¿Cómo hubiéramos hecho esto antes?
List<Integer> list = Arrays.asList(46, 34, 24, 93, 91, 1, 34, 94);

int count = 0;

for (int x : list) {

  if (x >= 50) continue;

  x += 5;

  count++;

  if (count > 4) break;

  System.out.print(x);

}
No parece haber mucho código y la lógica ya es un poco confusa. Veamos cómo se verá usando la transmisión:
Stream.of(46, 34, 24, 93, 91, 1, 34, 94)

      .filter(x -> x < 50)

      .map(x -> x + 5)

      .limit(4)

      .forEach(System.out::print);
Las transmisiones simplifican enormemente la vida al reducir la cantidad de código y hacerlo más legible. Para aquellos que quieran profundizar más en este tema, aquí tenéis un buen (incluso diría excelente) artículo sobre este tema .

lambda

Quizás la característica más importante y esperada sea la aparición de lambdas. ¿Qué es lambda? Este es un bloque de código que se puede pasar a diferentes lugares para poder ejecutarlo más tarde tantas veces como sea necesario. Suena bastante confuso, ¿no? En pocas palabras, usando lambdas, puede implementar un método de interfaz funcional (una especie de implementación de una clase anónima):
Runnable runnable = () -> { System.out.println("I'm running !");};

new Thread(runnable).start();
Implementamos el método run() rápidamente y sin trámites burocráticos innecesarios. Y sí: Runnable es una interfaz funcional. También uso lambdas cuando trabajo con secuencias (como en los ejemplos con secuencias anteriores). No profundizaremos mucho, ya que podemos profundizar bastante, dejaré un par de enlaces para que los chicos que todavía son excavadores de corazón puedan profundizar más:

para cada

Java 8 tiene un nuevo foreach que funciona con un flujo de datos como si fuera un flujo. He aquí un ejemplo:
List<Integer> someList = Arrays.asList(1, 3, 5, 7, 9);

someList.forEach(x -> System.out.println(x));
(análogo a someList.stream().foreach(…))

Referencia del método

Los métodos de referencia son una sintaxis nueva y útil diseñada para hacer referencia a métodos o constructores existentes de clases u objetos Java a través de :: Las referencias a métodos vienen en cuatro tipos:
  1. Enlace al diseñador:

    SomeObject obj = SomeObject::new

  2. Referencia del método estático:

    SomeObject::someStaticMethod

  3. Una referencia a un método no estático de un objeto de cierto tipo:

    SomeObject::someMethod

  4. Una referencia a un método regular (no estático) de un objeto específico.

    obj::someMethod

A menudo, las referencias a métodos se utilizan en secuencias en lugar de lambdas (los métodos de referencia son más rápidos que las lambdas, pero tienen una legibilidad inferior).
someList.stream()

        .map(String::toUpperCase)

      .forEach(System.out::println);
Para aquellos que quieran más información sobre métodos de referencia:

Tiempo API

Hay una nueva biblioteca para trabajar con fechas y horas: java.time. Del 8 al 13: una descripción completa de las versiones de Java.  Parte 1 - 2La nueva API es similar a cualquier Joda-Time. Las secciones más significativas de esta API son:
  • LocalDate es una fecha específica, por ejemplo: 2010-01-09;
  • LocalTime : hora teniendo en cuenta la zona horaria: 19:45:55 (análoga a LocalDate);
  • LocalDateTime - combo LocalDate + LocalTime - 2020-01-04 15:37:47;
  • ZoneId : representa zonas horarias;
  • Reloj : con este tipo puede acceder a la hora y fecha actuales.
Aquí hay un par de artículos realmente interesantes sobre este tema:

Opcional

Esta es una nueva clase en el paquete java.util , un contenedor de valores cuyo truco es que también puede contener null de forma segura . Recibir opcional: si pasamos nullOptional<String> someOptional = Optional.of("Something"); en Optional.of , obtendremos nuestra NullPointerException favorita . Para tales casos utilizan: - en este método no hay que temer al valor nulo. A continuación, cree un Opcional inicialmente vacío: Para verificar si está vacío, use: nos devolverá verdadero o falso. Realice una determinada acción si hay un valor y no haga nada si no hay ningún valor: Un método inverso que devuelve el valor pasado si Opcional está vacío (una especie de plan de respaldo): Puede continuar durante un tiempo muy, muy largo ( afortunadamente, Opcional ha agregado métodos con ambas manos generosas), pero no nos detendremos en esto. Es mejor para mí dejar un par de enlaces para empezar: Optional<String> someOptional = Optional.ofNullable("Something");Optional<String> someOptional = Optional.empty();someOptional.isPresent();someOptional.ifPresent(System.out::println);System.out.println(someOptional.orElse("Some default content")); Repasamos las innovaciones más famosas de Java 8; eso no es todo. Si quieres saber más te dejo esto:

Java 9

Entonces, el 21 de septiembre de 2017, el mundo vio JDK 9. Este Java 9 viene con un rico conjunto de características. Si bien no hay nuevos conceptos de lenguaje, las nuevas API y comandos de diagnóstico definitivamente serán de interés para los desarrolladores. Del 8 al 13: una descripción completa de las versiones de Java.  Parte 1 - 4

JShell (REPL - bucle de lectura-evaluación-impresión)

Esta es una implementación Java de una consola interactiva que se usa para probar la funcionalidad y usar diferentes construcciones en la consola, como interfaces, clases, enumeraciones, operadores, etc. Para iniciar JShell, sólo necesita escribir jshell en la terminal. Luego podemos escribir lo que nuestra imaginación nos permita: Del 8 al 13: una descripción completa de las versiones de Java.  Parte 1 - 5usando JShell, puedes crear métodos de nivel superior y usarlos dentro de la misma sesión. Los métodos funcionarán igual que los métodos estáticos, excepto que la palabra clave static se puede omitir. Lea más en el manual Java 9. REPL (JShell) .

Privado

A partir de la versión 9 de Java, tenemos la oportunidad de utilizar métodos privados en las interfaces (métodos predeterminados y estáticos, ya que simplemente no podemos anular otros debido a un acceso insuficiente). private static void someMethod(){} try-with-resources Se ha actualizado la capacidad de manejar excepciones de prueba con recursos:
BufferedReader reader = new BufferedReader(new FileReader("....."));
  try (reader2) {
  ....
}

Modularidad ( Rompecabezas )

Un módulo es un grupo de paquetes y recursos relacionados junto con un nuevo archivo descriptor de módulo. Este enfoque se utiliza para aflojar el acoplamiento del código. El acoplamiento flexible es un factor clave para la mantenibilidad y extensibilidad del código. La modularidad se implementa en diferentes niveles:
  1. Lenguaje de programación.
  2. Máquina virtual.
  3. API estándar de Java.
JDK 9 viene con 92 módulos: podemos usarlos o crear los nuestros propios. Aquí hay un par de enlaces para una mirada más profunda:

Colección inmutable

En Java 9, fue posible crear y completar una colección con una línea, mientras la hacía inmutable (anteriormente, para crear una colección inmutable, necesitábamos crear una colección, llenarla con datos y llamar a un método, por ejemplo, Colecciones.listainmodificable). Un ejemplo de tal creación: List someList = List.of("first","second","third");

Otras innovaciones:

  • Opcional expandido (se agregaron nuevos métodos);
  • las interfaces ProcessHandle y ProcessHandle aparecieron para controlar las acciones del sistema operativo;
  • G1: recolector de basura predeterminado;
  • Cliente HTTP compatible con protocolos HTTP/2 y WebSocket;
  • Corriente ampliada;
  • se agregó el marco API Reactive Streams (para programación reactiva);
Para una inmersión más completa en Java 9, te aconsejo que leas:

Java 10

Entonces, seis meses después del lanzamiento de Java 9, en marzo de 2018 (lo recuerdo como ayer), Java 10 entró en escena. Del 8 al 13: una descripción completa de las versiones de Java.  Parte 1 - 6

var

Ahora no tenemos que proporcionar un tipo de datos. Marcamos el mensaje como var y el compilador determina el tipo de mensaje según el tipo de inicializador presente a la derecha. Esta característica solo está disponible para variables locales con un inicializador: no se puede usar para argumentos de métodos, tipos de retorno, etc., ya que no existe un inicializador para poder definir el tipo. Var de ejemplo (para tipo String):
var message = "Some message…..";
System.out.println(message);
var no es una palabra clave: es esencialmente un nombre de tipo reservado, como int . El beneficio de var es grande: las declaraciones de tipo requieren mucha atención sin aportar ningún beneficio, y esta característica ahorrará tiempo. Pero al mismo tiempo, si una variable se obtiene de una larga cadena de métodos, el código se vuelve menos legible, ya que no queda claro de inmediato qué tipo de objeto se encuentra allí. Dedicado a quienes quieran familiarizarse más con esta funcionalidad:

Compilador JIT (GraalVM)

Sin más preámbulos, permítame recordarle que cuando ejecuta el comando javac, la aplicación Java se compila desde el código Java al código de bytes JVM, que es la representación binaria de la aplicación. Pero un procesador de computadora normal no puede simplemente ejecutar el código de bytes de JVM. Para que su programa JVM funcione, necesita otro compilador para este código de bytes, que se convierte en código de máquina que el procesador ya puede usar. Comparado con javac, este compilador es mucho más complejo, pero también produce código de máquina de mayor calidad. Actualmente, OpenJDK contiene la máquina virtual HotSpot, que a su vez cuenta con dos compiladores JIT principales. El primero, C1 ( compilador de cliente ), está diseñado para un funcionamiento a mayor velocidad, pero la optimización del código se ve afectada. El segundo es C2 (compilador de servidor). La velocidad de ejecución sufre, pero el código está más optimizado. ¿Cuándo se utiliza cuál? C1 es excelente para aplicaciones de escritorio donde las pausas JIT prolongadas no son deseables, y C2 es excelente para programas de servidor de larga ejecución donde dedicar más tiempo a la compilación es bastante soportable. La compilación multinivel es cuando la compilación pasa por C1 por primera vez y el resultado pasa por C2 (utilizado para una mayor optimización). GraalVM es un proyecto creado para reemplazar completamente a HotSpot. Podemos pensar en Graal como varios proyectos relacionados: un nuevo compilador JIT para HotSpot y una nueva máquina virtual políglota. La peculiaridad de este compilador JIT es que está escrito en Java. La ventaja del compilador Graal es la seguridad, es decir, no fallas, sino excepciones, no pérdidas de memoria. También tendremos un buen soporte IDE y podremos utilizar depuradores, perfiladores u otras herramientas convenientes. Además, el compilador puede ser independiente de HotSpot y podrá crear una versión de sí mismo compilada JIT más rápida. Para excavadores:

Paralelo G1

El recolector de basura G1 es ciertamente genial, de eso no hay duda, pero también tiene un punto débil: realiza un ciclo de GC completo de un solo subproceso. En un momento en el que necesitas toda la potencia del hardware que puedas reunir para encontrar objetos no utilizados, estamos limitados a un solo hilo. Java 10 solucionó este problema. Ahora el GC ahora funciona con todos los recursos que le agregamos (es decir, se vuelve multiproceso). Para lograr esto, los desarrolladores del lenguaje han mejorado el aislamiento de las fuentes principales de GC, creando una interfaz limpia y agradable para GC. Los desarrolladores de esta ternura, OpenJDK, tuvieron que limpiar específicamente el volcado en el código para no solo simplificar al máximo la creación de nuevos GC, sino también para permitir deshabilitar rápidamente los GC innecesarios del ensamblaje. Uno de los principales criterios de éxito es la ausencia de una reducción en la velocidad operativa después de todas estas mejoras. Miremos también: Otras innovaciones:
  1. Se introduce una interfaz limpia de recolector de basura. Esto mejora el aislamiento del código fuente de diferentes recolectores de basura, lo que permite integrar recolectores alternativos de manera rápida y sencilla;
  2. Combinar fuentes JDK en un repositorio;
  3. Las colecciones recibieron un nuevo método: copyOf (Collection) , que devuelve una copia inmutable de esta colección;
  4. Opcional (y sus variantes) tiene un nuevo método .orElseThrow() ;
  5. A partir de ahora, las JVM saben que se están ejecutando en un contenedor Docker y recuperarán la configuración específica del contenedor en lugar de consultar el sistema operativo en sí.
Aquí hay algunos materiales más para una introducción más detallada a Java 10: Solía ​​​​estar muy confundido por el hecho de que algunas versiones de Java se llamaban 1.x. Me gustaría ser claro: las versiones de Java anteriores a la 9 simplemente tenían un esquema de nombres diferente. Por ejemplo, Java 8 también se puede llamar 1.8 , Java 5 - 1.5 , etc. Y ahora vemos que con la transición a las versiones de Java 9, el esquema de nombres también ha cambiado y las versiones de Java ya no tienen el prefijo 1.x. . Este es el final de la primera parte: repasamos las nuevas características interesantes de Java 8-10. Continuaremos conociendo las últimas novedades en la próxima publicación .
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION