Prefacio: Java 8 ha sido lanzado por algún tiempo, y todos los signos indican que Java 8 es un cambio importante en la distribución. Ya hay muchos artículos sobre geeks del código Java que introducen nuevas características de Java 8, como jugar con Java 8 Lambdas y Concurrencia, Java 8 Tutorial de API de hora de fecha: Interfaz LocalDatetime y Abstract Class Versus en la era JDK 8. Este artículo también se refiere a alguna otra información, como: 15 debe leer los tutoriales de Java 8 y el lado oscuro de Java 8. Este artículo ha compilado la información anterior y la ha compilado en un libro de texto de referencia sobre las nuevas características de Java 8. Espero que gane algo.
1. Introducción
No hay duda de que Java 8 es la versión más importante de Java desde Java 5 (lanzado en 2004). Esta versión contiene más de una docena de nuevas características en idiomas, compiladores, bibliotecas, herramientas y JVM. En este artículo, aprenderemos estas nuevas características y usaremos ejemplos prácticos para ilustrar qué escenarios son adecuados para su uso.
Este tutorial contiene varios tipos de problemas que a menudo enfrentan los desarrolladores de Java:
idioma
Compilador
Biblioteca
herramienta
Tiempo de ejecución (JVM)
2. Nuevas características del idioma Java
Java 8 es una versión principal de Java. Algunas personas creen que, aunque los desarrolladores de Java esperan estas nuevas características, también requiere mucho esfuerzo para aprender. En esta sección, presentaremos la mayoría de las nuevas características de Java 8.
2.1 expresiones lambda e interfaces funcionales
Las expresiones Lambda (también conocidas como cierres) son los cambios de lenguaje más grandes y más esperados en Java 8. Nos permite pasar las funciones como parámetros de un método, o procesar el código en sí como datos: los desarrolladores funcionales están muy familiarizados con estos conceptos. Muchos idiomas en plataformas JVM (Groovy, Scala, etc.) han apoyado las expresiones de lambda desde su nacimiento, pero los desarrolladores de Java no tienen más remedio que usar clases internas anónimas en lugar de expresiones lambda.
El diseño de Lambda tomó mucho tiempo y muchos esfuerzos comunitarios, y finalmente encontró una solución de implementación de compromiso que podría lograr una estructura de lenguaje simple y compacta. La expresión de lambda más simple puede estar compuesta por una lista de parámetros separados por comas, -> símbolo y bloque de declaración, por ejemplo:
Arrays.aslist ("a", "b", "d") .ForEach (e -> system.out.println (e));
En el código anterior, el tipo de parámetro E se deriva por el razonamiento del compilador, y también puede especificar explícitamente el tipo de parámetro, por ejemplo:
Arrays.aslist ("a", "b", "d") .forEach ((cadena e) -> system.out.println (e));
Si una expresión de Lambda requiere un bloque de declaración más complejo, puede encerrar el bloque de instrucciones con aparatos ortopédicos, similar al cuerpo de una función en Java, por ejemplo:
Arrays.aslist ("a", "b", "d") .forEach (e -> {System.out.print (e); system.out.print (e);});Las expresiones Lambda pueden consultar a los miembros de la clase y las variables locales (que convertirán implícitamente estas variables en final), por ejemplo, los siguientes dos bloques de código tienen exactamente el mismo efecto:
String separator = ","; arrays.aslist ("a", "b", "d") .forEach ((string e) -> system.out.print (e + separator));y
Final String separator = ","; arrays.aslist ("a", "b", "d") .forEach ((string e) -> system.out.print (e + separator));Las expresiones Lambda tienen valores de retorno, y el tipo de valores de retorno también se deriva por la inferencia del compilador. Si el bloque de instrucciones en la expresión de Lambda tiene solo una línea, no necesita usar la declaración de retorno. Los siguientes dos fragmentos de código tienen el mismo efecto:
Arrays.aslist ("a", "b", "d") .sort ((e1, e2) -> e1.compareto (e2));y
Arrays.aslist ("a", "b", "d") .sort ((e1, e2) -> {int resultado = e1.compareto (e2); return resultado;});Para hacer que las funciones existentes sean bien compatibles con las expresiones lambda, los diseñadores de Lambda consideraron muchos métodos, por lo que se les ocurrió el concepto de interfaz de función. La interfaz de función se refiere a una interfaz con una sola función, y dicha interfaz puede convertirse implícitamente en una expresión de lambda. java.lang.runnable y java.util.concurrent.callable son los mejores ejemplos de interfaces funcionales. En la práctica, las interfaces funcionales son muy frágiles: mientras un desarrollador agrega una función a la interfaz, la interfaz ya no es una interfaz funcional, lo que resulta en una falla de compilación. Para superar esta vulnerabilidad a nivel de código y establecer explícitamente que una interfaz es una interfaz funcional, Java 8 proporciona una anotación especial @FunctionalInterface (todas las interfaces relacionadas en la biblioteca Java ya tienen esta anotación), para dar una definición simple de una interfaz funcional:
@FunctionalInterface Public Interface funcional {Void Method ();}Sin embargo, una cosa a tener en cuenta es que el método predeterminado y el método estático no destruirán la definición de la interfaz funcional, por lo que el siguiente código es legal.
@FunctionalInterface Public Interface FunctionDefaultMethods {Void Method (); predeterminado void defaultMethod () {}}Las expresiones de Lambda, como el mayor punto de venta de Java 8, tienen el potencial de atraer a más desarrolladores para unirse a la plataforma JVM y usar el concepto de programación funcional en programación pura Java. Si necesita saber más sobre las expresiones Lambda, puede consultar la documentación oficial.
2.2 Métodos predeterminados y estáticos de interfaces
Java 8 utiliza dos nuevos conceptos para extender el significado de una interfaz: el método predeterminado y el método estático. El método predeterminado hace que la interfaz sea un poco similar a los rasgos, pero los objetivos a lograr son diferentes. El método predeterminado permite a los desarrolladores agregar nuevos métodos a las interfaces existentes sin romper la compatibilidad binaria, es decir, no forzar clases que implementen la interfaz para implementar el método recién agregado al mismo tiempo.
La diferencia entre un método predeterminado y un método abstracto es que se debe implementar un método abstracto, mientras que un método predeterminado no lo hace. Los métodos predeterminados proporcionados por la interfaz serán heredados o sobrescribidos por la clase de implementación de la interfaz. El código de ejemplo es el siguiente:
Interfaz privada Desfaulable {// Las interfaces ahora permiten métodos predeterminados, el implementador puede o // puede no implementarlos (anular). cadena predeterminada Notrequired () {return "Implementación predeterminada"; }} Clase estática privada DefirtableImpl implementa Defaulable {} Clase estática privada INCVIRNABLEMPL implementa Defaulable {@Override public String Notrequired () {Return "Implementación anulada";}}La interfaz Defaulable utiliza la palabra clave predeterminada para definir un método predeterminado Notrequired (). La clase DefirtableImpl implementa esta interfaz y hereda los métodos predeterminados en esta interfaz de forma predeterminada; La clase OveridableImpl también implementa esta interfaz, pero anula los métodos predeterminados de la interfaz y proporciona una implementación diferente.
Otra característica interesante presentada por Java 8 es que los métodos estáticos se pueden definir en las interfaces. El código de ejemplo es el siguiente:
Interfaz privada DefaulableFactory {// Interfaces ahora Permitir métodos estáticos Defaulable Create (Proveedor <Foulable> Proveedor) {return Supply.get ();}}El siguiente fragmento de código integra los escenarios de uso de métodos predeterminados y métodos estáticos:
public static void main (string [] args) {defaulaable default = defaulableFactory.create (defaultableImpl :: new); system.out.println (defaultable.notRequired ()); defaulable = defaulableFactory.create (overidableImpl :: new); system.println (defaultable.notrequired ();});});});La salida de este código es la siguiente:
Implementación predeterminada
Implementación anulada
Dado que la implementación del método predeterminado en el JVM proporciona soporte a nivel de código de bytecodo, es muy eficiente. El método predeterminado permite interfaces mejoradas sin romper el sistema de herencia existente. La aplicación de esta característica en la biblioteca oficial es: Agregar nuevos métodos a la interfaz java.util.collection, como stream (), parallelStream (), foreach () y remoTiF (), etc.
Aunque los métodos predeterminados tienen tantos beneficios, deben usarse con precaución en el desarrollo real: en los sistemas de herencia complejos, los métodos predeterminados pueden causar ambigüedad y errores de compilación. Si desea saber más detalles, consulte la documentación oficial.
2.3 Referencia de método
La referencia del método permite a los desarrolladores hacer referencia directamente a los métodos existentes, constructores de clase Java o objetos de instancia. Las referencias de métodos y las expresiones Lambda se utilizan junto entre sí, lo que hace que el constructor de la clase Java se vea compacto y conciso, sin un montón de código de plantilla complejo.
En el ejemplo de Simon, la clase CAR es un ejemplo de diferentes referencias de métodos, que puede ayudar a los lectores a distinguir entre cuatro tipos de referencias de métodos.
Public Static Class Car {public static Car create (proveedor final <Car> Proveedor) {return proveedor.get ();} public static void colegue (auto de automóvil final) {system.out.println ("colisionado" + car.ToString ());} public void seguir (final de automóvil final) {System.Println ("siguiendo" + otro.Tostring (); {System.out.println ("reparado" + this.ToString ());}}El tipo de referencia para el primer método es una referencia de constructor, la sintaxis es clase :: nueva o más general forma: clase <t> :: nueva. Nota: Este constructor no tiene parámetros.
coche final de coche = car.create (coche :: nuevo); Lista final <Or> cars = arrays.aslist (coche);
El tipo de referencia del segundo método es una referencia del método estático, y la sintaxis es clase :: static_method. Nota: Este método acepta un parámetro de tipo de automóvil.
CARS.FOREACH (CAR :: COLLO);
El tercer método hace referencia al tipo es una referencia a un método miembro de una clase determinada, y la sintaxis es clase :: método. Tenga en cuenta que este método no define el parámetro:
Cars.ForEach (coche :: reparación);
El cuarto método referenciado es una referencia a un método miembro de un objeto de instancia, y la sintaxis es instancia :: método. Nota: Este método acepta un parámetro de tipo de automóvil:
Policía de automóvil final = car.create (auto :: nuevo); Cars.Foreach (Policía :: Follow);
Ejecute el ejemplo anterior y puede ver la siguiente salida en la consola (la instancia del automóvil puede ser diferente):
Colisionado com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d reparado com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d siguiendo el seguimiento de los seguidores de los seguidores de los siguientes com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d
Si desea comprender y aprender contenido más detallado, puede consultar la documentación oficial
2.4 Repetir comentarios
Desde la introducción de anotaciones en Java 5, esta característica se ha vuelto muy popular y se ha utilizado ampliamente en varios marcos y proyectos. Sin embargo, las anotaciones tienen una gran limitación: la misma anotación no se puede usar varias veces en el mismo lugar. Java 8 rompe esta limitación e introduce el concepto de anotaciones repetidas, lo que permite que la misma anotación se use varias veces en el mismo lugar.
Definir anotaciones repetidas en Java 8 usando la anotación @Repeatable en realidad no es una mejora a nivel de idioma, sino un truco hecho por el compilador, y la tecnología subyacente sigue siendo la misma. Puede usar el siguiente código para explicar:
paquete com.javacodegeeks.java8.repeatable.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.retention; import java.lang.annotation.retentionPolicy; import java.lang.annotation.target; public class RepetingAnnotations {@Target (elementtype.type) @retention (retentionPolicy.runtime) public @interface filtros {filtre [] value ();} @Target (elementType.type) @Retention (retentionPolicy.runtime) @Repeatable (filtr.class.class) public @interface filtro {string value ();};};};};};};};}; @Filter ("filtre1") @filter ("filtre2") interfaz pública filtrable {} public static void main (string [] args) {for (filtre filtre: filtreable.class.getAnnotationsByType (filtre.class) {system.println (filtro.value ());}}}}}}Como podemos ver, la clase de filtro aquí usa la anotación @Repeatable (Filters.Class), y Filters es un contenedor que almacena anotaciones de filtro. El compilador intenta bloquear estos detalles de los desarrolladores. De esta manera, la interfaz filtrable se puede anotar con dos anotaciones de filtro (aquí no se menciona información sobre los filtros).
Además, la API de reflexión proporciona un nuevo método: getAnnotationsByType (), que puede devolver anotaciones duplicadas de cierto tipo, por ejemplo
Filtable.class.getannoation (filters.class) devolverá dos instancias de filtro, y la salida de contenido a la consola es la siguiente:
filtro1
filtro2
Si desea saber más, puede consultar la documentación oficial.
2.5 Inferencia mejor tipo de tipo
El compilador Java 8 ha realizado grandes mejoras en la inferencia de tipo. En muchos escenarios, el compilador puede deducir el tipo de datos de cierto parámetro, lo que hace que el código sea más conciso. El código de ejemplo es el siguiente:
paquete com.javacodegeeks.java8.type.inference; Valor de clase pública <T> {public static <t> t defaultValue () {return null; } public t getorDefault (t value, t defaultValue) {return (value! = null)? Valor: DefaultValue;}}El siguiente código es una aplicación del valor de tipo <String>:
paquete com.javacodegeeks.java8.type.inference; public class typeInference {public static void main (string [] args) {value final <string> valor = nuevo valor <> (); value.getorDefault ("22", value.defaultValue ());}}El tipo de valor de parámetro. DefaultValue () es derivado por el compilador y no es necesario especificar explícitamente. En Java 7, este código tendrá un error de compilación a menos que se utilice el valor. <String> DefaultValue ().
2.6 ampliar los escenarios de la aplicación de las anotaciones
Java 8 amplía los escenarios de la aplicación de anotaciones. Ahora, las anotaciones se pueden usar en casi cualquier elemento: variables locales, tipos de interfaz, superclase y clases de implementación de interfaz, e incluso en definiciones de excepción de funciones. Aquí hay algunos ejemplos:
paquete com.javacodegeeks.java8.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.retention; import java.lang.annotation.retentionPolicy; import java.lang.annotation.target; import java.util.arrayList; import java.util.collection; Annotaciones de clase pública {@Retention (retentionPolicy.Runtime) @Target ({elementType.type_use, elementType.type_parameter}) public @interface nonempty {} public static class Holder <@nonempty t> extiende @nonempty objeto {public void method () lanza @nonempty excepción {}}}}}} "no utilizado") public static void main (string [] args) {Holder final <string> holder = new @NonEmpty Holder <String> (); @Nonempty Collection <@nonempty String> Strings = new ArrayList <> (); }}ElementType.Type_user y ElementType.Type_parameter son dos nuevas anotaciones agregadas a Java 8 para describir los escenarios de uso de las anotaciones. Lenguaje java
Yan también realizó los cambios correspondientes para identificar estas notas recientemente agregadas.
3. Nuevas características del compilador Java
3.1 Nombre del parámetro
Para obtener los nombres de parámetros de los métodos en los programas Java en tiempo de ejecución, las generaciones anteriores de programadores de Java deben usar diferentes métodos, como Paranamer Liberario. Java 8 finalmente normaliza esta característica, con soporte a nivel de lenguaje (usando la API de reflexión y el parámetro.getName () método) y el nivel de bytecode (usando el nuevo compilador JAVAC y el parámetro de parámetros).
paquete com.javacodegeeks.java8.parameter.names; import java.lang.reflect.method; import java.lang.reflect.parameter; public class Parameternames {public static void main (string [] args) lanza excepción {método método = parameternames.class.getMethod ("main", string []. class); for (parámetro de parámetro final: método.getParameter ()) {system.out.println ("parámetro:" + parameter.getName ());}}}En Java 8, esta función se desactiva de forma predeterminada, por lo que si compila el código anterior sin el parámetro -Parameters y la ejecuta, los siguientes resultados se emitirán:
Parámetro: Arg0
Si se usa el parámetro -Parameters, se emitirá el siguiente resultado (resultado correcto):
Parámetro: Args
Si usa Maven para la administración de proyectos, puede configurar el parámetro -Parameters en el elemento de configuración del compilador Maven-Compiler-Plugin:
<glugin> <MoupRoid> org.apache.maven.plugins </groupid> <artifactid> maven-compiler-plugin </artifactid> <verSerse> 3.1 </version> <figuration> <paspilerarGument>-parámetros </compilerargument> <overeur> 1.8 </over> <arget> 1.8 </gencation> </seltuguration> </plugin>
4. Nuevas características de la biblioteca oficial de Java
Java 8 ha agregado muchas clases de herramientas nuevas (clases de fecha/hora) y clases de herramientas existentes extendidas para admitir una programación concurrente moderna, programación funcional, etc.
4.1 Opcional
El error más común en las aplicaciones Java son las excepciones de valor nulo. Antes de Java 8, Google Guava introdujo la clase opcional para resolver nullpointerException, evitando así el código fuente contaminado por varios cheques nulos, para que los desarrolladores puedan escribir código más limpio. Java 8 también agrega opcional a la biblioteca oficial.
Opcional es solo algo fácil: almacenar un valor de Tipo T o NULL. Proporciona algunas interfaces útiles para evitar una verificación nula explícita, y puede consultar la documentación oficial de Java 8 para obtener más detalles.
A continuación, echemos un vistazo a algún ejemplo de uso opcional: un valor que puede estar vacío o un valor de cierto tipo:
Opcional <String> fullName = Oppectional.ofNullable (null); system.out.println ("El nombre completo está configurado?" + FullName.ispresent ()); System.out.println ("Nombre completo:" + fullname.orelseget (() -> "[ninguno]")); System.out.println (fullName.map (s -> "hey" + s + "!") .Orelse ("¡Hey Stranger!");Si la instancia opcional contiene un valor no nulo, el método isPresent () devuelve verdadero, de lo contrario devuelve falso; método orelseget (), y la instancia opcional se mantiene nulo, puede aceptar el valor predeterminado generado por una expresión de lambda; El método MAP () puede convertir el valor de la instancia opcional existente en un nuevo valor; El método orelse () es similar al método orelseget (), pero devuelve el valor predeterminado aprobado cuando se mantiene en retención nulo.
Los resultados de salida del código anterior son los siguientes:
¿Se establece el nombre completo? Falso nombre completo: [Ninguno] ¡Hola, extraño!
Echemos un vistazo a otro simple ejemplo:
Opcional <String> FirstName = Oppectional.of ("Tom"); System.out.println ("El primer nombre está configurado?" + FirstName.ispresent ()); System.out.println ("Nombre:" + firstName.OrelSeget (() -> "[Ninguno]")); System.out.println (FirstName.map (S -> "Hey" + S + "!") .Orelse ("¡Hey Stranger!"); System.out.println ();La salida de este ejemplo es:
¿Se establece el primer nombre? Verdadero nombre: Tom Hey Tom!
Si desea saber más detalles, consulte la documentación oficial.
4.2 transmisiones
La API de transmisión recién agregada (java.util.stream) introduce la programación funcional del entorno generado en la biblioteca Java. Esta es, con mucho, la mayor mejora de las bibliotecas Java para que los desarrolladores puedan escribir código más eficiente, conciso y compacto.
La API de Steam simplifica enormemente las operaciones de recolección (veremos más que solo colecciones más adelante). Primero, echemos un vistazo a esta clase llamada tarea:
Public Class Streams {Private Enum Status {Open, cerrado}; Tarea de clase final estática privada {Estado de estado final privado; Puntos enteros finales privados; tarea (estado final, puntos enteros finales) {this.status = status; this.Points = Points;} public entero getPoints () {puntos de retorno;} public status getStatus () {status de return;} @Override public String toString () {return string.format ("[ %s, %d]", estado, puntos);}}}La clase de tareas tiene un concepto de fracción (o pseudo-complejidad), y hay otros dos estados: abierto o cerrado. Ahora suponga que hay una colección de tareas:
colección final <ark> tareas = arrays.aslist (nueva tarea (status.open, 5), nueva tarea (status.open, 13), nueva tarea (status.cleed, 8));
Primero, veamos una pregunta: ¿cuántos puntos de estado abiertos hay en esta colección de tareas? Antes de Java 8, para resolver este problema, debe usar Foreach para pasar la colección de tareas; Sin embargo, en Java 8, puede usar Steams para resolverlo: incluya una lista de una serie de elementos y admitir el procesamiento secuencial y paralelo.
// Calcule los puntos totales de todas las tareas activas utilizando Sum () Long TotalPointSoFoPentasks = Tasks.stream (). Filter (Task -> Task.getStatus () == STATAT.OPEN) .MAPTOINT (TARK :: GetPoints) .sum (); System.out.Println ("Total Points:" + TotalPointSoPentAnks);La salida de la consola para ejecutar este método es:
Puntos totales: 18
Hay muchos puntos de conocimiento que vale la pena hablar aquí. Primero, el conjunto de tareas se convierte en una representación de vapor; En segundo lugar, la operación del filtro en el vapor filtra todas las tareas cerradas; Tercero, la operación Maptoint convierte el flujo de tareas en una colección entera basada en el método Tarea :: GetPoints de cada instancia de tarea; Finalmente, la suma se calcula mediante el método de suma para obtener el resultado final.
Antes de conocer el próximo ejemplo, debe recordar algunos puntos de conocimiento sobre Steams (haga clic aquí para obtener más detalles). Las operaciones por encima del vapor se pueden dividir en operaciones intermedias y operaciones tardías.
Las operaciones intermedias devolverán un nuevo vapor: realizar una operación intermedia (como el filtro) no realizará la operación de filtrado real, sino que creará un nuevo vapor y pondrá los elementos que cumplen con las condiciones en el vapor original en el vapor recién creado.
Las operaciones tardías (como foreach o suma) atraviesan el vapor y obtendrán resultados o resultados acompañantes; Después de realizar las operaciones tardías, la línea de procesamiento de Steam se ha procesado y no se puede utilizar. En casi todos los casos, las operaciones tardías están atravesando el vapor de inmediato.
Otro valor de Steam es el soporte creativo para el procesamiento paralelo. Para la recopilación de tareas anteriores, podemos usar el siguiente código para calcular la suma de puntos de todas las tareas:
// Calcule los puntos totales de todas las tareas finales Double TotalPoints = tasks.stream (). Parallel (). MAP (Task -> Task.GetPoints ()) // o Map (Task :: GetPoints) .Reduce (0, Integer :: Sum); System.out.println ("Total Points (todas las tareas):" + TotalPoints);Aquí usamos el método paralelo para procesar todas las tareas en paralelo y calcular el resultado final utilizando el método Reducir. La salida de la consola es la siguiente:
Puntos totales (todas las tareas): 26.0
Para una colección, a menudo es necesario agrupar elementos de acuerdo con ciertas condiciones. Este tipo de tarea se puede completar rápidamente utilizando la API proporcionada por Steam. El código es el siguiente:
// Las tareas de grupo por su estado final de estado <status, list <arke>> map = tareas.stream (). Coleccionar (coleccionar.groupingby (tarea :: getStatus)); system.out.println (map);
La salida de la consola es la siguiente:
{Cerrado = [[Cerrado, 8]], Open = [[Open, 5], [Open, 13]]}
La última pregunta de ejemplo sobre la recopilación de tareas es: cómo calcular la proporción de puntos de cada tarea en la colección en la colección. El código de procesamiento específico es el siguiente:
// Calcule el peso de cada tarea (como porcentaje de puntos totales) Collection final <String> result = tareas.stream () // stream <string> .maptaint (tarea :: getPoints) // intstream .aslongstream () // longstream .maptodouble (puntos -> puntos / total puntos) // doblón .boxed () // stream <doble> .maptolong (peso) (peso) (peso) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight) (weight weight) (weight weights) (weight weunds) Longstream .maptoobj (porcentaje -> porcentaje + "%") // stream <string> .collect (coleccions.tolist ()); // List <String> System.out.println (resultado);
Los resultados de la salida de la consola son los siguientes:
[19%, 50%, 30%]
Finalmente, como se mencionó anteriormente, la API de Steam no solo puede actuar sobre las colecciones de Java, sino que las operaciones tradicionales de IO (leer datos de un archivo o una red por línea) pueden beneficiarse del procesamiento de Steam. Aquí hay un pequeño ejemplo:
ruta final ruta = nuevo archivo (nombre de archivo) .topath (); Prueba (stream <string> líneas = files.lines (ruta, StandardCharsets.utf_8)) {líneas.Onclose (() -> System.out.println ("¡Hecho!")) .ForEach (System.out :: println);}El método de transmisión OnClose devuelve una transmisión equivalente con un mango adicional. Este mango se ejecutará cuando se llame al método Close () de la transmisión. La API de transmisión, las expresiones Lambda y las referencias de métodos compatibles con los métodos predeterminados de la interfaz y los métodos estáticos son la respuesta de Java 8 al paradigma moderno del desarrollo de software.
4.3 API de fecha/hora (JSR 310)
Java 8 presenta una nueva API de fecha y hora (JSR 310) para mejorar el procesamiento de tiempo y fechas. La gestión de la hora y la fecha siempre ha sido el problema más doloroso para los desarrolladores de Java. java.util.date y luego java.util.calendar no han resuelto este problema (aún más confundido por los desarrolladores).
Debido a las razones anteriores, nació la biblioteca de terceros Joda-Time, que puede reemplazar la API de gestión del tiempo de Java. La nueva API de gestión de tiempo y fechas en Java 8 está profundamente influenciada por el tiempo de Joda y ha absorbido gran parte de la esencia del tiempo de Joda. El nuevo paquete Java.Time contiene todas las clases sobre la fecha, la hora, la zona horaria, el instante (similar hasta la fecha pero preciso para los nanosegundos), la duración (duración) y las operaciones de reloj. La API recientemente diseñada considera seriamente la invariancia de estas clases (conferencias aprendidas de java.util.calendar) y devuelve un nuevo objeto si se necesita modificar una instancia.
Echemos un vistazo a las clases clave y sus respectivos ejemplos de uso en el paquete Java.Time. Primero, la clase de reloj usa una zona horaria para devolver la hora y fecha de nanosegundos actuales. El reloj puede reemplazar System.CurrentTimemillis () y TimeZone.getDefault ().
// Obtenga el reloj del sistema como reloj final de compensación final de reloj = clock.systemutc (); system.out.println (clock.instant ()); system.out.println (clock.millis ());
La salida de este ejemplo es:
2014-04-12T15: 19: 29.282Z 1397315969360
En segundo lugar, concéntrese en las clases locales de Date y Localtime. LocalDate solo contiene la parte de la fecha en el sistema de calendario ISO-8601; Localtime solo contiene la parte de tiempo en el sistema de calendario. Los objetos de ambas clases se pueden construir utilizando objetos de reloj.
// Obtenga la fecha local y la hora local Date local Date Date = localDate.Now (); Final localdate dateFromClock = localDate.now (reloj); system.out.println (dateFromClock); // Obtenga la fecha local y la hora local Localtime Time = localtime.now (); TimeTime de LocaltimeFromClock final = Localtime.now (Clock); System.out.println (Time); System.out.println (TimeFromClock);
Los resultados de salida del ejemplo anterior son los siguientes:
2014-04-12 2014-04-12 11: 25: 54.568 15: 25: 54.568
La clase LocalDateTime contiene información sobre localDate y Localtime, pero no contiene información de zona horaria en el sistema de calendario ISO-8601. Aquí hay algunos ejemplos sobre LocalDate y Localtime:
// Obtenga la fecha/hora local LocalDateTime DateTime = localDateTime.Now (); Final localDateTime dateTimeFromClock = localDateTime.Now (reloj); System.out.println (DateTime); System.out.println (DateTimeFromClock);
Los resultados de salida del ejemplo anterior son los siguientes:
2014-04-12T11: 37: 52.309 2014-04-12T15: 37: 52.309
Si necesita información de datos/tiempo para una zona horaria específica, puede usar ZonedateTimeMe, que contiene la fecha y la hora del sistema de fecha ISO-8601, y tiene información de zona horaria. Aquí hay algunos ejemplos de uso de diferentes zonas horarias:
// Obtenga la fecha/hora en zonas Final ZonedDatetime ZonedDateTime = ZonedDateTime.Now (); Final ZonedDatetime ZonedDateTimeFromClock = ZonedDateTime.NOW (reloj); Final ZonedDatetime ZonedDateTimeFromZone = ZonedDateTime.now (ZoneId.of ("America/Los_angeles")); System.out.println (ZonedDateTime); System.out.println (ZonedDateTimeFromClock); System.out.println (ZonedDateTimeFromClock); System.out.println (ZonedDateTimeFromZone);La salida de este ejemplo es:
2014-04-12T11: 47: 01.017-04: 00 [America/New_york] 2014-04-12T15: 47: 01.017Z 2014-04-12T08: 47: 01.017-07: 00 [América/Los_angeles]
Finalmente, veamos la clase de duración, que mantiene el tiempo hasta segundos y nanosegundos. Esto facilita calcular la diferencia entre dos fechas, el código de ejemplo es el siguiente:
// Obtenga la duración entre dos fechas finales localDatetime de = localDatetime.of (2014, mes.april, 16, 0, 0, 0); local Datetime a = localDatetime.of (2015, mes.april, 16, 23, 59, 59); Duración final de duración = Duración. duración.todays ()); system.out.println ("Duración en horas:" + duración.tohours ());Este ejemplo se utiliza para calcular el número de días y horas entre el 16 de abril de 2014 y el 16 de abril de 2015, y la salida es la siguiente:
Duración en días: 365 Duración en horas: 8783
La impresión general de la nueva fecha y hora de Java 8 es relativamente positiva, en parte debido al impacto positivo del tiempo de Joda, y en parte porque el funcionario finalmente escuchó las necesidades de los desarrolladores. Si desea saber más detalles, puede consultar la documentación oficial.
4.4 Motor Nashorn JavaScript
Java 8 proporciona el nuevo motor Nashorn JavaScript, lo que nos permite desarrollar y ejecutar aplicaciones JS en el JVM. Nashorn JavaScript Engine es otra versión de implementación de javax.script.scriptEngine. Este tipo de motor de script sigue las mismas reglas y permite que Java y JavaScript se usen de manera interactiva. El código de ejemplo es el siguiente:
Scriptenginemanager ganager = new scriptengineManager (); scriptEngine motor = gereM.getEngineByName ("javascript"); system.out.println (motor.getClass (). GetName ()); system.out.println ("resultado:" + motor.eval ("function f () {return 1;); f () + 1;"));La salida de este código es la siguiente:
jdk.nashorn.api.scripting.nashornscriptEngine Resultados: 2
4.5 Base64
El soporte para la codificación de Base64 se ha agregado a la biblioteca Java 8 oficial, de modo que la codificación Base64 se puede realizar sin usar una biblioteca de terceros. El código de ejemplo es el siguiente:
paquete com.javacodegeeks.java8.base64; import java.nio.charset.standardcharsets; import java.util.base64; Public Class Base64S {public static void main (String [] args) {String final text = "Base64 Finalmente en Java 8!"; Cadena final ENCODED = Base64.GetEncoder (). EncodeToString (text.getBytes (StandardCharSets.utf_8)); System.out.println (encoded); String final decoded = new String (base64.getDeCoder (). Decode (encoded), StandardCharSets.utf_8); System.out.println (decodificado);}}La salida de este ejemplo es la siguiente:
QMFZZTY0IGZPBMFSBHKGAW4GSMF2YSA4IQ ==
Base64 Finalmente en Java 8!
El nuevo Base64API también admite la codificación y decodificación de URL y minas.
(Base64.getUrlEncoder () / base64.geturldecoder (), base64.getMimeEncoder () / base64.getMimedecoder ()).
4.6 matrices paralelas
La versión Java8 ha agregado muchos métodos nuevos para admitir el procesamiento de matriz paralelo. El método más importante es Parallelsort (), que puede acelerar significativamente la clasificación de matriz en máquinas múltiples. El siguiente ejemplo demuestra el método de la serie ParallelExxxx:
paquete com.javacodeeks.java8.parallel.arrays; import java.util.arrays; import java.util.concurrent.threadlocalrandom; clase pública ParallelArrays {public static void main (string [] args) {long [] arrayoFlong = new Long [20000]; Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();Arrays.parallelSort( arrayOfLong );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();}}上述这些代码使用parallelSetAll()方法生成20000个随机数,然后使用parallelSort()方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。上述例子的代码输出的结果是:
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 Sorted: 39 220 263 268 325 607 655 678 723 793
4.7 并发性
基于新增的lambda表达式和steam特性,为Java 8中为java.util.concurrent.ConcurrentHashMap类添加了新的方法来支持聚焦操作;另外,也为java.util.concurrentForkJoinPool类添加了新的方法来支持通用线程池操作(更多内容可以参考我们的并发编程课程)。
Java 8还添加了新的java.util.concurrent.locks.StampedLock类,用于支持基于容量的锁――该锁有三个模型用于支持读写操作(可以把这个锁当做是java.util.concurrent.locks.ReadWriteLock的替代者)。
在java.util.concurrent.atomic包中也新增了不少工具类,列举如下:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
5. 新的Java工具
Java 8提供了一些新的命令行工具,这部分会讲解一些对开发者最有用的工具。
5.1 Nashorn引擎:jjs
jjs是一个基于标准Nashorn引擎的命令行工具,可以接受js源码并执行。例如,我们写一个func.js文件,内容如下:
function f() { return 1; }; print( f() + 1 );可以在命令行中执行这个命令:jjs func.js,控制台输出结果是:
2
如果需要了解细节,可以参考官方文档。
5.2 类依赖分析器:jdeps
jdeps是一个相当棒的命令行工具,它可以展示包层级和类层级的Java类依赖关系,它以.class文件、目录或者Jar文件为输入,然后会把依赖关系输出到控制台。
我们可以利用jedps分析下Spring Framework库,为了让结果少一点,仅仅分析一个JAR文件:org.springframework.core-3.0.5.RELEASE.jar。
jdeps org.springframework.core-3.0.5.RELEASE.jar
这个命令会输出很多结果,我们仅看下其中的一部分:依赖关系按照包分组,如果在classpath上找不到依赖,则显示”not found”.
org.springframework.core-3.0.5.RELEASE.jar -> C:/Program Files/Java/jdk1.8.0/jre/lib/rt.jarorg.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)-> java.io -> java.lang -> java.lang.annotation -> java.lang.ref -> java.lang.reflect -> java.util -> java.util.concurrent -> org.apache.commons.logging not found-> org.springframework.asm not found-> org.springframework.asm.commons not foundorg.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)-> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util
更多的细节可以参考官方文档。
6. JVM的新特性
使用Metaspace(JEP 122)代替持久代(PermGen space)。在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。
7. 结论
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。