Articulo de referencia

Sintaxis de Java

Un fragmento de código Java. La sintaxis de Java es el conjunto de reglas que definen cómo se escribe e interpreta un programa Java . La sintaxis se deriva principalmente de C y...

Un fragmento de código Java.

La sintaxis de Java es el conjunto de reglas que definen cómo se escribe e interpreta un programa Java .

La sintaxis se deriva principalmente de C y C++ . A diferencia de C++, Java no tiene funciones ni variables globales, pero sí miembros de datos que se consideran variables globales . Todo el código pertenece a clases y todos los valores son objetos . La única excepción son los tipos de datos primitivos , que no se consideran objetos por motivos de rendimiento (aunque se pueden convertir automáticamente a objetos y viceversa mediante autoboxing ). Algunas características, como la sobrecarga de operadores o los tipos de datos enteros sin signo, se omiten para simplificar el lenguaje y evitar posibles errores de programación.

La sintaxis de Java se ha ido ampliando gradualmente a lo largo de numerosas versiones importantes del JDK , y ahora admite funcionalidades como la programación genérica y las funciones anónimas (literales de función, llamadas expresiones lambda en Java). Desde 2017, se publica una nueva versión del JDK dos veces al año.

Lo esencial

El programa Java "Hola, mundo!" es el siguiente:

public class HelloWorld { public static void main ( String [] args ) { System . out . println ( "Hello World!" ); } }

Desde Java 25, se puede escribir un programa "Hola Mundo" simplificado sin una clase explícita:

void main () { IO.println ( " ¡ Hola Mundo!" ); }

Identificador

Un identificador es el nombre de un elemento en el código . Existen ciertas convenciones de nomenclatura estándar que deben seguirse al seleccionar nombres para los elementos. En Java, los identificadores distinguen entre mayúsculas y minúsculas .

Un identificador puede contener:

  • Cualquier carácter Unicode que sea una letra (incluidas las letras numéricas como los números romanos ) o un dígito.
  • Símbolo monetario (como ¥ ).
  • Carácter de puntuación de conexión (como _ ).

Un identificador no puede:

  • Empieza con un dígito.
  • Debe ser igual a una palabra clave reservada, un literal nulo o un literal booleano .

Palabras clave

Palabras clave

Las siguientes palabras son palabras clave y no pueden utilizarse como identificadores bajo ninguna circunstancia; hay 47.

  • abstract
  • assert
  • boolean
  • break
  • byte
  • case
  • catch
  • char
  • class
  • continue
  • default
  • do
  • double
  • else
  • enum
  • extends
  • final
  • finally
  • float
  • for
  • if
  • implements
  • import
  • instanceof
  • int
  • interface
  • long
  • native
  • new
  • package
  • private
  • protected
  • public
  • return
  • short
  • static
  • super
  • switch
  • synchronized
  • this
  • throw
  • throws
  • transient
  • try
  • void
  • volatile
  • while

Identificadores reservados

Las siguientes palabras son palabras clave contextuales y solo están restringidas a ciertos contextos, de los cuales hay 16.

  • exports
  • module
  • non-sealed
  • open
  • opens
  • permits
  • provides
  • record
  • requires
  • sealed
  • to
  • transitive
  • var
  • when
  • with
  • yield

Palabras reservadas para valores literales

Las siguientes palabras se refieren a valores literales utilizados por el idioma, de los cuales hay 3.

  • true
  • false
  • null

No usado

Las siguientes palabras están reservadas como palabras clave, pero actualmente no tienen ningún uso ni propósito, de las cuales hay 4.

  • _
  • const
  • goto
  • strictfp

Literales

Los literales enteros son de inttipo por defecto a menos que longse especifique el tipo agregando Lun lsufijo al literal, por ejemplo 367L. Desde Java SE 7, es posible incluir guiones bajos entre los dígitos de un número para aumentar la legibilidad; por ejemplo, un número 145608987 se puede escribir como 145_608_987 .

Variables

Las variables son identificadores asociados a valores. Se declaran escribiendo el tipo y el nombre de la variable, y opcionalmente se inicializan en la misma instrucción asignándoles un valor.

int count ; // Declarando una variable no inicializada llamada 'count', de tipo 'int' count = 35 ; // Inicializando la variable int count = 35 ; // Declarando e inicializando la variable al mismo tiempo

Se pueden declarar e inicializar varias variables del mismo tipo en una sola instrucción utilizando la coma como delimitador.

int a , b ; // Declarar múltiples variables del mismo tipo int a = 2 , b = 3 ; // Declarar e inicializar múltiples variables del mismo tipo

Inferencia de tipo

Desde Java 10, es posible inferir automáticamente los tipos de las variables mediante el uso de var.

import java.io.FileOutputStream ;// El flujo tendrá el tipo FileOutputStream como se infiere de su inicializador var stream = new FileOutputStream ( "file.txt" );// Una declaración equivalente con un tipo explícito FileOutputStream stream = new FileOutputStream ( "file.txt" );

bloques de código

Los separadores { y } indican un bloque de código y un nuevo ámbito. Los miembros de una clase y el cuerpo de un método son ejemplos de lo que puede estar dentro de estas llaves en diversos contextos.

Dentro del cuerpo de los métodos, se pueden usar llaves para crear nuevos ámbitos, como sigue:

void hacerAlgo () { int a ;{ int b ; a = 1 ; }a = 2 ; b = 3 ; // Ilegal porque la variable b está declarada en un ámbito interno. }

Comentarios

Java tiene tres tipos de comentarios : comentarios tradicionales , comentarios de fin de línea y comentarios de documentación .

Los comentarios tradicionales, también conocidos como comentarios de bloque, comienzan con /*y terminan con */, y pueden abarcar varias líneas. Este tipo de comentario se derivó de C y C++.

/* Este es un comentario de varias líneas. Puede ocupar más de una línea. */

Los comentarios de fin de línea comienzan //y se extienden hasta el final de la línea actual. Este tipo de comentario también está presente en C++ y en C moderno.

// Este es un comentario de fin de línea

Los comentarios de documentación en los archivos fuente son procesados ​​por la herramienta Javadoc para generar la documentación. Este tipo de comentario es idéntico a los comentarios tradicionales, excepto que comienza con /**las convenciones definidas por la herramienta Javadoc y las sigue. Técnicamente, estos comentarios son un tipo especial de comentario tradicional y no están definidos específicamente en la especificación del lenguaje.

/** * Este es un comentario de documentación. * * @author John Doe */

Tipos universales

Las clases del paquete java.lang(pero no sus subpaquetes) se importan implícitamente en todos los programas, siempre que ningún tipo importado explícitamente tenga el mismo nombre. Algunos ejemplos importantes son:

java.lang.System

java.lang.Systemes una de las clases más fundamentales en Java. Es una clase de utilidad para interactuar con el sistema, que contiene flujos estándar de entrada, salida y error, propiedades del sistema y variables de entorno, tiempo y más.

java.lang.Object

java.lang.Objectes el tipo principal de Java . Es implícitamente la superclase de todas las clases que no declaran ninguna clase padre (por lo tanto, todas las clases en Java heredan de Object). Todos los valores se pueden convertir a este tipo, aunque para los valores primitivos esto implica autoboxing .

java.lang.Record

Todos los registros extienden implícitamente java.lang.Record. Por lo tanto, un record, aunque se trate como un class, no puede extender ninguna otra clase.

java.lang.Enum<E>

Todos los tipos enumerados (enums) en Java extienden implícitamente java.lang.Enum<E>. Por lo tanto, un enum, aunque se trata como un class, no puede extender ninguna otra clase. El parámetro de tipo Eestá restringido como .Enum<EextendsEnum<E>>

java.lang.Class<T>

java.lang.Class<T>es una clase que representa un classo interfaceen la aplicación en tiempo de ejecución. No se puede construir mediante instanciación directa, sino que la crea la JVM cuando una clase se deriva de los bytes de un .classarchivo . Se puede obtener un literal de clase a través de X.classen dicha clase X. Por ejemplo, String.classdevuelve Class<String>. Una clase desconocida que se está modelando se puede representar como Class<?>(donde ?denota un comodín ).

Aunque X.classparece estar accediendo a un campo llamado " class ", en realidad no existe tal campo con ese nombre ; de ​​hecho, está ejecutando la instrucción , que inserta una constante del grupo de constantes de tiempo de ejecución de la clase en la pila de operandos. [ 1 ]ldc

java.lang.String

java.lang.Stringes el tipo de cadena básico de Java. Es inmutable . No implementa Iterable<Character>, por lo que no se puede iterar sobre él en un bucle for-each, pero se puede convertir a char[]. Algunos métodos tratan cada unidad de código UTF-16 como un , pero también hay charmétodos disponibles para convertirlo a un int[]que es efectivamente UTF-32String . implementa java.lang.CharSequence, por lo que se puede acceder a charlos en el Stringmediante el método charAt().

java.lang.Throwable

java.lang.Throwablees el supertipo de todo lo que se puede lanzar o capturar con las sentencias throwy de Java catch. Sus subclases directas conocidas son java.lang.Error(para errores graves irrecuperables) y java.lang.Exception(para excepciones que pueden ocurrir naturalmente en la ejecución de un programa).

java.lang.Error

java.lang.Errores el supertipo de todas las clases de error y extiende Throwable. Se utiliza para indicar condiciones que una aplicación razonable no debería capturar.

java.lang.Exception

java.lang.Exceptiones el supertipo de todas las clases de excepción y extiende Throwable. Se utiliza para indicar condiciones que una aplicación razonable podría tener motivos para capturar.

java.lang.Math

java.lang.Mathes una clase de utilidad que contiene funciones matemáticas y constantes matemáticas (como Math.sin(), Math.pow(), y Math.PI).

java.lang.IO

java.lang.IOes una clase introducida en Java 25 (anteriormente ubicada en java.iocomo java.io.IO). Permite un acceso más sencillo a los flujos de entrada y salida estándar a través de System.iny System.out.

Primitivos

Cada tipo primitivo tiene una clase contenedora asociada (ver tipos primitivos ).

Estructura del programa

Las aplicaciones Java constan de colecciones de clases. Las clases existen en paquetes, pero también pueden estar anidadas dentro de otras clases.

mainmétodo

Cada aplicación Java debe tener un punto de entrada. Esto es cierto tanto para aplicaciones con interfaz gráfica como para aplicaciones de consola. El punto de entrada es el mainmétodo. Puede haber más de una clase con un mainmétodo, pero la clase principal siempre se define externamente (por ejemplo, en un archivo de manifiesto ). El mainmétodo junto con la clase principal debe declararse public. El método debe ser staticy se le pasan argumentos de línea de comandos como una matriz de cadenas. A diferencia de C++ o C# , nunca devuelve un valor y debe devolver void. Sin embargo, se puede especificar un código de retorno al sistema operativo llamando a System.exit().

public static void main ( String [] args ) { // ... }

Paquetes

Los paquetes son parte del nombre de una clase y se utilizan para agrupar y/o distinguir entidades con nombre de otras. Otro propósito de los paquetes es controlar el acceso al código junto con los modificadores de acceso. Por ejemplo, java.io.InputStreames un nombre de clase completamente calificado para la clase InputStreamque se encuentra en el paquete java.io.

Un paquete es esencialmente un espacio de nombres . Los paquetes no tienen jerarquías, aunque los puntos puedan sugerir lo contrario. Se puede controlar si un paquete es accesible externamente o internamente a un proyecto mediante módulos .

Un paquete se declara al inicio del archivo con la packagedeclaración:

paquete org.wikipedia.examples ;clase pública Foo { // ... }

Las clases con el publicmodificador deben colocarse en archivos con el mismo nombre y extensión .java y ubicarse en carpetas anidadas que correspondan al nombre del paquete. La clase anterior tendrá la siguiente ruta: .org.wikipedia.examples.Fooorg/wikipedia/examples/Foo.java

Módulos

La programación modular (módulos) se utiliza para agrupar paquetes y controlar rigurosamente qué paquetes pertenecen a la API pública. A diferencia de los archivos JAR , los módulos declaran explícitamente de qué módulos dependen y qué paquetes exportan. [ 2 ] Las declaraciones explícitas de dependencia mejoran la integridad del código, al facilitar el razonamiento sobre aplicaciones grandes y las dependencias entre los componentes de software.

La declaración del módulo se encuentra en un archivo llamado module-info.java , en la raíz de la jerarquía de archivos fuente del módulo. El JDK verificará las dependencias e interacciones entre módulos tanto en tiempo de compilación como en tiempo de ejecución.

Por ejemplo, la siguiente declaración de módulo declara que el módulo org.wikipedia.bardepende de otro org.wikipedia.bazmódulo y exporta los siguientes paquetes: org.wikipedia.bar.alphay org.wikipedia.bar.beta:

organización del módulo . Wikipedia . barra { requiere org . Wikipedia . baz ;exportaciones org.wikipedia.bar.alpha ; exportaciones org.wikipedia.bar.beta ; }

Los miembros públicos de org.wikipedia.bar.alphalos org.wikipedia.bar.betapaquetes serán accesibles para los módulos dependientes. Los miembros privados son inaccesibles incluso mediante métodos como la programación reflexiva (reflexión). En las versiones de Java de la 9 a la 16, si dicho "acceso ilegal" está permitido de facto depende de una configuración de la línea de comandos . [ 3 ]

El JDK se modularizó en Java 9. [ 4 ] Por ejemplo, la mayor parte de la biblioteca estándar de Java se exporta mediante el módulo java.base.

Declaración de importación

Una instrucción de importación se utiliza para resolver un tipo que pertenece a otro paquete (espacio de nombres). Se puede considerar similar a como se hace usingen C++.

Declaración de importación de tipo

Una declaración de importación de tipo permite que un tipo con nombre se referencie mediante un nombre simple en lugar del nombre completo que incluye el paquete. Las declaraciones de importación pueden ser de un solo tipo o de importación bajo demanda . Deben colocarse al principio del archivo de código, después de la declaración del paquete.

paquete org.wikipedia.examples ;import java.util.Random ; // Declaración de un solo tipopublic class ImportsTest { public static void main ( String [] args ) { /* La siguiente línea es equivalente a  * java.util.Random random = new java.util.Random();  * Habría sido incorrecto sin la importación.  */ Random random = new Random (); } }

En el código se mencionan las declaraciones de importación bajo demanda. Una "importación comodín" (o "importación glob") importa todos los tipos del paquete. Una "importación estática" importa los miembros del paquete.

/** * Esta forma de importar clases hace que todas las clases * del paquete java.util estén disponibles por nombre, podría usarse en lugar de la * declaración de importación del ejemplo anterior. */ import java.util.* ;

Una importación con comodín puede no importar nada si no existen símbolos en ese paquete. Por ejemplo, las instrucciones como son válidas, pero no hacen nada, ya que todas las clases residen en paquetes dentro del paquete ; dicha importación no importaría recursivamente todas las clases del paquete.importjava.*;java

Declaración de importación estática

Este tipo de declaración está disponible desde J2SE 5.0 . Las declaraciones de importación estática permiten acceder a miembros estáticos definidos en otra clase, interfaz, anotación o enumeración, sin especificar el nombre de la clase.

import static java.lang.System.out ; // 'out' es un campo estático en java.lang.Systempublic class HelloWorld { public static void main ( String [] args ) { /* La siguiente línea es equivalente a  System.out.println("¡Hola Mundo!");  y habría sido incorrecta sin la declaración de importación. */ out . println ( "¡Hola Mundo!" ); } }

Las declaraciones de importación bajo demanda permiten importar todos los campos del tipo:

/** * Esta forma de declaración hace que todos los * campos de la clase java.lang.System estén disponibles por nombre, y puede usarse en lugar * de la declaración de importación del ejemplo anterior. */ import static java.lang.System.* ;

Las constantes de enumeración también se pueden usar con importación estática. Por ejemplo, esta enumeración está en el paquete llamado screen:

enumeración pública Colores { ROJO , AZUL , VERDE };

Es posible utilizar declaraciones de importación estáticas en otra clase para recuperar las constantes de enumeración:

import org.wikipedia.screen.ColorName ; import static org.wikipedia.screen.ColorName.* ;public class Dots { /* La siguiente línea es equivalente a 'ColorName foo = ColorName.RED',  y habría sido incorrecta sin la importación estática. */ ColorName foo = RED ;void shift () { /* La siguiente línea es equivalente a  if (foo == ColorName.RED) foo = ColorName.BLUE; */ if ( foo == RED ) { foo = BLUE ; } } }

Declaración de importación del módulo

Desde Java 25, los módulos se pueden usar en las declaraciones de importación para importar automáticamente todos los paquetes exportados por el módulo. [ 5 ] Esto se hace usando import module. Por ejemplo, es equivalente aimportmodulejava.sql;

import java.sql.* ; import javax.sql.* ; // Exportaciones indirectas restantes de java.logging, java.transaction.xa y java.xml

De manera similar, , de manera similar, importa los 54 paquetes pertenecientes a .importmodulejava.base;java.base

import module java . base ;/** * La importación del módulo java.base nos permite evitar la importación manual de la mayoría de las clases . * Se utilizan las siguientes clases (fuera de java.lang): * java.text.MessageFormat * java.util.Date * java.util.List * java.util.concurrent.ThreadLocalRandom */ public class Example { public static void main ( String [] args ) { List < String > colours = List . of ( "Red" , "Orange" , "Yellow" , "Green" , "Blue" , ​​"Indigo" , "Violet" ); IO . println ( MessageFormat . format ( "My favorite colour is {0} and today is {1,date,long}" , colours . get ( ThreadLocalRandom . current (). nextInt ( colours . size ())), new Date () )); } }

Operadores

Los operadores en Java son similares a los de C++ . Sin embargo, Java no admite deleteoperadores de tipo `signo` debido a los mecanismos de recolección de basura , ni operaciones con punteros>>> . Otra diferencia radica en que Java cuenta con un operador de desplazamiento a la derecha sin signo (`signo` ), mientras que el signo del operador de desplazamiento a la derecha en C depende del tipo de dato. Los operadores en Java no se pueden sobrecargar . El único operador sobrecargado es operator+el de concatenación de cadenas.

Estructuras de control

Declaraciones condicionales

ifdeclaración

Las sentencias if en Java son similares a las de C y utilizan la misma sintaxis:

if ( i == 3 ) { hacerAlgo (); }

ifLa instrucción puede incluir elseun bloque opcional, en cuyo caso se convierte en una instrucción if-then-else:

if ( i == 3 ) { hacerAlgo (); } else { hacerAlgoMás (); }

Al igual que en C, la construcción else-if no implica ninguna palabra clave especial, se forma como una secuencia de sentencias if-then-else separadas:

if ( i == 3 ) { hacerAlgo (); } else if ( i == 2 ) { hacerAlgoMás (); } else { hacerAlgoDiferente (); }

Además, se puede usar el operador ?: en lugar de una simple sentencia if, por ejemplo

int a = 1 ; int b = 2 ; int minVal = ( a < b ) ? a : b ;

switchdeclaración

Las sentencias switch en Java pueden usar tipos de datos primitivos byte, short, char, y int(no long) o sus tipos envoltorio correspondientes. A partir de J2SE 5.0, es posible usar tipos enum . A partir de Java SE 7, es posible usar cadenas. [ 7 ] Otros tipos de referencia no se pueden usar en switchsentencias.

Los valores posibles se indican mediante caseetiquetas. En Java, estas etiquetas solo pueden contener constantes (incluidas las constantes de enumeración y las constantes de cadena). La ejecución comenzará después de la etiqueta que corresponda a la expresión entre corchetes. Opcionalmente, defaultpuede incluirse una etiqueta para indicar que el código que la sigue se ejecutará si ninguna de las etiquetas de caso corresponde a la expresión.

El código de cada etiqueta finaliza con la breakpalabra clave. Es posible omitirla, lo que provoca que la ejecución continúe con la siguiente etiqueta; sin embargo, normalmente se mostrará una advertencia durante la compilación.

switch ( ch ) { case 'A' : hacerAlgo (); // Se activa si ch == 'A' break ; case 'B' : case 'C' : hacerAlgoMás (); // Se activa si ch == 'B' o ch == 'C' break ; default : hacerAlgoDiferente (); // Se activa en cualquier otro caso break ; }
switchexpresiones

Desde Java 14 es posible utilizar expresiones switch, que emplean la nueva sintaxis de flecha:

enum Resultado { GRANDE , BIEN , // más valores de enum }Resultado resultado = switch ( ch ) { case 'A' -> Resultado . GREAT ; case 'B' , 'C' -> Resultado . FINE ; default -> throw new Exception (); };

Alternativamente, existe la posibilidad de expresar lo mismo con la yieldinstrucción, aunque se recomienda preferir la sintaxis de flecha porque evita el problema de las caídas accidentales.

Resultado resultado = switch ( ch ) { case ' A' : yield Resultado . GREAT ; case 'B' : case 'C' : yield Resultado . FINE ; default : throw new Exception (); };

Declaraciones de iteración

Las sentencias de iteración son sentencias que se ejecutan repetidamente cuando una condición determinada se evalúa como verdadera. Desde J2SE 5.0 , Java tiene cuatro formas de dichas sentencias. La condición debe ser de tipo booleano java.lang.Boolean. Java no convierte implícitamente enteros ni tipos de clase a valores booleanos.

Por ejemplo, el siguiente código es válido en C, pero produce un error de compilación en Java.

mientras ( 1 ) { hacerAlgo (); }

whilebucle

En el whilebucle, la prueba se realiza antes de cada iteración.

mientras ( i < 10 ) { hacerAlgo (); }

do- whilebucle

En el bucle do- while, la prueba se realiza después de cada iteración. Por lo tanto, el código siempre se ejecuta al menos una vez.

// doSomething() se llama al menos una vez do { doSomething (); } while ( i < 10 );

forbucle

forEn Java, los bucles incluyen un inicializador, una condición y una expresión de contador. Es posible incluir varias expresiones del mismo tipo utilizando la coma como delimitador (excepto en la condición). Sin embargo, a diferencia de C, la coma es solo un delimitador y no un operador.

for ( int i = 0 ; i < 10 ; i ++ ) { hacerAlgo (); } // Un bucle más complejo usando dos variables for ( int i = 0 , j = 9 ; i < 10 ; i ++ , j -= 3 ) { hacerAlgo (); }

Al igual que en C, las tres expresiones son opcionales. El siguiente bucle nunca termina:

para (;;) { hacerAlgo (); }

bucle foreach

Los bucles `foreach` están disponibles desde J2SE 5.0 . Este tipo de bucle utiliza iteradores integrados sobre matrices y colecciones para devolver cada elemento de la colección dada. Cada elemento se devuelve y es accesible dentro del contexto del bloque de código. Cuando se ejecuta el bloque, se devuelve el siguiente elemento hasta que no queden más. Este bucle `for` de Java se incorporó posteriormente a C++11 . A diferencia de C# , este tipo de bucle no utiliza una palabra clave especial, sino una notación diferente.

para ( int i : intArray ) { hacerAlgo ( i ); }

Sentencias de salto

Etiquetas

Las etiquetas se asignan a puntos en el código utilizados por breaklas continuesentencias. Si bien gotoes una palabra clave reservada en Java, no se puede usar para saltar a puntos específicos en el código (de hecho, no tiene ninguna utilidad).

inicio : algúnMétodo ();

breakdeclaración

La breakinstrucción sale del bucle o switchinstrucción más cercana. La ejecución continúa en la instrucción posterior a la instrucción que finalizó, si la hay.

for ( int i = 0 ; i < 10 ; i ++ ) { while ( true ) { break ; } // Se detendrá en este punto }

Es posible salir del bucle exterior utilizando etiquetas:

exterior : para ( int i = 0 ; i < 10 ; i ++ ) { mientras ( verdadero ) { romper exterior ; } } // Se detendrá en este punto

continuedeclaración

La continueinstrucción interrumpe la iteración actual de la instrucción de control actual y comienza la siguiente iteración. El siguiente whilebucle en el código a continuación lee caracteres llamando a getChar(), omitiendo las instrucciones en el cuerpo del bucle si los caracteres son espacios:

int ch ; while ( ch == getChar ()) { if ( ch == ' ' ) { continue ; // Salta el resto del bucle while }// Resto del bucle while, no se alcanzará si ch == ' ' doSomething (); }

Las etiquetas se pueden especificar en continuedeclaraciones y breakdeclaraciones:

externo : para ( String str : stringsArr ) { char [] strChars = str.toCharArray ( ) ; para ( char ch : strChars ) { if ( ch == ' ' ) { /* Continúa el ciclo externo y se recupera la siguiente cadena de stringsArr *  / continue outer ; } doSomething ( ch ); } }

returndeclaración

La returninstrucción se utiliza para finalizar la ejecución del método y devolver un valor. El valor devuelto por el método se escribe después de la returnpalabra clave. Si el método devuelve algo distinto de void, debe utilizar la returninstrucción para devolver algún valor.

void doSomething ( boolean streamClosed ) { // Si streamClosed es verdadero, la ejecución se detiene if ( streamClosed ) { return ; } readFromStream (); }int calcularSuma ( int a , int b ) { int resultado = a + b ; return resultado ; }

returnLa instrucción finaliza la ejecución inmediatamente, excepto en un caso: si la instrucción se encuentra dentro de un trybloque y se complementa con un finally, el control se pasa al finallybloque.

void doSomething ( boolean streamClosed ) { try { if ( streamClosed ) { return ; } readFromStream (); } finally { // Se llamará al final incluso si readFromStream() no se llamó freeResources (); } }

Instrucciones para el manejo de excepciones

try- catch- finallydeclaraciones

Las excepciones se gestionan dentro de los bloques try- catch.

try { // Instrucciones que pueden lanzar excepciones methodThrowingExceptions (); } catch ( Exception e ) { // Excepción capturada y manejada aquí reportException ( e ); } finally { // Instrucciones que siempre se ejecutan después de los bloques try/catch freeResources (); }

Las instrucciones dentro del trybloque se ejecutan y, si alguna de ellas genera una excepción, la ejecución del bloque se interrumpe y la excepción se gestiona catch. Puede haber varios catchbloques; en ese caso, se ejecuta el primer bloque que contenga una variable de excepción cuyo tipo coincida con el tipo de la excepción generada.

Java SE 7 también introdujo cláusulas multicatch, además de las cláusulas unicatch. Este tipo de cláusulas catch permite a Java gestionar diferentes tipos de excepciones en un único bloque, siempre que no sean subclases entre sí. Si bien se asemeja a un tipo de unión , en realidad es el supertipo común más específico de las alternativas.

import java.net.HttpRetryException ; import java.net.http.HttpTimeoutException ;void makeRequest () throws HttpRetryException , HttpTimeoutException { // ... }void main ( String [] args ) { try { methodThrowingExceptions (); } catch ( HttpRetryException | HttpTimeoutException e ) { // Tanto HttpRetryException como HttpTimeoutException serán capturadas y manejadas aquí // Esto, de hecho, se reduce a simplemente java.io.IOException. reportException ( e ); } }

Si ningún bloque coincide con el tipo de la excepción lanzada, se interrumpe catchla ejecución del bloque (o método) externo que contiene la instrucción try- y la excepción se propaga hacia arriba, fuera del bloque (o método) contenedor. La excepción se propaga hacia arriba a través de la pila de llamadas hasta que se encuentra un bloque coincidente dentro de uno de los métodos activos. Si la excepción se propaga hasta el método superior sin que se encuentre un bloque coincidente , se escribe una descripción textual de la excepción en la salida estándar.catchcatchmaincatch

Las instrucciones dentro del finallybloque se ejecutan siempre después de los bloques try`and` catch, independientemente de si se produjo una excepción o incluso si returnse llegó a una instrucción. Estos bloques son útiles para proporcionar código de limpieza que se ejecuta siempre con seguridad.

Los bloques catchy finallyson opcionales, pero al menos uno u otro debe estar presente después del trybloque.

try-declaraciones con recursos

tryLas sentencias -with-resources son un tipo especial de sentencias try- catch- finallyintroducidas como una implementación del patrón dispose en Java SE 7. En una trysentencia -with-resources, la palabra clave va seguida de la inicialización de uno o más recursos que se liberan automáticamente cuando finaliza tryla ejecución del bloque. Los recursos deben implementar . Las sentencias -with-resources no requieren un bloque o a diferencia de las sentencias - - normales .tryjava.lang.AutoCloseabletrycatchfinallytrycatchfinally

import java.beans.XMLEncoder ; import java.io.FileOutputStream ; import java.io.IOException ; import java.util.logging.Level ; import java.util.logging.Logger ;try ( FileOutputStream fos = new FileOutputStream ( "filename" ); XMLEncoder xEnc = new XMLEncoder ( fos )) { xEnc . writeObject ( object ); } catch ( IOException e ) { Logger . getLogger ( Serializer . class . getName ()). log ( Level . SEVERE , null , e ); }

Desde Java 9 es posible utilizar variables ya declaradas:

import java.beans.XMLEncoder ; import java.io.FileOutputStream ; import java.io.IOException ; import java.util.logging.Level ; import java.util.logging.Logger ;FileOutputStream fos = new FileOutputStream ( " filename " ) ; XMLEncoder xEnc = new XMLEncoder ( fos ) ; try ( fos ; xEnc ) { xEnc.writeObject ( object ) ; } catch ( IOException e ) { Logger.getLogger ( Serializer.class.getName ( ) ) . log ( Level.SEVERE , null , e ) ; }

throwdeclaración

Esta throwinstrucción se utiliza para lanzar una excepción y finalizar la ejecución del bloque o método. La instancia de la excepción lanzada se escribe después de la throwinstrucción.

void methodThrowingExceptions ( Object obj ) { if ( obj == null ) { // Lanza una excepción de tipo NullPointerException throw new NullPointerException (); } // No se llamará si el objeto es nulo doSomethingWithObject ( obj ); }

assertdeclaración

assertLas declaraciones están disponibles desde J2SE 1.4 . Este tipo de declaraciones se utilizan para realizar aserciones en el código fuente, las cuales pueden activarse y desactivarse durante la ejecución para clases o paquetes específicos. Para declarar una aserción, assertse utiliza la palabra clave seguida de una expresión condicional. Si se evalúa como verdadera falseal ejecutar la declaración, se produce un error java.lang.AssertionError. Esta declaración puede incluir dos puntos seguidos de otra expresión, que actuará como mensaje de detalle del error.

// Si n es igual a 0, se lanza java.lang.AssertionError assert n != 0 ; /* Si n es igual a 0, se lanzará java.lang.AssertionError con el mensaje después de los dos puntos */ assert n != 0 : "n era igual a cero" ;

Enhebrado

Java ofrece hilos de ejecución a través de la clase java.lang.Thread. Esta clase toma un java.lang.Runnable(como una lambda), y se inician usando el start()método y se pausan/bloquean usando sleep(). Para notificar a otros hilos, wait()libera el bloqueo del objeto y pausa el hilo hasta que otro hilo llame a notify()o notifyAll().

Thread t = new Thread (() -> { System . out . println ( "Running" ); });t.start ( ) ;

Los hilos demonio son hilos que se ejecutan en segundo plano, por ejemplo, los recolectores de basura o los hilos de monitorización. La JVM finaliza cuando solo quedan hilos demonio.

Se puede acceder al hilo actual con este currentThread()método.

Históricamente, java.lang.ThreadDeathse lanzaba un error al llamar stop()a un hilo, sin embargo, stop()se eliminó desde Java 20 [ 8 ] , y este error ya no se lanza. [ 9 ] La forma idiomática de detener un hilo ahora es con el interrupt()método, donde se permite que un hilo termine de forma elegante en lugar de detenerse inmediatamente sin limpieza.

control de concurrencia de subprocesos

Java cuenta con herramientas integradas para la programación multihilo. Para la sincronizaciónsynchronized de hilos , se incluye una instrucción en el lenguaje Java.

Para que un bloque de código sea sincronizado, se antepone la synchronizedpalabra clave `sync` seguida del objeto de bloqueo entre corchetes. Cuando el hilo en ejecución alcanza el bloque sincronizado, adquiere un bloqueo de exclusión mutua , ejecuta el bloque y luego libera el bloqueo. Ningún otro hilo puede acceder a este bloque hasta que se libere el bloqueo. Se puede utilizar cualquier tipo de referencia no nula como bloqueo.

/* Adquiere un bloqueo sobre someObject. Debe ser de tipo referencia y no debe ser nulo */ synchronized ( someObject ) { // Instrucciones sincronizadas }

Características de concurrencia

Java ofrece la biblioteca java.util.concurrent, así como características de atomicidadjava.util.concurrent.atomic ( ) y características de bloqueo ( java.util.concurrent.locks).

En lugar de , se proporciona java.lang.Threadotra clase para computación asíncrona [ 10 ] ; esto puede verse como similar a un en C# , aunque Java carece de corrutinas explícitas como las de C#.java.util.concurrent.CompletableFuture<T>System.Threading.Tasks.Task<T>

Como parte del Proyecto Loom en Java 21, se introdujeron los hilos virtuales , que permiten código síncrono, bloqueante y normal mientras escalan a millones de tareas. [ 11 ] [ 12 ] El hilo clásico de Java se llama "hilo de plataforma", que corresponde aproximadamente a un hilo del sistema operativo, pero los hilos virtuales de Java son ligeros y administrados por la JVM, programados en un pequeño grupo de hilos reales del sistema operativo. Los hilos virtuales siguen siendo java.lang.Thread, pero se crean con métodos de fábrica como ofVirtual(). Aunque el código sigue siendo síncrono y bloqueante, los hilos físicos del sistema operativo son asíncronos; al detenerse para esperar, la JVM desvincula el código del hilo físico, ejecutando otras tareas hasta que el sistema operativo envía una notificación de evento asíncrono para retomar el código donde lo dejó para terminar.

Tipos primitivos

Los tipos primitivos en Java incluyen tipos enteros, números de punto flotante, unidades de código UTF-16char y un tipo booleano. A diferencia de C++ y C#, no hay tipos sin signo en Java excepto el tipo, que se usa para representar unidades de código UTF-16. La falta de tipos sin signo se compensa con la introducción de la operación de desplazamiento a la derecha sin signo ( >>>), que no está presente en C++, métodos como .toUnsignedInt(). Sin embargo, se han formulado críticas sobre la falta de compatibilidad con C y C++ que esto causa. [ 13 ]

nullno tiene tipo, ni primitivo ni de clase. Cualquier tipo de objeto puede almacenar null.

La clase Voidse utiliza para mantener una referencia al Classobjeto que representa la palabra clave void. No se puede instanciar, ya que voidno puede ser del tipo de ningún objeto. Por ejemplo, CompletableFuture<Void>significa que java.util.concurrent.CompletableFuture<T>realiza una tarea que no devuelve un valor.

La clase java.lang.Numberrepresenta todos los valores numéricos que se pueden convertir a byte, double, float, int, long, y short.

charNo necesariamente corresponde a un solo carácter. Puede representar parte de un par sustituto , en cuyo caso el punto de código Unicode se representa mediante una secuencia de dos charvalores.

Las clases contenedoras contienen métodos estáticos adicionales para operaciones en sus tipos primitivos, como Integer.parseInt(), así como constantes estáticas que describen la información del tipo. La java.lang.Characterpropia clase contenedora contiene tipos anidados adicionales:

  • java.lang.Character.Subset, que representa un subconjunto del conjunto de caracteres Unicode
  • java.lang.Character.UnicodeBlock, que contiene constantes que representan un bloque Unicode
  • java.lang.Character.UnicodeScript, una enumeración de escrituras en Unicode

Empaquetado y desempaquetado

Esta característica del lenguaje se introdujo en J2SE 5.0 . El empaquetado (boxing) consiste en convertir un valor de un tipo primitivo en un valor de un tipo de referencia correspondiente, que actúa como contenedor para dicho tipo primitivo. El desempaquetado (unboxing) es la operación inversa, que consiste en convertir un valor de un tipo de referencia (previamente empaquetado) en un valor de un tipo primitivo correspondiente. Ninguna de las dos operaciones requiere una conversión explícita.

Ejemplo:

int foo = 42 ; // Tipo primitivo Integer bar = foo ; /* foo se convierte a bar, bar es de tipo Integer,  que sirve como contenedor para int */ int foo2 = bar ; // Se descomprime de nuevo a tipo primitivo

Tipos de referencia

Los tipos de referencia incluyen tipos de clase, tipos de interfaz y tipos de matriz. Al llamar al constructor, se crea un objeto en el montón y se asigna una referencia a la variable. Cuando una variable de un objeto queda fuera de su ámbito, la referencia se rompe y, cuando ya no quedan referencias, el objeto se marca como basura. El recolector de basura lo recoge y lo destruye tiempo después.

Una variable de referencia es nullaquella que no hace referencia a ningún objeto.

Matrices

En Java, los arrays se crean en tiempo de ejecución, al igual que las instancias de clase. La longitud del array se define al crearlo y no se puede modificar.

int [] numbers = new int [ 5 ] ; numbers [ 0 ] = 2 ; numbers [ 1 ] = 5 ; int x = numbers [ 0 ] ; int len ​​= numbers.length ; // len = 5

En Java también se aceptan las declaraciones de matrices al estilo de C , pero no se utilizan habitualmente.

int arr [] = nuevo int [ 10 ] ;

En la JVM, los arreglos se representan con un corchete izquierdo de apertura para cada dimensión del arreglo, con las siguientes letras:

  • B , parabyte
  • C , parachar
  • D , paradouble
  • F , parafloat
  • Yo , porint
  • J , paralong
  • S , parashort
  • Z , paraboolean
  • L , para cualquier tipo de referencia (no primitivo), seguido del nombre de clase completamente calificado y luego un punto y coma de cierre [ a ].

Por ejemplo, int[][]se convierte en [[Iy String[]se convierte en [Ljava.lang.String;.

voidno existe como un tipo en la memoria, pero se representa internamente con la letra V.

Los arreglos están limitados a un máximo de 255 dimensiones. La longitud máxima teórica de un arreglo es2311{\displaystyle 2^{31}-1}(2.147.483.647, equivalente a Integer.MAX_VALUE), porque las matrices solo se pueden direccionar con índices de 32 bits, pero este límite suele depender del sistema y de la máquina virtual.

Las clases java.util.Arraysy java.lang.reflect.Arrayse proporcionan para la manipulación de matrices y operaciones reflectivas en matrices, respectivamente. [ 14 ] [ 15 ]

Los arreglos parecen tener un campo length, pero no es un campo real; es una propiedad integrada evaluada de forma nativa por la arraylengthinstrucción de código de bytes . [ 1 ] Los arreglos no tienen miembros, métodos ni constructores declarados (aparte de los heredados de java.lang.Object).

Los arrays heredan la clase java.lang.Objecte implementan las interfaces java.lang.Cloneabley java.io.Serializable. Los arrays son implícitamente final, y no pueden heredar de . A pesar de ser clases, no hay un archivo .class para los arrays porque se crean directamente en la memoria durante la ejecución, no tienen una representación física de bytecode y no son cargados por un cargador de clases .

Dado que los arrays son tipos de referencia, pueden utilizarse directamente en parámetros de tipos genéricos.

import java.util.ArrayList ; import java.util.List ;Lista < int []> frecuencias = nuevo ArrayList <> ();

Inicializadores

// Sintaxis larga int [] numbers = new int [] { 20 , 1 , 42 , 15 , 34 }; // Sintaxis corta int [] numbers2 = { 20 , 1 , 42 , 15 , 34 };

Matrices multidimensionales

En Java, los arreglos multidimensionales se representan como arreglos de arreglos. Técnicamente, se representan mediante arreglos de referencias a otros arreglos.

int [][] números = nuevo int [ 3 ][ 3 ] ; números [ 1 ][ 2 ] = 2 ;int [][] numbers2 = {{ 2 , 3 , 2 }, { 1 , 2 , 6 }, { 2 , 4 , 5 }};

Debido a la naturaleza de los arreglos multidimensionales, los subarreglos pueden variar en longitud, por lo que los arreglos multidimensionales no están obligados a ser rectangulares a diferencia de C:

int [][] numbers = new int [ 2 ][] ; // Inicialización solo de la primera dimensiónnúmeros [ 0 ] = nuevo entero [ 3 ] ; números [ 1 ] = nuevo entero [ 2 ] ;

Clases

Las clases son fundamentales en un lenguaje orientado a objetos como Java. Contienen miembros que almacenan y manipulan datos. Las clases se dividen en clases de nivel superior y clases anidadas . Las clases anidadas son clases ubicadas dentro de otra clase que pueden acceder a los miembros privados de la clase contenedora. Las clases anidadas incluyen clases miembro (que pueden definirse con el modificador static para anidamiento simple o sin él para clases internas), clases locales y clases anónimas .

Declaración

Instanciación

Los miembros no estáticos de una clase definen los tipos de las variables de instancia y los métodos, que están relacionados con los objetos creados a partir de esa clase. Para crear estos objetos, la clase debe instanciarse utilizando el newoperador `instance()` y llamando al constructor de la clase.

Foo foo = nuevo Foo ();

Acceso a los miembros

Se accede a los miembros de las instancias y de las clases estáticas mediante el .operador (punto).

Acceso a un miembro de instancia Se puede acceder a los miembros de instancia a través del nombre de una variable.

String foo = "Hola" ; String bar = foo . toUpperCase ();

Acceso a un miembro estático de una clase Los miembros estáticos se acceden utilizando el nombre de la clase o cualquier otro tipo. Esto no requiere la creación de una instancia de la clase. Los miembros estáticos se declaran utilizando el staticmodificador.

clase pública Foo { public static void hacerAlgo () { // ... } }// Llamando al método estático Foo.doSomething ( );

Modificadores

Los modificadores son palabras clave que se utilizan para modificar las declaraciones de tipos y miembros de tipo. Cabe destacar que existe un subgrupo que contiene los modificadores de acceso.

  • abstract- Especifica que una clase solo sirve como clase base y no puede ser instanciada.
  • static- Se utiliza únicamente para clases miembro; especifica que la clase miembro no pertenece a una instancia específica de la clase contenedora.
  • final- Las clases marcadas como finalno se pueden extender y no pueden tener subclases.
  • strictfp- Especifica que todas las operaciones de punto flotante deben realizarse de conformidad con la norma IEEE 754 y prohíbe el uso de precisión mejorada para almacenar resultados intermedios.
Clase abstracta

Por defecto, todos los métodos de todas las clases son concretos, a menos que se utilice la palabra clave `abstract`. Una clase abstracta puede incluir métodos abstractos, que no tienen implementación. Por defecto, todos los métodos de todas las interfaces son abstractos, a menos que se utilice la palabra clave `default`. Esta palabra clave permite especificar un método concreto en una interfaz.

// Por defecto, todos los métodos en todas las clases son concretos, a menos que se utilice la palabra clave abstract. abstract class Demo { // Una clase abstracta puede incluir métodos abstractos, que no tienen implementación. public abstract int sum ( int x , int y );// Una clase abstracta también puede incluir métodos concretos. public int product ( int x , int y ) { return x * y ; } }// Por defecto, todos los métodos en todas las interfaces son abstractos, a menos que se utilice la palabra clave default. interface DemoInterface { int getLength (); // La palabra clave abstract se puede usar aquí, aunque es completamente inútil // La palabra clave default se puede usar en este contexto para especificar un método concreto en una interfaz default int product ( int x , int y ) { return x * y ; } }

Clase final

Una clase final no puede tener subclases. Dado que esto puede brindar beneficios de seguridad y eficiencia, muchas de las clases de la biblioteca estándar de Java son finales, como java.lang.Systemy java.lang.String.

Ejemplo:

clase final Final { // ... }// Clase prohibida Derived extiende Final { // ... }
Modificadores de acceso

Los modificadores de acceso , o modificadores de herencia , establecen la accesibilidad de las clases, los métodos y otros miembros. Los miembros marcados como publicaccesibles pueden ser accedidos desde cualquier lugar. Si una clase o uno de sus miembros no tiene modificadores, se asume el acceso predeterminado.

clase pública Foo { int baz () { return 0 ; }clase privada Bar {} }

La siguiente tabla muestra si el código dentro de una clase tiene acceso a la clase o al método dependiendo de la ubicación de la clase que accede y del modificador para la clase o miembro de clase al que se accede:

Esta imagen describe el alcance de los miembros de la clase dentro de las clases y los paquetes.

Constructores e inicializadores

Un constructor es un bloque especial similar a un método que se llama cuando se inicializa un objeto. Su propósito es inicializar los miembros del objeto. Las principales diferencias entre los constructores y los métodos ordinarios son que los constructores se llaman solo cuando se crea una instancia de la clase y nunca devuelven nada. Los constructores se declaran de forma similar a un método, pero se nombran según la clase y no se especifica ningún tipo de retorno; para una clase X, un constructor es X(...); en particular, un constructor predeterminado no tiene parámetros, X().

clase Foo { String str ;// Constructor sin argumentos Foo () {}// Constructor con un argumento Foo ( String str ) { this . str = str ; } }

Un constructor parece asemejarse a un método, pero en realidad son bloques de inicialización de instancia y no pueden ser invocados directamente por una llamada a método (a diferencia de C++ , donde los constructores se consideran funciones miembro especiales ). [ 16 ] A pesar de esto, en el bytecode de la JVM , un constructor aparece con el nombre <init>y se denomina "método de inicialización de instancia", y están marcados void(y, por lo tanto, no colocan ningún objeto resultante en la pila de operandos de la JVM al finalizar la ejecución). [ 17 ]

Los inicializadores son bloques de código que se ejecutan cuando se crea una clase o una instancia de una clase. Hay dos tipos de inicializadores: inicializadores estáticos e inicializadores de instancia .

Los inicializadores estáticos inicializan los campos estáticos cuando se crea la clase. Se declaran utilizando la staticpalabra clave:

clase Foo { static { // Inicialización } }

Una clase se crea solo una vez. Por lo tanto, los inicializadores estáticos no se llaman más de una vez. En cambio, los inicializadores de instancia se llaman automáticamente antes de la llamada a un constructor cada vez que se crea una instancia de la clase. A diferencia de los constructores, los inicializadores de instancia no pueden aceptar argumentos y, por lo general, no pueden lanzar excepciones comprobadas (salvo en algunos casos especiales). Los inicializadores de instancia se declaran en un bloque sin palabras clave.

clase Foo { { // Inicialización } }

Dado que Java se basa en un mecanismo de recolección de basura, no existen destructores~X() ni deleteoperadores. Sin embargo, cada objeto tiene un finalize()método que se llama antes de la recolección de basura, el cual puede ser sobrescrito para implementar la finalización. No obstante, no hay garantías de qué hilo invocará el finalize()método para un objeto dado, ni siquiera de que se invoque en absoluto. Los finalizadores no son deterministas (a diferencia de los destructores en C++), y liberar recursos críticos en una finalize()llamada puede conducir a un comportamiento impredecible. [ 18 ] Otra clase, java.lang.ref.Cleanerintroducida en Java 9, se proporciona adicionalmente para redes de seguridad de nivel inferior para recuperar recursos que no son de memoria, como punteros nativos, descriptores de archivos o sockets. [ 19 ]

Métodos

En Java, todas las instrucciones deben estar dentro de métodos . Los métodos son similares a las funciones, excepto que pertenecen a clases. Un método tiene un valor de retorno, un nombre y, por lo general, algunos parámetros que se inicializan al ser llamado con ciertos argumentos. Al igual que en C++, los métodos que no devuelven nada tienen un tipo de retorno declarado como `delete` void. A diferencia de C++, en Java los métodos no pueden tener valores de argumento predeterminados y, en su lugar, suelen sobrecargarse.

clase Foo { int bar ( int a , int b ) { return ( a * 2 ) + b ; }// Método sobrecargado con el mismo nombre pero con un conjunto diferente de argumentos int bar ( int a ) { return a * 2 ; } }

Se llama a un método utilizando .una notación sobre un objeto o, en el caso de un método estático, también sobre el nombre de una clase.

Foo foo = new Foo (); int result = foo . bar ( 7 , 2 ); // Se llama a un método no estático en fooint resultadoFinal = Math.abs ( resultado ); // Llamada a método estático

La throwspalabra clave indica que un método lanza una excepción. Todas las excepciones comprobadas deben aparecer en una lista separada por comas.

import java.io.File ; import java.io.IOException ; import java.util.zip.DataFormatException ;// Indica que se pueden lanzar IOException y DataFormatException void operateOnFile ( File f ) throws IOException , DataFormatException { // ... }
Modificadores
  • abstract- Los métodos abstractos solo pueden estar presentes en clases abstractas ; dichos métodos no tienen cuerpo y deben ser sobrescritos en una subclase, a menos que esta sea abstracta en sí misma.
  • static- Hace que el método sea estático y accesible sin necesidad de crear una instancia de la clase. Sin embargo, los métodos estáticos no pueden acceder a los miembros no estáticos de la misma clase.
  • final- Declara que el método no puede ser sobrescrito en una subclase.
  • native- Indica que este método se implementa mediante JNI en código dependiente de la plataforma. La implementación real se realiza fuera del código Java, y dichos métodos no tienen cuerpo.
  • strictfp- Declara conformidad estricta con la norma IEEE 754 para la realización de operaciones de punto flotante. Actualmente obsoleto.
  • synchronized- Declara que un hilo que ejecute este método debe adquirir un monitor. Para synchronizedlos métodos, el monitor es la instancia de la clase o java.lang.Classsi el método es estático.
  • Modificadores de acceso: idénticos a los que se utilizan con las clases.
Métodos finales

Un método final no puede ser sobrescrito ni ocultado por subclases. [ 20 ] Esto se utiliza para evitar un comportamiento inesperado cuando una subclase modifica un método que puede ser crucial para la función o la consistencia de la clase. [ 21 ]

Ejemplo:

public class Base { public void foo () { // ... }public final void bar () { // ... }public static void baz () { // ... }public static final void qux () { // ... } }public class Derived extends Base { // OK, sobrescribiendo Base::foo() @Override public void foo () { // ... }// prohibido @Override public void bar () { // ... }// OK, sobrescribiendo Base::baz() @Override public static void baz () { // ... }// prohibido @Override public static void qux () { // ... } }

Una idea errónea común es que declarar un método como `inline` finalmejora la eficiencia al permitir que el compilador lo inserte directamente dondequiera que se llame (véase expansión en línea ). Dado que el método se carga en tiempo de ejecución , los compiladores no pueden hacer esto. Solo el entorno de ejecución y el compilador JIT saben exactamente qué clases se han cargado, por lo que solo ellos pueden tomar decisiones sobre cuándo realizar la expansión en línea, independientemente de si el método es final o no. [ 22 ]

Los compiladores de código máquina que generan código máquina específico de la plataforma y ejecutable directamente son una excepción. Al usar el enlace estático , el compilador puede asumir con seguridad que los métodos y variables computables en tiempo de compilación pueden insertarse en línea.

Parámetros variádicos

Esta característica del lenguaje se introdujo en J2SE 5.0 . El último argumento del método puede declararse como un parámetro de aridad variable , en cuyo caso el método se convierte en un método de aridad variable (a diferencia de los métodos de aridad fija) o simplemente en un método variádico . Esto permite pasar al método como parámetros un número variable de valores (posiblemente cero), del tipo declarado. Estos valores estarán disponibles dentro del método como un array.

// numbers representa argumentos variables void printReport ( String header , int ... numbers ) { // numbers aparece como int[] System . out . println ( header ); for ( int num : numbers ) { System . out . println ( num ); } }// Llamando al método varargs printReport ( "Datos del informe" , 74 , 83 , 25 , 96 );

Campos

Los campos, o variables de clase , pueden declararse dentro del cuerpo de la clase para almacenar datos.

clase Foo { doble barra ; }

Los campos se pueden inicializar directamente al declararlos.

clase Foo { doble barra = 2.3 ; }
Modificadores
  • static- Convierte el campo en un miembro estático.
  • final- Permite que el campo se inicialice solo una vez en un constructor o dentro de un bloque de inicialización o durante su declaración, lo que ocurra primero.
  • transient- Indica que este campo no se almacenará durante la serialización .
  • volatile- Si se declara un campo volatile, se garantiza que todos los hilos vean un valor consistente para la variable.

Herencia

En Java, las clases solo pueden heredar de una única clase. Una clase puede derivarse de cualquier clase que no esté marcada como ` <inherit> final`. La herencia se declara mediante la extendspalabra clave `<herit>`. Una clase puede referenciarse a sí misma mediante la thispalabra clave `<herit>` y a su superclase directa mediante la superpalabra clave `<herit>`.

clase Foo {}clase Foobar extiende Foo {}

Si una clase no especifica su superclase, hereda implícitamente de java.lang.Objectla clase base. Por lo tanto, todas las clases en Java son subclases de Objectla clase base.

Si la superclase no tiene un constructor sin parámetros, la subclase debe especificar en sus constructores qué constructor de la superclase utilizar. Por ejemplo:

clase Foo { público Foo ( int n ) { // Hacer algo con n } }clase Foobar extiende Foo { private int number ; // La superclase no tiene constructor sin parámetros // por lo que tenemos que especificar qué constructor de nuestra superclase usar y cómopublic Foobar ( int number ) { super ( number ); this . number = number ; } }
Métodos de anulación

A diferencia de C++, todos finallos métodos que no son de clase en Java son virtuales y pueden ser sobrescritos por las clases que heredan de ellos.

clase Operación { public int hacerAlgo () { return 0 ; } }clase NewOperation extiende Operation { @Override public int hacerAlgo () { return 1 ; } }
Clases abstractas

Una clase abstracta es una clase que está incompleta, o debe considerarse incompleta, y no puede ser instanciada (directamente). [ 23 ]

Una clase Xtiene métodos abstractos si se cumple alguna de las siguientes condiciones:

  • XContiene explícitamente una declaración de un método abstracto.
  • Cualquiera de Xlas superclases de en la cadena de herencia tiene un método abstracto y Xno declara ni hereda un método que lo implemente.
  • Una superinterfaz directa Xdeclara o hereda un método (que por lo tanto es necesariamente abstracto) y Xno declara ni hereda un método que lo implemente.
  • Se puede instanciar una subclase de una clase abstracta que no sea abstracta en sí misma, lo que da como resultado la ejecución de un constructor para la clase abstracta y, por lo tanto, la ejecución de los inicializadores de campo para las variables de instancia de esa clase.
paquete org.wikipedia.examples ;public class MyAbstractClass { private static final String hola ;static { System.out.printf ( " % s : tiempo de ejecución del bloque estático%n" , MyAbstractClass.class.getName ( ) ) ; hello = String.format ( " hola desde %s" , MyAbstractClass.class.getName ( ) ); }{ System.out.printf ( "%s: tiempo de ejecución del bloque de instancia %n " , MyAbstractClass.class.getName ( ) ) ; }public AbstractClass ( ) { System.out.printf ( " % s : constructor runtime%n " , MyAbstractClass.class.getName ( ) ) ; }public static void hello () { System . out . println ( hello ); } }
paquete org.wikipedia.examples ;clase pública MyCustomClass extiende MyAbstractClass {static { System.out.printf ( "% s : tiempo de ejecución del bloque estático%n" , MyCustomClass.class.getName ( ) ) ; }{ System.out.printf ( "%s: tiempo de ejecución del bloque de instancia %n " , MyCustomClass.class.getName ( ) ) ; }public CustomClass ( ) { System.out.printf ( " % s : constructor runtime%n " , MyCustomClass.class.getName ( ) ) ; }public static void main ( String [] args ) { MyCustomClass nc = new MyCustomClass (); hello (); MyAbstractClass . hello (); // también válido } }

Producción:

org.wikipedia.examples.MyAbstractClass: tiempo de ejecución de bloque estático org.wikipedia.examples.MyCustomClass: tiempo de ejecución de bloque estático org.wikipedia.examples.MyAbstractClass: tiempo de ejecución del bloque de instancia org.wikipedia.examples.MyAbstractClass: tiempo de ejecución del constructor org.wikipedia.examples.MyCustomClass: tiempo de ejecución del bloque de instancia org.wikipedia.examples.MyCustomClass: tiempo de ejecución del constructor Hola desde org.wikipedia.examples.MyAbstractClass 

Enumeraciones

Esta característica del lenguaje se introdujo en J2SE 5.0 . Técnicamente, las enumeraciones son un tipo de clase que contiene constantes de enumeración en su cuerpo. Cada constante de enumeración define una instancia del tipo de enumeración. Las clases de enumeración no se pueden instanciar en ningún otro lugar que no sea la propia clase de enumeración.

enum Temporada { INVIERNO , PRIMAVERA , VERANO , OTOÑO , }

Las constantes de enumeración pueden tener constructores, que se llaman cuando se carga la clase:

public enum Season { WINTER ( "Frío" ), SPRING ( "Más cálido" ), SUMMER ( "Calor" ), AUTUMN ( "Más fresco" );Temporada ( Cadena descripción ) { esta . descripción = descripción ; }Descripción privada final de cadena ;public String getDescription () { return description ; } }

Las enumeraciones pueden tener cuerpos de clase, en cuyo caso se tratan como clases anónimas que extienden la clase enum:

public enum Season { WINTER { @Override String getDescription () { return "frío" } }, SPRING { @Override String getDescription () { return "cálido" ; } }, SUMMER { @Override String getDescription () { return "caliente" ; } }, FALL { @Override String getDescription () { return "fresco" ; } };cadena abstracta obtenerDescripción (); }

Interfaces

Las interfaces son tipos que no contienen campos y generalmente definen varios métodos sin una implementación real. Son útiles para definir un contrato con cualquier número de implementaciones diferentes. Toda interfaz es implícitamente abstracta. Los métodos de interfaz pueden tener un subconjunto de modificadores de acceso que depende de la versión del lenguaje, strictfplo que tiene el mismo efecto que para las clases, y también staticdesde Java SE 8.

interface ActionListener { int ACTION_ADD = 0 ; int ACTION_REMOVE = 1 ; void actionSelected ( int action ); }

Implementación de una interfaz

Una interfaz se implementa mediante una clase utilizando la implementspalabra clave `interface`. Se permite implementar más de una interfaz; en ese caso, se escriben después de implementsla palabra clave `interface` en una lista separada por comas. Una clase que implementa una interfaz debe sobrescribir todos sus métodos; de lo contrario, debe declararse como abstracta.

interfaz RequestListener { int requestReceived (); }class ActionHandler implements ActionListener , RequestListener { public void actionSelected ( int action ) { // ... }public int requestReceived () { // ... } }// Llamada al método definido por la interfaz // ActionHandler puede representarse como RequestListener... RequestListener listener = new ActionHandler (); // ...y por lo tanto se sabe que implementa el método requestReceived() listener . requestReceived ();

Interfaces funcionales y expresiones lambda

Estas características se introdujeron con el lanzamiento de Java SE 8. Una interfaz se convierte automáticamente en una interfaz funcional si define un solo método (excluyendo aquellos que se heredan de java.lang.Object). En este caso, una implementación se puede representar como una expresión lambda en lugar de implementarla en una nueva clase, lo que simplifica enormemente la escritura de código en el estilo funcional . Las interfaces funcionales pueden anotarse opcionalmente con la java.lang.FunctionalInterfaceanotación , que le indicará al compilador que verifique si la interfaz se ajusta a la definición de una interfaz funcional.

// Una interfaz funcional @FunctionalInterface interface Calculation { int calculate ( int someNumber , int someOtherNumber ); }// Un método que acepta esta interfaz como parámetro int runCalculation ( Calculation calculation ) { return calculation . calculate ( 1 , 2 ); }// Usando una expresión lambda para llamar al método runCalculation (( número , otroNúmero ) -> número + otroNúmero );// Código equivalente que utiliza una clase anónima en su lugar runCalculation ( new Calculation () { @Override public int calculate ( int someNumber , int someOtherNumber ) { return someNumber + someOtherNumber ; } })

Los tipos de parámetros de una expresión lambda no tienen que especificarse completamente y pueden inferirse de la interfaz que implementa, pero no hay lugar para especificar explícitamente el tipo de retorno de la expresión lambda (que se infiere). El cuerpo de la expresión lambda puede escribirse sin un bloque body ni una returninstrucción si es solo una expresión. Además, para aquellas interfaces que solo tienen un parámetro en el método, se pueden omitir los paréntesis. [ 24 ]

// Misma llamada que la anterior, pero con tipos completamente especificados y un bloque de cuerpo runCalculation (( int number , int otherNumber ) -> { return number + otherNumber ; });// Una interfaz funcional con un método que tiene un solo parámetro interface StringExtender { String extendString ( String input ); }// Inicializando una variable de este tipo usando una expresión lambda StringExtender extensor = input -> input + " Extended" ;

Las expresiones lambda no pueden especificar throwscláusulas. Las expresiones lambda implementan alguna interfaz funcional de destino (como java.lang.Runnable, etc.). Las expresiones lambda en sí mismas no son expresiones completas de tipos concretos, sino más bien expresiones polimórficas, cuyo tipo está influenciado por el tipo de destino circundante en lugar de estar completamente influenciado por su propio contenido. [ 25 ]

import java.util.function.Function ;// Inválido, ya que las expresiones lambda en sí mismas no tienen un tipo independiente var f = ( int x ) -> x + 1 ; // En su lugar debería ser: Function < Integer , Integer > f = x -> x + 1 ;

Si una lambda implementa una interfaz funcional que extiende java.lang.Serializable, entonces cuando la lambda se serializa, se convierte en un java.lang.invoke.SerializedLambda[ 26 ] , que captura información sobre la lambda misma, llamada su "forma serializada". [ 27 ] Esto es necesario, porque las clases de implementación de lambda son sintéticas y específicas de la JVM, y por lo tanto deben reconstruirse exactamente en otra invocación de la JVM. Al igual que en C++, los nombres de los tipos de lambda son sintéticos y generados por el compilador con detalles de implementación no especificados.

Runnable r = () -> System . out . println ( "Hola" );// Podría imprimir algo como: // class org.wikipedia.examples.Main$$Lambda$1/0x0000000800b00440 System . out . println ( r . getClass ());

Las expresiones lambda no generan archivos de clase , sino que emiten una invokedynamicinstrucción en la JVM, que el entorno de ejecución utiliza para crear una clase de implementación adecuada.

Las expresiones lambda de captura suelen ser objetos distintos, debido a que requieren un estado capturado almacenado; sin embargo, las expresiones lambda que no son de captura pueden ser potencialmente singletons .

Runnable r1 = () -> {}; Runnable r2 = () -> {};System.out.println ( r1 == r2 ) ; // Puede imprimir verdadero o falso; lo determina la JVM

Sin embargo, a diferencia de C++, los tipos lambda no forman parte del modelo de lenguaje; no se puede depender de nada de su clase de tiempo de ejecución, mientras que en C++ el tipo lambda aún se puede extraer decltypepara obtener un tipo de cierre único real; el de una lambda java.lang.Class<?>no se puede referenciar en el código.

Referencias de métodos

Java permite referencias a métodos mediante el operador ::(no está relacionado con el operador de calificación de espacio de nombres de C++ ::). No es necesario usar expresiones lambda cuando ya existe un método con nombre compatible con la interfaz. Este método se puede pasar en lugar de una expresión lambda mediante una referencia a método. Existen varios tipos de referencias a métodos:

El código anterior que realiza llamadas runCalculationpodría reemplazarse con el siguiente utilizando las referencias de método:

ejecutarCálculo ( Entero :: suma );

Tenga en cuenta que en Java, los constructores no se consideran métodos y, por lo tanto, X::Xno existen; una referencia a un constructor se obtiene con X::new.

Herencia

Las interfaces pueden heredar de otras interfaces, al igual que las clases. A diferencia de las clases, pueden heredar de múltiples interfaces. Sin embargo, es posible que varias interfaces tengan un campo con el mismo nombre, en cuyo caso se convierte en un único miembro ambiguo al que no se puede acceder.

/* La clase que implementa esta interfaz debe implementar métodos tanto de ActionListener como de RequestListener */ interface EventListener extends ActionListener , RequestListener { }

Métodos predeterminados

Java SE 8 introdujo métodos predeterminados en las interfaces, lo que permite a los desarrolladores añadir nuevos métodos a las interfaces existentes sin romper la compatibilidad con las clases que ya las implementan. A diferencia de los métodos de interfaz habituales, los métodos predeterminados tienen un cuerpo que se ejecutará si la clase que los implementa no los sobrescribe.

interface StringManipulator { String extendString ( String input ); // Un método que es opcional implementar por defecto String shortenString ( String input ) { return input . substring ( 1 ); } }// Esta es una clase válida a pesar de no implementar todos los métodos. class PartialStringManipulator implements StringManipulator { @Override public String extendString ( String input ) { return String . format ( "%s Extended" , input ); } }

Métodos estáticos

Los métodos estáticos son otra característica del lenguaje introducida en Java SE 8. Se comportan exactamente igual que en las clases.

interface StringUtils { static String shortenByOneSymbol ( String input ) { return input . substring ( 1 ); } }StringUtils.shortenByOneSymbol ( "Test " ) ;

Métodos privados

Los métodos privados se añadieron en la versión Java 9. Una interfaz puede tener un método cuyo cuerpo esté marcado como privado, en cuyo caso no será visible para las clases que hereden de ella. Se puede llamar desde métodos predeterminados para reutilizar el código.

interface Logger { default void logError () { log ( Level . ERROR ); }default void logInfo () { log ( Level . INFO ); }private void log ( Level level ) { SystemLogger . log ( level . id ); } }

Anotaciones

Las anotaciones en Java son una forma de incorporar metadatos al código. Esta característica del lenguaje se introdujo en J2SE 5.0 .

Tipos de anotación

Java cuenta con un conjunto de tipos de anotación predefinidos, pero permite definir otros nuevos. La declaración de un tipo de anotación es un tipo especial de declaración de interfaz. Se declaran de la misma forma que las interfaces, con la diferencia de que la interfacepalabra clave va precedida del @signo `<a>`. Las anotaciones de Java son interfaces y se extienden implícitamente desde ellas java.lang.annotation.Annotation, sin poder extenderse desde ninguna otra cosa. A diferencia de las anotaciones de C++, que pueden ser cualquier objeto, las anotaciones de Java no pueden instanciarse en un objeto real en tiempo de ejecución.

@interface BlockingOperations {}

Las anotaciones pueden tener las mismas declaraciones en el cuerpo que las interfaces comunes; además, se permite incluir enumeraciones y anotaciones. La principal diferencia radica en que las declaraciones de métodos abstractos no deben tener parámetros ni lanzar excepciones. También pueden tener un valor predeterminado, que se declara mediante la defaultpalabra clave después del nombre del método:

@interface BlockingOperations { boolean fileSystemOperations (); boolean networkOperations () default false ; }
Uso de anotaciones

Las anotaciones pueden utilizarse en cualquier tipo de declaración, ya sea de paquete, clase (incluidas las enumeraciones), interfaz (incluidas las anotaciones), campo, método, parámetro, constructor o variable local. También pueden utilizarse con constantes de enumeración. Las anotaciones se declaran utilizando el @signo que precede al nombre del tipo de anotación, seguido de los pares elemento-valor entre corchetes. A todos los elementos sin valor predeterminado se les debe asignar un valor.

@BlockingOperations ( fileSystemOperations , // obligatorio networkOperations = true // opcional ) void openOutputStream () { // método anotado }

Además de la forma genérica, existen otras dos formas para declarar una anotación, que son abreviaturas. La anotación de marcador es una forma abreviada que se utiliza cuando no se asignan valores a los elementos:

@Unused // Abreviatura de @Unused() void travelToJupiter () {}

La otra forma abreviada se llama anotación de elemento único . Se utiliza con tipos de anotaciones que contienen un solo elemento o cuando hay varios elementos, pero solo uno de ellos carece de un valor predeterminado. En la anotación de elemento único, se omite el nombre del elemento y solo se escribe el valor:

/* Equivalente a @BlockingOperations(fileSystemOperations = true). networkOperations tiene un valor predeterminado y no es necesario asignarle un valor */@BlockingOperations ( true ) void openOutputStream () {}

Medicamentos genéricos

La programación genérica (genéricos), o tipos parametrizados, o polimorfismo paramétrico , es una de las características principales introducidas en J2SE 5.0 . Antes de la introducción de los genéricos, era necesario declarar todos los tipos explícitamente. Con los genéricos, se hizo posible trabajar de manera similar con diferentes tipos sin declarar los tipos exactos. El propósito principal de los genéricos es garantizar la seguridad de tipos y detectar errores en tiempo de ejecución durante la compilación. A diferencia de las plantillas de C++ , los genéricos no instancian una especialización para cada tipo, y a diferencia de C#, los genéricos no se reifican , y la información sobre los parámetros utilizados no está disponible en tiempo de ejecución debido al borrado de tipos [ 28 ] (mientras que Kotlin , otro lenguaje de JVM, ofrece genéricos reificados , lo logra mediante la inserción en línea del tipo en el sitio de llamada [ 29 ] ). Esto se debe a que históricamente, Java carecía por completo de genéricos; por lo tanto, los genéricos se borran en tiempo de ejecución para permitir que una clase genérica X<T>sea compatible con X.

Por ejemplo, antes de que se introdujeran los genéricos, el marco de colecciones de Java almacenaba todo como java.lang.Object.

import java.util.ArrayList ; import java.util.List ;List nombres = new ArrayList (); nombres . add ( "Alice" ); nombres . add ( "Bob" ); String primero = ( String ) nombres . get ( 0 ); // primero = "Alice" nombres . add ( 1 ); // Permitido

En tiempo de ejecución, los genéricos aparecen como el límite inferior del parámetro de tipo (si no se especifica ningún límite inferior, esto es implícitamente java.lang.Object). Debido a que los genéricos se borran, no es posible construir un tipo genérico (como ) ni comprobar tipos con respecto a una especialización (como ).newT()if(namesinstanceofList<String>)

Also, generic classes may not extend java.lang.Throwable.[30] This is because a generic exception FooException<T> would erase FooException<String> and FooException<Integer> to the same type at runtime, making the two indistinguishable at runtime. However, a generic type may be thrown (as long as it is bounded below by java.lang.Throwable). For example, the following pattern, sometimes known as the generic rethrow or sneaky throw, is used to bypass checked exceptions entirely, such as in the Project Lombok@SneakyThrows attribute.[31]

static<TextendsThrowable>voidsneakyThrow(Throwablet)throwsT{throw(T)t;}

This erases T down to java.lang.Throwable, bypassing the checked exception mechanism (which enforces only on types which do not descend from java.lang.Error or java.lang.RuntimeException) entirely.

Generic classes

Classes can be parameterized by adding a type variable inside angle brackets (< and >) following the class name. It makes possible the use of this type variable in class members instead of actual types. There can be more than one type variable, in which case they are declared in a comma-separated list.

It is possible to limit a type variable to a subtype of some specific class or declare an interface that must be implemented by the type. In this case the type variable is appended by the extends keyword followed by a name of the class or the interface. If the variable is constrained by both class and interface or if there are several interfaces, the class name is written first, followed by interface names with & sign used as the delimiter (in a way which resembles an intersection type).

/* This class has two type variables, T and V. T must be a subtype of ArrayList and implement Formattable interface */publicclassMapper<TextendsArrayList&Formattable,V>{publicvoidadd(Tarray,Vitem){// array has add method because it is an ArrayList subclassarray.add(item);}}

When a variable of a parameterized type is declared or an instance is created, its type is written exactly in the same format as in the class header, except the actual type is written in the place of the type variable declaration.

/* Mapper is created with CustomList as T and Integer as V.CustomList must be a subclass of ArrayList and implement Formattable */Mapper<CustomList,Integer>mapper=newMapper<CustomList,Integer>();

Since Java SE 7, it is possible to use a diamond (<>) in place of type arguments, in which case the latter will be inferred. This is sometimes called the "diamond operator", even though it is not actually an operator, but rather an empty type list. The following code in Java SE 7 is equivalent to the code in the previous example:

Mapper<CustomList,Integer>mapper=newMapper<>();

When declaring a variable for a parameterized type, it is possible to use wildcards instead of explicit type names. Wildcards are expressed by writing ? sign instead of the actual type. It is possible to limit possible types to the subclasses or superclasses of some specific class by writing the extends keyword or the super keyword correspondingly followed by the class name.

/* Any Mapper instance with CustomList as the first parametermay be used regardless of the second one.*/Mapper<CustomList,?>mapper;mapper=newMapper<CustomList,Boolean>();mapper=newMapper<CustomList,Integer>();/* Will not accept types that use anything buta subclass of Number as the second parameter */voidaddMapper(Mapper<?,?extendsNumber>mapper){}

Generic methods and constructors

Usage of generics may be limited to some particular methods, this concept applies to constructors as well. To declare a parameterized method, type variables are written before the return type of the method in the same format as for the generic classes. In the case of constructor, type variables are declared before the constructor name.

classMapper{// The class is not generic, the constructor is<T,V>Mapper(Tarray,Vitem){}}/* This method will accept only arrays of the same type asthe searched item type or its subtype */static<T,VextendsT>booleancontains(Titem,V[]arr){for(TcurrentItem:arr){if(item.equals(currentItem)){returntrue;}}returnfalse;}

Generic interfaces

Interfaces can be parameterized in the similar manner as the classes.

interfaceExpandable<TextendsNumber>{voidaddItem(Titem);}// This class is parameterizedclassMyArray<TextendsNumber>implementsExpandable<T>{voidaddItem(Titem){}}// And this is not and uses an explicit type insteadclassIntegerArrayimplementsExpandable<Integer>{voidaddItem(Integeritem){}}

See also

Notes

  1. Note that in class files, the dots appear as slashes. For example, Object[] becomes [Ljava/lang/Object;

References

  1. 12Oracle Corporation. "Chapter 6. The Java Virtual Machine Instruction Set". docs.oracle.com. Oracle Corporation. Retrieved May 26, 2026.
  2. Mark Reinhold (March 8, 2016). "The State of the Module System". Oracle Corporation. Retrieved February 18, 2017.
  3. "JEP 396: Strongly Encapsulate JDK Internals by Default". Retrieved February 6, 2021.
  4. "JDK Module Summary". Oracle Corporation. June 24, 2016. Archived from the original on December 8, 2015. Retrieved February 18, 2017.
  5. "JEP 494: Module Import Declarations (Second Preview)". openjdk.org.
  6. "Operators (The Java™ Tutorials > Learning the Java Language > Language Basics)". docs.oracle.com. Oracle and/or its affiliates. Archived from the original on June 24, 2015. Retrieved June 16, 2015.
  7. "The switch Statement (The Java™ Tutorials > Learning the Java Language > Language Basics)". docs.oracle.com. Archived from the original on March 15, 2010. Retrieved August 15, 2021.
  8. OpenJDK (September 23, 2025). "Release Note: Thread.stop Is Removed". bugs.openjdk.org. OpenJDK.
  9. Oracle Corporation. "Class ThreadDeath". docs.oracle.com. Oracle Corporation. Retrieved May 27, 2026.
  10. Oracle Corporation. "Class CompletableFuture<T>". docs.oracle.com. Oracle Corporation. Retrieved May 27, 2026.
  11. "Virtual Threads". Oracle Help Center. Retrieved September 10, 2024.
  12. "JEP 491: Synchronize Virtual Threads without Pinning". OpenJDK. Retrieved March 30, 2025.
  13. Owens, Sean. "Java and unsigned int, unsigned short, unsigned byte, unsigned long, etc. (Or rather, the lack thereof)". Archived from the original on February 20, 2009. Retrieved April 21, 2010.
  14. Oracle Corporation (March 17, 2026). "Class Arrays". docs.google.com. Oracle Corporation.
  15. Oracle Corporation (March 17, 2026). "Class Array". docs.google.com. Oracle Corporation.
  16. Oracle Corporation. "Chapter 8. Classes". docs.oracle.com. Oracle Corporation. Retrieved May 26, 2026.
  17. Oracle Corporation. "Chapter 2. The Structure of the Java Virtual Machine". docs.oracle.com. Oracle Corporation. Retrieved May 26, 2026.
  18. Sun Microsystems. "Avoid Finalizers". docs.oracle.com. Sun Microsystems. Retrieved May 27, 2026.
  19. Sun Microsystems. "Class Cleaner". docs.oracle.com. Sun Microsystems. Retrieved May 27, 2026.
  20. "Chapter 8. Classes". docs.oracle.com. Retrieved April 25, 2024.
  21. "Writing Final Classes and Methods". docs.oracle.com. Retrieved April 25, 2024.
  22. "Java theory and practice: Is that your final answer?". developer.ibm.com. Archived from the original on February 8, 2009. Retrieved April 25, 2024.
  23. Oracle Corporation. "8.1.1.1 - abstract Classes". docs.oracle.com. Oracle Corporation. Retrieved May 27, 2026.
  24. "Lambda Expressions (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com. Archived from the original on June 16, 2020. Retrieved August 8, 2021.
  25. Oracle Corporation. "Chapter 15. Expressions". docs.oracle.com. Oracle Corporation. Retrieved May 31, 2026.
  26. Oracle Corporation. "Class SerializedLambda". docs.oracle.com. Oracle Corporation. Retrieved May 31, 2026.
  27. Oracle Corporation. "Serialized Form". docs.oracle.com. Oracle Corporation. Retrieved May 31, 2026.
  28. "Generics in the Run Time (C# Programming Guide)". Archived from the original on March 10, 2016. Retrieved March 9, 2016.
  29. JetBrains sro (14 de diciembre de 2024). "Genéricos: entrada, salida, dónde" . kotlinlang.org . JetBrains sro
  30. Oracle Corporation. "Restricciones sobre genéricos" . docs.oracle.com . Oracle Corporation . Consultado el 28 de mayo de 2026 .
  31. Proyecto Lombok (22 de abril de 2026). "Tipo de anotación SneakyThrows" . projectlombok.org . Proyecto Lombok.

Lecturas adicionales

  • Naughton, Patrick ; Schildt, Herbert (1999). Java 2: La referencia completa (3.ª  ed.). The McGraw-Hill Companies. ISBN 0-07-211976-4.
  • Vermeulen; Ambler; Bumgardner; Metz; Misfeldt; Shur; Thompson (2000). Los elementos del estilo Java . Cambridge University Press. ISBN 0-521-77768-2.
  • Gosling, James ; Joy, Bill ; Steele, Guy ; Bracha, Gilad (2005). Especificación del lenguaje Java (3.ª  ed.). Addison-Wesley Professional. Archivado del original el 26 de febrero de 2009. Recuperado el 3 de diciembre de 2008 .
  • Especificación del lenguaje Java, tercera edición. Descripción autorizada del lenguaje Java.
  • Javadocs de la API de Java SE 24