Archivo de la categoría: Java

Java es un lenguaje puramente orientado a objetos, todo el código está definido en clases

Fue definido por la empresa Sun Microsystems, que fue adquirida en el año 2009 por Oracle Corporation

Java

Java

Java es un lenguaje puramente orientado a objetos, todo el código debe estar incluido en alguna clase

Fue definido por la empresa Sun Microsystems, que fue adquirida en el año 2009 por Oracle Corporation, anteriormente parte de Silicon Valley, fabricante de semiconductores y software; pero el lenguaje sigue siendo de dominio público. En particular, Oracle ofrece en su página web, de forma gratuita, un JDK (Java Development Kit) el cual permite compilar, depurar y ejecutar programas Java

Si tenemos un programa en un fichero programa.java, podremos compilarlo desde la línea de comandos con:

Esto genera una colección de ficheros de extensión .class, uno por cada clase que aparezca en programa.java y con el nombre de la clase correspondiente

Solo en una de las clases (en el entorno activo) debe tener un método llamado main (el método principal); supongamos que está en la clase programa, podremos ejecutar el programa desde la línea de comandos:

En el caso anterior, la extensión seria opcional

Para que esto funcione correctamente, debemos tener instalado el JDK y hay que asegurarse de que la trayectoria almacenada en la variable CLASSPATH esté en la trayectoria por defecto del sistema

Normalmente los programas se desarrollan en un entorno integral de programación que contiene además de la funcionalidad del JDK, al menos un editor de programas. Sun ofrece en su página web un entorno integrado para Java: Netbeans

El resultado de la compilación de un programa java (los ficheros .class) está expresado en un lenguaje intermedio llamado bytecode

El bytecode es interpretado por la máquina virtual de Java (JVM); la cual es un programa que es invocado por la orden java de la línea de comandos

Los distintos navegadores modernos incorporan una JVM, de modo que pueden ejecutar programas Java desde el navegador, a través de Applets incrustados dentro de las páginas html

Aunque el navegador de Google Chrome ha restringido su uso por seguridad y el JVM no está incluido por defecto, en caso de que el usuario deseara usarlas, debería instalar un pluging de terceros o utilizar un servidor dedicado para aplicaciones Java como por ejemplo Tomcat

Diferencias entre Java y C++

La sintaxis de Java está inspirada en la de C y es superficialmente muy similar a C++ por esa causa. Pero también tiene importantes diferencias

Modos de acceso de Java:

  • Cada atributo o método tiene asociado un permiso
  • Además de public y private (ambos sin los dos puntos) está el modo por defecto package

En cambio en C++, el modo de acceso por defecto es privado

Para utilizar algo por defecto en Java se usan los package (paquetes), que es una simulación de la noción de namespace de C++

Para crear o extender un paquete utilizando la orden package:

  • Debe encontrarse en la 1ª línea del fichero fuente (todo aquello que se encuentre en ese fichero pertenecerá al paquete)
  • Irá seguido del nombre del paquete que se desee utilizar

Si se quieren utilizar desde un paquete clases, atributos, métodos de otro paquete, se utiliza la orden import (es muy similar al using namespaces de C++)

Si el programador no define ningún package explícitamente, el sistema crea uno por defecto sin nombre, que contendrá todos los nombres de los ficheros (que no tengan package explícito) que pertenezcan a ese directorio del sistema operativo

En conclusión, en caso de ausencia de packages explícitos, el modo de acceso por defecto a una clase, un atributo o un método, es accesible desde todos los ficheros de un mismo directorio

Las clases en Java también tienen modo de acceso: pública, privada o por defecto

Si encontramos public al definir una clase, tiene dos consecuencias:

  • Es accesible desde cualquier punto, independientemente de los packages
  • Su nombre debe ser el del fichero .java que lo contiene; sólo puede haber una clase pública por fichero

Comentarios

Java tiene tres tipos de comentarios:

  • El // comenta hasta el final de la línea
  • El /* comenta hasta el primer */ (no se permite el anidamiento)
  • El /** comenta hasta el primer **/

Paquetes

Paquetes

A medida que se incorporan clases de terceros aumenta la posibilidad de encontrarse clases con el mismo nombre

Las posibilidades son aún mayores en Java, donde las clases de millones de programadores viajan a través de la red

La solución que aporta Java son los paquetes (packages)

Para especificar la clase deseada basta con indicar el nombre del paquete al que pertenece

Sun dió en su día unas recomendaciones para que los nombres de los paquetes fueran únicos:

  • Si se va a utilizar dentro de la empresa podrán usar cualquier convenio interno

  • Si se va a utilizar fuera de la empresa deberá usarse como prefijo el dominio de Internet de la compañía invertido (esto asegura que sea único), por ejemplo com.sun.graphics3D

  • Las clases estándar tienen reservado los prefijos java y javax

Toda clase debe pertenecer a algún paquete

Las clases Java se reparten en varios paquetes básicos:

Package
java javax
java.applet javax.accessibility
java.awt javax.activation
java.beans javax.activity
java.io javax.annotation
java.lang javax.crypto
java.math javax.imageio
java.net javax.jws
java.nio javax.lang
java.rmi javax.management
java.security javax.naming
java.sql javax.net
java.text javax.print
java.util javax.rmi
javax.script
javax.security
javax.smartcardio
javax.sound
javax.sql
javax.swing
javax.tools
javax.transaction
javax.xml

El nombre de una clase incluye un prefijo el paquete al que pertenece

En el ejemplo, la clase Vector se encuentra dentro del paquete java.util

En el ejemplo se ha utilizado la palabra reservada import que permite indicar el paquete al que pertenece una o más clases, de tal manera que no haya que repetirlo con cada uso de las mismas

import no produce la carga de ningún tipo de fichero (no equivale al #include de C / C++)

El paquete java.lang es importado automáticamente

Colisión de nombres

En el caso de que se repita el nombre de una clase en dos paquetes importados habría que utilizar el nombre completo de la clase para deshacer la ambigüedad

En el ejemplo se ha especificado el paquete del que se desea coger la clase Vector, con el símbolo asterisco le estamos diciendo que tome todo el paquete

En el ejemplo se ha usado un import de la clase concreta Vector y otro de todo el paquete

Hay que tener en cuenta que el import de la clase tendrá prioridad sobre el import de todo un paquete

Por lo que podría eliminarse si no queremos usar el resto de las clases de ese paquete, al ser redundante

Creación de paquetes

Para indicar que una clase pertenece a un paquete hay que usar la cláusula package

En el ejemplo se ha creado el paquete ejemplos que contiene sólo la clase Ejemplo1

Si hemos de utilizar en otra clase, el nombre del paquete podrá estar formado por varias palabras separadas por punto

En este caso el nombre de la clase será ejemplos.Ejemplo1

Si quisiéramos ejecutarla para usarla en otras clases desde línea de comandos:

Classpath

Java no tiene el concepto de fichero ejecutable (fichero con extensión .EXE o .COM)

Cada clase genera un fichero class independiente

Para ejecutar un programa hay que indicar el nombre de una clase que contenga al menos una función main

El resto de clases se irán cargando dinámicamente bajo demanda

Puede haber varias clases con main pero sólo puede haber un main que sea el principal

En el caso de aplicaciones locales se localizará el código de las clases:

  • La JVM añade al nombre de la clase la extensión class para formar el nombre del fichero

  • Para localizar dicho fichero se utiliza la variable de entorno CLASSPATH

  • CLASSPATH indica una serie de localizaciones en las cuales buscar las clases que pueden ser:

    • Un directorio
    • Un fichero ZIP o JAR (Java Archive Resource)

En el ejemplo se ha usado la línea de comandos de Windows para definir el CLASSPATH

La clase Prueba buscaría (en este orden) en:

  1. c:\ejemplos\Prueba.class
  2. dentro del fichero
  3. c:\java\lib\classes.zip y en ningún lugar más

Cuando se utilizan clases hechas por terceros es necesario incluir su directorio (o ZIP, JAR) en el CLASSPATH para que la JVM se capaz de encontrarlas

Paquetes y Classpath

En el JDK hay relación entre el paquete de una clase y el directorio en el que se encuentra

Todas las clases de un paquete deberán estar en un directorio con el mismo nombre

Si el nombre de un paquete tiene varias palabras cada palabra se corresponde con el nombre de un subdirectorio

Por ejemplo, las clases del paquete ur.mis.ejemplos estarían en el directorio ur/mis/ejemplos

Este directorio deberá ser accesible a partir de alguna de las localizaciones del CLASSPATH

El mecanismo de la JVM del JDK para localizar la implementación de una clase en tiempo de ejecución es:

  1. añade .class al nombre completo de la clase
  2. transforma los puntos en separadores de directorio \
  3. añade la cadena resultante a cada una de las localizaciones del CLASSPATH
  4. si no se encuentra en ninguna de ellas se produce un error de ejecución

Sigamos el proceso con un ejemplo en el que queremos cargar en el JDK la clase UR.ejemplos.ejemplo1:

  1. Definimos el CLASSPATH como:

  2. La JVM busca la clase en el fichero UR\ejemplos\ejemplo1.class

  3. El fichero será buscado en las localizaciones del CLASSPATH c:\programas\UR\ejemplos\ejemplo1.class y c:\temp\UR\ejemplos\ejemplo1.class

  4. Si la clase no se encuentra en ninguno de estos dos sitios, se producirá un error de ejecución

Tipos primitivos en Java

Tipos primitivos

Los tipos primitivos se dividen en:

  • Caracteres
    • char
    • String
  • boolean
  • Numéricos
    • Enteros
      • byte
      • short
      • int
      • long
    • Reales
      • float
      • double
  • Arrays

Los tipos primitivos se caracterizan por tener definidos un mismo tamaño en todas las plataformas (a diferencia del C / C++)

Los literales reales son de tipo double a menos que se incluya el sufijo F

3.14 no es del mismo tipo que 3.14F, el primero será double y el segundo float

Cuando se desea provocar una conversión se utiliza el operador cast

Tiene la siguiente sintáxis:

Usando el cast se pueden realizar aquellas conversiones que el compilador no realiza por defecto

En el ejemplo se ha realizado un casting del double 5.4 a int para guardarlo en la variable i

El resultado de esa operación será que el compilador tomará sólo la parte entera del double, es decir, guardará en i el valor 5

En la segunda operación del ejemplo se divide 5 entre 2, dicha operación devolvería su resultado como un double, pero si el resultado no tendría decimales, se guardaría como un entero

Al hacer un casting en el denominador, forzamos al compilador a que guarde el resultado de la división como un double aunque no tenga decimales

Hay que tener en cuenta, que un casting no corrige el error de división por 0, debe controlarse mediante el uso de excepciones

Wrappers

Un wapper es una envoltura que se aplica a un tipo primitivo concreto

Hay una clase wrapper asociada a cada tipo primitivo

En el ejemplo se han redefinido los wrappers para int (es Integer) y para double (es Double) para poder añadir los datos primitivos int y double en la clase Vector

Las clases Wrapper tienen una segunda funcionalidad

Se utilizan para ubicar todos aquellos servicios propios de los tipos primitivos que representan

Algunos métodos de wrappers importantes:

Wrappers
Integer Character
MAX_VALUE chaValue()
MIN_VALUE equals(Object)
Integer(int) getType(char)
Integer(String) isDigit(char)
byteValue() isIdentifierIgnorable(char)
doubleValue() isJavaIdentifierPart(char)
equals(Object) isJavaIdentifierStart(char)
floatValue() isJavaLetterOrDigit(char)
intValue() isLetter()
longValue() isLetterOrDigit(char)
parseInt(String) isLowerCase(char)
parseInt(String, int) isUpperCase(char)
shortValue() isWhitespace(char)
toBinaryString(int) toLowerCase(char)
toHexString(int) toString()
toOctalString(int) toUpperCase(char)
toString(int, int) toString(int, int)

Caracteres

Un carácter es un símbolo que sigue el estándar ASCII y que nos sirve para generar los textos de nuestros programas

Una cadena de caracteres es un array de caracteres que permiten guardar textos o frases

char

Es una letra que sigue el estándar ASCII y es la unidad menor de cadena

Su tamaño es de 8 bits

Su valor se podría considerar un entero pequeño ya que su rango es -128 a 127

Los valores char tienen asociada la clase Character

Se inicializa con comillas simples

En el ejemplo se inicializado la variable cad como cadena y como puede apreciarse se ha guardado el símbolo ASCII que corresponde a la letra a mayúscula

String

Los cadenas de caracteres no son un tipo primitivo

Sin embargo Java le ofrece unas clases especiales para su tratamiento

Las cadenas son objetos que se manipulan mediante las clases String y StringBuffer

Se usa String cuando la cadena no va a ser modificada

Se usa StringBuffer cuando se desea manipular la cadena

Se recomienda usar habitualmente String (al ser constantes) por ser más eficientes

Inicialización de un String

Las cadenas van entre comillas dobles y los carácter con comillas simples

Cuando el compilador encuentre un literal de cadena, creará un objeto String con el texto de la misma

En el ejemplo se han inicializado dos variables que podrían ser equivalentes

s1 se ha inicializado usando sólo las comillas dobles

s2 se ha inicializando el constructor de objetos de la clase String pasándole como argumento el valor con comillas dobles

Ambas inicializaciones son válidas y el contenido de las variables s1 y s2 será en ambas Hola

La única diferencia es que al ser objetos distintos, su referencia en memoria será también distinta

Métodos básicos

length

El método length devuelve el número de caracteres de una cadena

En el ejemplo se han pedido a dos cadenas de caracteres su número de caracteres

En la primera se han usado comillas dobles y en la segunda el objeto String s1 del ejemplo anterior

Las dos expresiones anteriores serian equivalentes, devuelven un entero con el número de caracteres de la cadena

charAt

El método charAt devuelve el carácter de la posición especificada

Si el argumento de charAt no es un número entre 0 y length – 1 se producirá una excepción

En el ejemplo se ha creado una función que dados un String str y el char busca, se busca el carácter en esa cadena y si se encuentra, el valor de retorno será mayor que 0

Para lograrlo se ha usado el método charAt al que se le ha pasado el valor de i que representa la posición de carácter leído hasta el momento

Como devuelve el carácter leído en ese momento hemos podido compararlo con el carácter busca que nos pasaron como argumento

Métodos de búsqueda

Los métodos para realizar búsquedas en cadenas devuelven la posición del elemento buscado o -1 si no se ha encontrado:

  • indexOf(int ch)
  • indexOf(int ch, int start)
  • indexOf(String str)
  • indexOf(String, int start)
  • lastIndexOf(int ch)
  • lastIndexOf(int ch, int start)
  • lastIndexOf(String str)
  • lastIndexOf(String str, int start)

En el ejemplo se ha usado cada uno de los métodos y se ha comentado su valor devuelto para poder comprenderlos mejor

Comparación de cadenas

Para comparar cadenas no sirve el operador ==, ya que en realidad estamos comparando objetos

La clase String tiene varios métodos para comparar cadenas

Dos cadenas serán iguales si tienen la misma longitud y los mismos caracteres Unicode (a y á son distintos)

Los métodos más habituales son:

  • equals
  • equalsIgnoreCase

    distingue entre mayúsculas y minúsculas

  • compareTo

    además de permitir averiguar si dos cadenas son iguales, también nos dice cual es mayor de las dos:

    • Igual a 0

      Eran iguales

    • Mayor que 0

      El segundo valor era mayor

    • Menor que 0

      El segundo valor era menor

En el ejemplo se ha usado cada uno de los métodos y se ha comentado su valor devuelto para poder comprenderlos mejor

Métodos para comparar prefijos y sufijos

  • boolean starsWith(String)
  • boolean starsWith(String, int start)
  • boolean endsWith(String)

En el ejemplo se ha usado cada uno de los métodos y se ha comentado su valor devuelto para poder comprenderlos mejor

Extracción de cadenas

Hay una serie de métodos que devuelven una nueva cadena que resulta de manipular la cadena original

La cadena original no se modifica (se crea una nueva instancia de la cadena, es decir, se usa otra posición de memoria)

  • String concat(String)
  • String replace(char, char)
  • String replaceAll(String, String)
  • String substring(int start)
  • String substring(int start, int end)
  • String toLowerCase()
  • String toUpperCase()
  • String trim()

En el ejemplo se han usado algunos de los métodos y se ha comentado su valor devuelto para poder comprenderlos mejor

Conversiones de cadenas

Los tipos primitivos se pueden convertir automáticamente a cadenas

Para convertir las cadenas en tipos primitivos hay que utilizar alguna de las siguientes funciones:

  • boolean new Boolean(String).booleanValue()
  • int Integer.parseInt(String)
  • long Long.parseLong(String)
  • float Float.parseFloat(String)
  • double Double.parseDouble(String)

Otros métodos

  • char[] toCharArray()
  • void getChars(int srcBegin, int srcEnd, char[] dest, int destBegin)
  • int hashCode()
  • String valueOf(boolean)
  • String valueOf(int)
  • String valueOf(long)
  • String valueOf(float)
  • String valueOf(double)
  • String[] split(String)

Como puede verse en el ejemplo, usaremos split para trocear cadenas usando un token, en este caso hemos usado el espacio

Este método es bastante útil para trabajar con ficheros de texto en formato CSV, ya que se suele utilizar el símbolo ; (u otro símbolo de token) para separar las distintas columnas o celdas

StringBuffer

Aunque la clase String es la más habitual no es la adecuada para cadenas que deben modificarse frecuentemente

En el ejemplo se crean cuatro objetos String de los cuales sólo uno va a ser usado, admiracion1 o admiracion2

La clase StringBuffer permite modificar la cadena original sin necesidad de crear objetos intermedios como se hizo en el ejemplo

Inicialización de un StringBuffer

Admite los siguientes constructores:

  • StringBuffer()
  • StringBuffer(int i)
  • StringBuffer(String str)

Métodos de modificación

Admite los siguientes métodos de modificación:

  • append(Objetc obj)
  • append(String str)
  • append(char str[] )
  • append(boolean b)
  • append(int i)
  • append(long l)
  • append(float f)
  • append(double d)
  • append(char ch)
  • insert(int offset, Object obj)
  • insert(int offset, String str)
  • insert(int offset, char str[] )
  • insert(int offset, boolean b)
  • insert(int offset, int i)
  • insert(int offset, long l)
  • insert(int offset, float f)
  • insert(int offset, double d)
  • insert(int offset, char ch)
  • setCharAt(int index, char c)
  • setLength(int longitud)

En el ejemplo se han usado algunos de los métodos y se ha comentado su valor devuelto para poder comprenderlos mejor

Métodos básicos

length

Es similar al de String

charAt

Es similar al de String

toString

Realiza la conversión del BufferString a un String

Operador +

Java permite la concatenación de cadenas mediante el +

En el ejemplo, internamente el compilador utiliza un StringBuffer en vez de un String

Por lo que este ejemplo es totalmente compatible con el anterior

En el ejemplo hemos concatenado la cadena con otro tipo (entero), este último se convierte automáticamente a cadena (el compilador invoca implícitamente el método toString() de la clase StringBuffer)

boolean

Su tamaño es de 1 bit

Su valor se podría considerar un entero pequeño ya que su rango es 0 a 1

Siendo el valor 0 igual a FALSE y 1 igual a TRUE

Los valores boolean tienen asociada la clase Boolean

Métodos básicos

booleanValue

Devuelve el valor del booleano

toString

Convierte el tipo booleano a una cadena

Numéricos

Son todos los tipos que permiten trabajar con números y se pueden distinguir según si son enteros o decimales o según su precisión

Enteros

Son todos los tipos que permiten trabajar con números enteros, hay varios tipos según su precisión y el rango de números que pueden llegar a manejar

byte

Su tamaño es de 8 bits

Su valor se podría considerar un entero pequeño sin signo ya que su rango es 0 a 255

Los valores byte tienen asociada la clase Byte

short

Su tamaño es de 16 bits

Su valor se podría considerar un entero sin signo ya que su rango es 0 a 65535

Los valores short tienen asociada la clase Short

int

Su tamaño es de 16 bits

Su valor se podría considerar un entero con signo ya que su rango es -32768 a 32767

Los valores int tienen asociada la clase Integer

long

Su tamaño es de 32 bits

Su valor se podría considerar un entero grande ya que su rango es -2147483648 a 2147483647

Los valores long tienen asociada la clase Long

Reales

Son todos los tipos que permiten trabajar con números decimales, hay varios tipos según su precisión y el rango de números que pueden llegar a manejar

float

Su tamaño es de 32 bits

Su valor se podría considerar un decimal pequeño ya que su rango es 3,4E-38 a 3,4E+38

Los valores float tienen asociada la clase Float

double

Su tamaño es de 64 bits

Su valor se podría considerar un decimal grande ya que su rango es 1,7E-308 a 1,7E+308

Los valores double tienen asociada la clase Double

Arrays

Un array es una agrupación de elementos del mismo tipo

Los arrays en Java se caracterizan por ser objetos

Por tanto, se necesitará una referencia para manipularlos

En el ejemplo podemos ver que como cualquier otro objeto, se inicializa con new

Para acceder a los elementos del array se usa la notación clásica de arrays (usando un índice)

Si no se especifica el valor máximo del indice, el compilador lo tomará como un array dinámico

Se puede considerar que la notación es equivalente a enviar un mensaje al array para consultar un elemento o modificarlo

El rango de un array está entre 0 y N-1 (siendo N el tamaño del array)

Si se accede a una posición fuera de rango se producirá una excepción

En el ejemplo hemos inicializado el array a para luego poder visualizar todo su contenido por pantalla

Como puede verse, Java permite inicializar el array en su declaración

El operador length permite averiguar el tamaño de un array

En el ejemplo hemos inicializado un array de referencias en su declaración

Como puede verse, ambas declaraciones son equivalentes

Arrays bidimensionales

Un array bidimensional puede tener varias filas, y en cada fila no tiene por qué haber el mismo número de elementos o columnas

En el ejemplo hemos declarar e inicializado la matriz bidimensional de doubles matriz para luego recorrerla y visualizarla por pantalla

La primer fila tiene cuatro elementos {1,2,3,4}

La segunda fila tiene dos elementos {5,6}

La tercera fila tiene seis elementos {7,8,9,10,11,12}

La cuarta fila tiene un elemento {13}

matriz.length nos proporciona el número de filas que en este caso es

matriz[i].length nos proporciona el número de elementos en cada fila

Mostramos los elementos de una fila separados por un tabulador usando la función print

Una vez completada una fila se pasa a la siguiente mediante println

Ejemplo de matriz identidad

Para mostrar la potencia de una matriz bidimensional, en este ejemplo se va a construir una matriz identidad de dimensión 4

Una matriz identidad es aquella cuyos elementos son ceros excepto los de la diagonal principal, i = = j, que son unos

Mediante un doble bucle for recorremos los elementos de la matriz especificando su fila i y su columna j poblando la matriz con 1 o 0 en función de si coincide i y j

Mediante un doble bucle for recorremos los elementos de la matriz especificando su fila i y su columna j

Mostramos los elementos de una fila separados por un tabulador usando la función print

Una vez completada una fila se pasa a la siguiente mediante println

Clases en Java

Clases en Java

En Java todo objeto (las clases) se manipula a través de una referencia

Esa referencia guarda el identificador del objeto

Y se encarga de enviar los mensajes al objeto asociado

El tipo de la referencia indica qué tipo de mensajes puede enviar

Modificadores de acceso

Al crear clases surge la necesidad de indicar el uso de sus atributos y métodos

Algunos atributos serán internos:

  • No deben modificarse

  • No es necesario saber de su existencia

Lo mismo ocurre con los métodos:

  • No deben invocarse desde el exterior de la clase

  • No hace falta conocer su utilidad

Los modificadores de acceso controlan la visibilidad de los miembros de la clase

Indican cuales de ellos son de interés para el exterior

Java ofrece cuatro modificadores de acceso:

  • private

    es la visibilidad más restrictiva

    Sólo permite la invocación desde la propia clase

  • public

    es la visibilidad menos restrictiva

    Permite la invocación desde cualquier clase

  • package

    es la visibilidad por defecto

    Sólo permite la invocación desde las clases que pertenezcan a su mismo paquete

  • protected

    es la visibilidad asociada a la herencia

    Sólo permite la invocación desde las clases derivadas

Constructores

Cuando una clase no declara ningún constructor se crea automáticamente un constructor por defecto

En el ejemplo el constructor ha sido llamado desde el método main y es el encargado de dejar al objeto en un estado inicial con el que podremos trabajar con él

Como no hemos creado el constructor, se ha usado automáticamente el constructor por defecto

En el ejemplo se ha definido una clase llamada Persona que tiene sólo el atributo nombre que es privado

Se ha usado el constructor para inicializarlo y para acceder a él (como es privado no es posible acceder directamente) se han añadido los métodos get y set

En caso de no haber usado el constructor para inicializarlo con new, al usar p habríamos obtenido un valor null, ya que el objeto todavía no estaría definido en memoria

Para referirnos a un atributo de la propia clase usaremos la palabra reservada this

En este caso se ha usado para referirnos al atributo nombre

Constructor con parámetros

Cuando necesitamos añadir información externa, podemos usar los constructores con parámetros

Un constructor puede declarar los parámetros que necesite para crear el objeto

Este ejemplo es muy similar al anterior, sin embargo ahora tenemos dos constructores, un constructor por defecto y un constructor con parámetros

En el caso de que tengamos el mismo nombre en un parámetro y un atributo, podremos distinguirlos si usamos la palabra reservada this

Nos ocurría con el parámetro nombre y el atributo nombre, al usar this han quedado desambigüados

Un constructor también puede estar sobrecargado, es decir, recibe distintos tipos de argumentos para tener un comportamiento distinto

En este ejemplo se han definido las clases Point y Rectangle

Definiendo varios constructores con parámetros de distintos tipos para la clase Rectangle

Así hemos sobrecargado el constructor consiguiendo distintos resultados en el método main

Atributos static

En una clase se describen los atributos de cada objeto (atributos de instancia)

A veces se necesitan atributos que sean compartidos por todos: que permanezcan a la clase

La palabra reservada static declara un atributo de clase

En el ejemplo se ha declarado un atributo static de tipo int, no es una clase pero como puede verse también se pueden usar tipos primitivos

Para acceder al atributo static le hemos indicando el nombre de la clase, en este caso A

No es necesario que haya ninguna instancia de clase para poder acceder al atributo

Se crean al cargar la clase y se inicializarán con los valores del constructor por defecto

En el ejemplo se han usado las clases Cronometro y A inicializando sus atributos en un bloque static

Con static no deben crearse objetos nuevos porque se reiniciaran con cada nueva instanciación

Los atributos de clase se inicializan mediante un bloque static

Los bloques static se ejecutan al cargar la clase

Se pueden considerar como un constructor más de la clase

En este ejemplo se ha reducido el código al usar una alternativa equivalente al inicializar en la propia declaración

Métodos static

De la misma forma que hay atributos static hay métodos static

Se recomienda su uso con métodos de utilidad que no se realizan sobre ningún objeto pero sí sobre tipos primitivos

Por ejemplo la clase Math ofrece los métodos static sqrt, random, pow, etc

Se declaran mediante la palabra static

En el ejemplo se ha declarado el método static f en la clase A

Para invocarlos se usa el nombre de la clase, en este caso la clase A

Un método static solo puede acceder:

  • a atributos static

  • a otros métodos static

En el ejemplo se ha añadido una referencia al método static g lo que nos permite acceder a los elementos de instancia de la clase A sin que fueran static

Hay que tener en cuenta que para acceder a elementos de instancia se debe obtener una referencia previamente

Herencia

Una clase derivada podrá añadir nuevas operaciones (ampliar el número de mensajes que entiende el objeto)

En el ejemplo se han creado las clases A y B

Como B heredaba los métodos de A y por tanto, podía usar metodoA sin problemas

Sin embargo, A no heredaba los métodos de B, por eso nos daba un error de ejecución

En el ejemplo se ha usado una clase derivada para redefinir operaciones

Redefinir un método consiste en dar una implementación distinta al mismo mensaje (que será tratado por otro método)

Es importante no confundir la sobrecarga de un método con su redefinición

En el ejemplo se ha redefinido el método metodoA de la clase A en la clase B, reutilizandolo sustituyendo su implementación

Se ha utilizado la palabra reservada super para referirse a los métodos de la clase base

También puede utilizarse la palabra reservada super para referirse a los atributos de la clase base

En el ejemplo hay un objeto base Vehiculo, dos derivados Coche y Bicicleta

Un aspecto fundamental de la herencia es la relación entre el tipo base y el tipo derivado

La clase derivada tiene, al menos, las operaciones de la clase base

Es decir, un objeto de la primera clase entiende todos los mensajes que pudiera entender un objeto de la segunda

Por tanto, cualquier referencia del tipo base podrá apuntar a un objeto del tipo derivado (ya que entiende todos sus mensajes)

Polimorfismo

La herencia permite que distintas clases puedan compartir un conjunto común de operaciones (las de la clase base)

El polimorfismo (dynamic binding) permite que aunque dos objetos acepten los mismos mensajes puedan realizarlos de forma diferente

En el ejemplo podemos ver que gracias al polimorfismo, a pesar de que v es de tipo Vehiculo se tiene en cuenta el tipo de objeto al que apuntaba

Es importante diferenciar los dos tipos que participan en el envío de un mensaje:

  • El tipo de referencia

    es el que determina los mensajes que ésta puede transmitir a su objeto asociado

    Por tanto, sólo se podrá asociar a objetos que entiendan, al menos, dichos mensajes (objetos del mismo tipo o de un tipo derivado)

  • El tipo del objeto asociado

    es el que determina que método es el que despachará el mensaje recibido

Los constructores y la herencia

Los constructores no pueden heredarse

En el ejemplo se han declarado dos constructores para la clase A y luego se ha heredado en la clase B

Al tratar de usar el constructor con parámetro entero nos dará un error porque B no poseía un constructor apropiado a pesar de que A sí que tenía

Una clase solo podrá usar los constructores que declare

Si no se ha declarado ninguno se le asignará el constructor por defecto

En el ejemplo se han declarado dos constructores para la clase A y luego se ha heredado en la clase B

Pero en esta ocasión se ha hecho una llamada al constructor por defecto de la clase base dentro del constructor de la clase derivada

Es obligatorio que se invoque algún constructor de la clase base (para garantizar su estado)

Mediante super se puede seleccionar el constructor adecuado

super solo puede aparecer una vez y debe ser la primera línea del constructor

Admitirá sólo los parámetros que admitan los constructores de la clase base

Clases abstractas

Una clase abstracta es aquella que tiene uno o más métodos abstractos

Un método abstracto es aquel que no ha sido implementado en la clase base

Su código deberá ser aportado en cada una de las clases derivadas

Para definir una clase como abstracta se utiliza la palabra reservada abstract

En el ejemplo se ha definido la clase abstracta Vehiculo con dos métodos abstractos

Como puede verse, los métodos también se definen usando la palabra reservada abstract

Las clases abstractas tienen la restricción de que no pueden instanciarse (por tener métodos sin implementar)

Si una clase derivada no redefine todos los métodos abstractos, será a su vez una clase abstracta

Creación de clases abstractas:

  • Identificar las operaciones comunes que deben tener los objetos a manipular

  • Crear con ellos una clase abstracta base

  • Identificar las operaciones a redefinir y dar una implementación al resto

Uso de clases abstractas:

  • Su único objetivo es la derivación

  • Las clase derivadas tendrán que implementar los métodos abstractos

  • Las demás podrán heredarse, sustituirse o ampliarse

Clases final

Puede que se desee que una clase no pueda ser derivada (por razones de seguridad, no se desea que nadie pueda sustituirla por una clase derivada)

Se utiliza la palabra reservada final para que la clase no pueda ser derivada

En el ejemplo hemos intentado heredar con la clase B la clase A que era final

Como hemos definido A como final el interprete no permitirá que tenga clases heredadas

En el el ejemplo se ha definido un método final y otro que no lo es

También hay un nivel intermedio: que se pueda derivar de la clase pero no ciertos métodos

Un método también puede usar la palabra reservada final

Un método final no puede ser redefinido

En el ejemplo se ha intentado modificar un atributo final, el intérprete nos dará un error

Hacer una clase o método final es una gran restricción

Solo debe hacerse en situaciones especiales y poniendo sumo cuidado

Un atributo final no puede ser modificado una vez que haya sido iniciado

En el ejemplo se han definido dos variables llamadas x como final

Si el valor del atributo final es el mismo para todas las instancias se está desaprovechando espacio en memoria

En este ejemplo se han utilizado variables constantes para solucionar el problema del ejemplo anterior

En Java las constantes se declaran combinando las palabras reservadas static seguida de final

Las constantes no ocupan memoria y por convenio, se suelen definir con un nombre en mayúsculas

Downcasting

Cuando una clase no deriva explícitamente de otra clase entonces deriva implícitamente de la clase java.lang.Object

La clase Object no es un tipo primitivo, pero es la clase de la cuál derivan todas las clases del lenguaje y aquellas que el programador cree

Sin embargo, no es necesario usar la palabra reservada extends porque deriva implícitamente

Por tanto, toda clase de Java es un Object y heredará los métodos de este

Algunos métodos destacados de la clase Object:

  • boolean equals(Object)
  • void finalize()
  • int hashCode()
  • String toString()

Hay ocasiones en las que se sabe con certeza que el objeto es de la clase adecuada

En estos casos se podrá forzar la asignación mediante un cast

En el ejemplo se han utilizado las clases Vehiculo y Canoa que hereda de ella

Para luego hacer una declaración de un Vehiculo v para el que se ha utilizado el constructor de Canoa

Y se ha declarado una Canoa c para la que se conocía previamente el tipo (Canoa) por lo que se ha realizado un cast

El cast comprueba que el objeto es de la clase adecuada y realiza la asignación

En caso de no ser de la clase adecuada o no es capaz de realizar la asignación lanzará una excepción ClassCastException

Operador instanceof

El operador instanceof permite averiguar si un objeto pertenece a una determinada clase

En el ejemplo se ha realizado dos comprobaciones con instanceof

Cuando hemos probado con Object devolverá siempre True porque es un supertipo de la clase de la instancia

Cuando hemos probado con Canoa hemos comprobado si el cast puede aplicarse

De esta forma podemos evitar tener que controlar la excepción ClassCastException

Interfaces

Los interfaces, al igual que las clases, son una forma de crear objetos

Se caracterizan porque sólo se declaran un comportamiento (y/o constantes)

  • No tienen atributos

  • No tienen código asociado para los métodos

Se definen con la palabra reservada interface de forma similar a como se hace con las clases

En el ejemplo se ha definido la interfaz unInterface con los métodos metodo1 y metodo2

De forma similar a como ocurría con las clases abstractas, como no tienen implementación es posible instanciar la interfaz

Sólo es posible usarla para crear nuevos tipos de datos (otras clases o interfaces)

En este ejemplo se ha implementado la interfaz unInterface heredandola en la clase UnaClase

Cuando la clase UnaClase ha heredado los métodos de unInterface se ha convertido en un subtipo de esa interfaz

Por esa razón hay que darles una implementación en la clase, ya que sinó estaríamos obligados a declararla abstract

Pero precisamente por haber utilizado una interfaz, el interprete nos mostrará un mensaje de error diciéndonos qué métodos concretos no han sido implementados

Diferencias entre clase e interface

Clase:

  • Tipo que al extenderlo mediante herencia se obtienen su comportamiento y su implementación (tanto atributos como sus métodos)

  • Ventaja: Menor codificación al crear nuevos subtipos ya que su comportamiento viene con su implementación

  • Inconveniente: Sólo se puede derivar de una de ellas, no está permitida la herencia múltiple

Interfaces:

  • Tipo que al extenderlo mediante herencia se obtiene solamente su comportamiento (descripción de los métodos)

  • Ventaja: Se pueden heredar varios interfaces sin conflictos

  • Inconveniente: Hay que codificar la implementación de cada método para cada subtipo heredado

Sentencias de control en Java

Sentencias de control

Las sentencias de control son no son más que un conjunto de palabras que indican al intérprete cosas como cuál es la próxima sentencia a ejecutar, cuántas veces evaluar una determinada expresión, etc

Sentencias de selección

Sirven para agrupar declaraciones y sentencias en una sentencia compuesta o bloque, que a partir de ese momento se puede considerar sintácticamente equivalente a una simple

if-else

La expresión if-else permite elegir si se ejecutará la siguiente o siguientes sentencias en función de un valor booleano

Tiene la siguiente sintáxis:

Primero se evalúa la expresión booleana y en caso de que su resultado sea igual TRUE se ejecutara el bloque de sentencias a continuación del if

En caso de que su resultado sea igual FALSE se ejecutara el bloque de sentencias a continuación del else

else es opcional y podemos no usarlo, aunque si queremos tener todas las opciones cubiertas, es recomendable usarlo

Dentro de un bloque de sentencias pueden incluirse if-else adicionales, a esta práctica se la conoce como anidamiento y es muy útil para seguir añadiendo condiciones de comprobación al código

En el ejemplo se han comparado los valores de los enteros contenidos en las variables a, b y c, utilizando if-else anidados, mostrando al usuario el valor más alto por pantalla

else-if

La expresión else-if es una mejora a la expresión if-else, su funcionamiento es similar, sin embargo permite disponer de una selección multiple sin recurrir al anidamiento, aunque dentro del bloque de sentencias seguirá siendo posible realizar anidaciones

Tiene la siguiente sintáxis:

else es opcional y podemos no usarlo, aunque si queremos tener todas las opciones cubiertas, es recomendable usarlo

El código de este ejemplo es muy similar al anterior usado con if-else, sintácticamente son equivalentes, pero se ha conseguido reducir un poco el número de líneas de código

switch

Dado que la selección múltiple es una construcción muy habitual, es más recomendable usar el switch

Pero hay que tener en cuenta que sólo funciona con expresiones enteras, no podemos usar cualquier expresión booleana como con if-else o else-if

Tiene la siguiente sintáxis:

Primero se evalúa la expresión entera y se comprueba si es igual al valor contenido en el primer case, en caso de que su resultado sea igual TRUE se ejecutara el bloque de sentencias a continuación de ese case

La ejecución no finalizará hasta encontrar un break, con lo que finalizaría la ejecución de la expresión switch

break puede omitirse y entonces pasaría a comprobar el siguiente case

En caso de que ningún case tenga valor TRUE, entonces funcionaría de forma similiar al else, ejecutándose el bloque de sentencias a continuación del default

En el ejemplo se ha comprobado qué día de la semana es el valor del entero mostrando el nombre del día y en caso de ser sábado o domingo, mostrando que es un día no lectivo y fin de semana

Este ejemplo sólo funcionaría con valores del 1 al 5, ya que si introducimos cualquier otro valor, nos mostraría por pantalla el valor por defecto, es decir, que es fin de semana

Sentencias de salto

En ocasiones necesitamos salir incondicionalmente de alguna expresión y para ello usaremos las sentencias de salto

break

Esta expresión proporciona la opción de salir incondicionalmente de una expresión while, do-while, for, for-each o switch

En caso de que la expresión esté anidada, lo que hará es salir de la expresión más interna

Tiene la siguiente sintáxis:

Aunque sintácticamente es correcta, su uso hace que el código sea difícil de leer, puesto que está añadiendo un punto de salida no controlado por una expresión booleana

continue

Esta expresión es similar a la expresión break, sin embargo, es menos agresiva

Rompe la ejecución del bloque de sentencias pero se vuelve a evaluar la expresión booleana

No puede usarse en un switch, ya que no tendría efecto alguno

En caso de que la expresión esté anidada, lo que hará es salir de la expresión más interna

Tiene la siguiente sintáxis:

Sentencias de iteración

En ocasiones necesitamos ejecutar un bloque de sentencias un determinado o indeterminado número de veces, a estas sentencias se las denomina bucles

while

Esta expresión repite un bloque de sentencias mientras se cumpla una expresión booleana

Tiene la siguiente sintáxis:

Dentro del bloque de sentencias debemos asegurarnos de que se modifica de alguna manera el valor de la expresión booleana, ya que sino podemos caer en un bucle que no termina nunca y que se denomina bucle infinito

Esta situación no es deseable, ya que nuestro programa quedará colgado y es probable que el usuario no sabría qué hacer en esa situación, ya que el programador no se ha molestado en controlarla

En el ejemplo se recorren los números del 1 al 100 y se muestran al usuario por pantalla

Se ha empezado el contador en 0, porque es un convenio que se utilizaba para inicializar variables en C y se sigue utilizando en Java

Igual que utilizar los nombres de variable i, k o j para contadores

do-while

Esta expresión es similar al while, sin embargo siempre se ejecuta el bloque de sentencias al menos una vez y la comprobación de la condición se realiza después de esa ejecución

Tiene la siguiente sintáxis:

Dentro del bloque de sentencias debemos asegurarnos de que se modifica de alguna manera el valor de la expresión booleana, ya que sino podemos caer en un bucle infinito

También admite las expresiones break y continue

En el ejemplo se recorren los números del 1 al 100 y se muestran al usuario por pantalla

Hay que tener en cuenta que la primera vez no se evalúa la expresión booleana, sino fuera por esa situación, sería totalmente equivalente a la expresión while

for

Esta expresión es similar al while, sin embargo permite inicializar la variable que controlará el bucle

Tiene la siguiente sintáxis:

Dentro de la variación de la variable debemos asegurarnos de que se modifica de alguna manera el valor de la expresión booleana, ya que sino podemos caer en un bucle infinito

También admite las expresiones break y continue aunque se desaconseja su uso, ya que se considera que para esta expresión rompen el flujo normal del programa

En el ejemplo se recorren los números del 1 al 100 y se muestran al usuario por pantalla

Es totalmente equivalente a la expresión while, pero ahorra algunas líneas de código al incluir su propia inicialización y en este caso, un incremento de la variable fuera del bloque de expresiones

for-each

Esta expresión es similar al for pero está especializada en recorrer objetos que admitan la clase Iterator

Fue introducido en la versión 5 de Java

Tiene la siguiente sintáxis:

No es necesario asegurarnos de que se modifica de alguna manera el valor de la expresión booleana como en un for, ya que al basarse en la clase Iterator, el bucle terminará cuando no queden más elementos del objeto, impidiendo de esta forma caer en un bucle infinito

También admite las expresiones break y continue aunque se desaconseja su uso, ya que se considera que para esta expresión rompen el flujo normal del programa

En el ejemplo se recorre un array que contiene los números del 1 al 5 y se muestran al usuario por pantalla

En este caso se en ha recorrido un sencillo array, pero podrían recorrerse objetos más especializados (siempre que admitan la clase Iterator) como un ArrayList, un Vector, un Map, etc

Excepciones en Java

Excepciones en Java

Si una operación no puede completarse debido a un error, el programa deberá:

  • volver a un estado estable y permitir otras operaciones

  • intentar guardar el trabajo y finalizar

Esta tarea es difícil debido a que generalmente el código que detecta el error no es el que puede realizar dichas tareas por eso debe informar al que pueda manejarlo

La solución más habitual son los códigos de error

Java ofrece otra forma de tratar con los errores en condiciones excepcionales: las excepciones

Una condición excepcional es aquella que impide la continuación de una operación

No se sabe como manejarla, pero no se puede continuar

En Java se lanza una excepción para que alguien que sepa manejarla la trate en un contexto superior

En el ejemplo se comprueba de forma condicional si el fichero readme.txt existía en el sistema y en caso de no existir se lanza la excepción IOException que de momento está sin tratar

La palabra reservada throw finaliza el método actual y lanza un objeto que facilite información sobre el error ocurrido

Normalmente se utilizará una clase para cada tipo de error

Java obliga a que un método informe de las excepciones explícitas que pueda lanzar

Un método no solo tiene que decir que devuelve si todo va bien; debe indicar también qué puede fallar

En el ejemplo se han declarado las excepciones EOFException y FileNotFoundException que afectaban al método fichero

La especificación de excepciones se realiza mediante la palabra throws en la declaración del método y puede haber tantas como necesitemos separadas por un espacio

Manejo de la excepciones

Una vez detectado el error hace falta indicar quien se encarga de tratarlo

Un manejador de excepciones tiene el siguiente formato:

El bloque try delimita el grupo de operaciones que puede producir excepciones

El bloque catch es el lugar al que se transfiere el control si alguna de las operaciones produce una excepción

En el ejemplo se ha usado un bloque try con su bloque catch para gestionar los errores del ejemplo anterior

Si alguna de las operaciones del bloque produce una excepción, se interrumpe el bloque try y se ejecuta el catch

Al finalizar este, se continua normalmente

Si no se produce ninguna excepción, el bloque catch se ignora

También existe la posibilidad de utilizar la excepción genérica Exception, pero es más recomendable usar el tipo de excepción específica para el error

En el ejemplo se ha usado un bloque try con su bloque catch para gestionar varias excepciones como en el ejemplo anterior

Un bloque try puede tener varios catch asociados

Cuando una excepción no se corresponde con ningún catch se propaga hacia atrás en la secuencia de invocaciones hasta encontrar un catch adecuado

En el ejemplo se gestionan las excepciones de los métodos f1 y f2 y si no encuentran ninguna continua la ejecución del código de forma normal fuera del bloque try

En este ejemplo se han tratado de mostrar las 3 alternativas que podemos usar para tratar la excepción IOException desde el método f2

Cuando se invoca un método que lanza excepciones es obligatorio:

  • que se maneje el error con un bloque catch

  • que se indique mediante throws su propagación

Bloque finally

Puede haber ocasiones en que se desea realizar alguna operación tanto si se producen excepciones como si no

Dichas operaciones se pueden situar dentro de un bloque finally

Un bloque finally tiene el siguiente formato:

Si no hay ninguna excepción se ejecutará el bloque try y el finally

En el ejemplo se ha controlado el cierre del archivo f en el bloque finaly, ejecutándose siempre

Si se produce alguna excepción:

  • Si es atrapada por un catch del mismo try se ejecuta este y luego el finally

    La ejecución continua después del finally normalmente

  • Si no es atrapada se ejecuta el finally y la excepción se propaga al contexto anterior

Jerarquía de las excepciones

Toda excepción debe ser una instancia de la clase de Throwable

De ella hereda los siguientes métodos:

  • getMessage()

    devuelve una cadena con un mensaje que detalla de detalla excepción

  • toString()

    devuelve una descripción breve que es el resultado es concatenar:

    • el nombre de la clase de este objeto
    • el token :
    • el resultado del método getLocalizedMessage() de este objeto
  • fillInStackTrace()

    registra información sobre el estado actual de los marcos de pila para el subproceso actual

    Si el estado de la pila no se puede escribir, no habrá valor devuelto

  • printStackTrace()

    devuelve la traza que ha seguido la excepción

    La primera línea de salida contiene el resultado del método toString() de este objeto

    Las líneas restantes representan los datos registrados previamente por el método fillInStackTrace()

La base de la jerarquía de excepciones de Java es:

  • Throwable

    • Error

    • Exception

      • RuntimeException

Las clases derivadas de Error describen errores internos de la JVM:

  • no deben ser lanzados por las clases de usuario

  • estas excepciones rara vez ocurren y cuando así sea lo único que se puede hacer es intentar cerrar el programa sin perder los datos

  • Ejemplos: OutOfMemoryError; StackOverflow; etc

Los programas en Java trabajarán con las excepciones de la rama Exception

Este grupo se divide a su vez en:

  • Las clases que derivan directamente de Exception (explícitas)

  • Las que derivan de RuntimeException (implícitas)

Se utilizan las RunTimeException para indicar un error de programación

Si se produce una excepción de este tipo hay que arreglar el código

Ejemplos:

  • Un cast incorrecto

  • Acceso a un array fuera de rango

  • Uso de un puntero null

El resto de Exception indican que ha ocurrido algún error debido a alguna causa ajena al programa

Ejemplos:

  • Un error de E/S

  • Error de conexión

Declaración de excepciones

Los métodos deben declarar sólo las excepciones explícitas

Las implícitas no deben declararse (aunque pueden producirse igualmente)

Por tanto cuando un método declara una excepción avisa de que puede producirse dicho error además de cualquier error implícito

Creación de excepciones

Si se necesita gestionar algún error no contemplado por los estándar que dispone Java, podemos crear nuestra propia clase de excepción

La única condición es que la clase derive de Throwable o de alguna que derive de ella

En la práctica generalmente se derivarán:

  • de RunTimeException si se desea notificar un error de programación

  • de Exception en cualquier otro caso

Resumen

Excepciones explícitas Excepciones implícitas
Derivan de Exception (no de RunTimeException) Derivan de RunTimeException
Indican un error externo a la aplicación Indican un error de programación
Si se lanzan es obligatorio declararlas No se declarar: se corrigen
Si se invoca un método que las lanza es obligatorio atraparlas o declararlas

Se pueden atrapar

En caso contrario, finaliza la aplicación

Excepciones y herencia

Al redefinir un método se está dando otra implementación para un mismo comportamiento

El nuevo método sólo podrá lanzar las excepciones declaradas en el método original

En el ejemplo se ha redefinido el método f de la clase A en la clase B con una excepción adicional

Un método puede declarar excepciones que no lance realmente

Así un método base puede permitir que las redefiniciones puedan producir excepciones

Los constructores, al no heredarse, pueden lanzar nuevas excepciones

Normas de uso

  1. Norma

    • Si una excepción se puede manejar no debe propagarse

    • Es más cómodo usar métodos que no produzcan errores

  2. Norma

    • No utilizar las excepciones para evitar una consulta

      • No abusar de ellas

  3. Norma

    • Separar el tratamiento de errores de la lógica

      Todo junto
      Separado
  4. Norma

    • No ignorar una excepción ya que dejamos el código con errores sin controlar

Flujos de E/S en Java

Flujos de E/S

Los flujos de E/S en Java se realizan a través de secuencias ordenadas de bytes (streams)

Estos pueden ser de entrada (InputStream) o de salida (OutputStream) independizando a los objetos de los datos

Las clases de E/S se encuentran en el paquete java.io

Los métodos de OutputStream y de InputStream lanzan la excepción IOException ante cualquier fallo relacionado con el intento de lectura/escritura

La clase OutputStream

Clase abstracta de la que derivan los demás flujos de salida

Fundamentalmente ofrece los siguientes métodos:

  • public abstract void write(int b) throws IOException
  • public void write(byte b[]) throws IOException
  • public void write(byte b[], int off, int len) throws IOException
  • public void flush() throws IOException

    Fuerza la escritura

  • public void close() throws IOException

Hay varias clases que derivan de OutputStream y redefinen el método write para darle una implementación distinta:

  • FileOutputStream

    redefine el método write para enviar bytes a un fichero

  • ByteArrayOutputStream
  • PipedOutputStream
  • Sun.net.TelnetOutputStream

FilterOutputStream

Un filtro de salida es un OutputStream al cual se le asocia en su constructor otro OutputStream (un patrón decorador)

Los filtros reenvían toda la información que reciben a su OutputStream asociado, previa realización de algún tipo de transformación en la misma

De esta manera cada filtro añade una funcionalidad adicional al OutputStream que encapsula

Se pueden encadenar varios filtros para obtener varias funcionalidades combinadas

En el ejemplo se ha cargado el archivo datos.dat para escritura y utilizando el método write se han guardado varios valores enteros que se almacenaban en la variable i

Se le añade & 0xFF al final del flujo de escritura para indicar dentro del fichero, que se ha terminado de escribir un dato

Filtro DataOutputStream

Los métodos de la clase OutputStream solo permiten enviar bytes

Cualquier otro tipo deberá descomponerse en una secuencia de bytes antes de poder escribirse

Entre los métodos más destacados:

  • void writeBytes(String s) throws IOException
  • void writeBoolean(boolean v) throws IOException
  • void writeShort(int v) throws IOException
  • void writeChar(int v) throws IOException
  • void writeInt(int v) throws IOException
  • void writeLong(long v) throws IOException
  • void writeFloat(float v) throws IOException
  • void writeDouble(double v) throws IOException
  • void writeChars(Strings s) throws IOException

    Casi no usado, es más usado el filtro PrintStream

En el ejemplo se han usado dos filtros, el FileOutputStream visto anteriormente y el DataOutputStream visto ahora

La clase DataOutputStream añade la funcionalidad de poder enviar todos los tipos primitivos directamente

Así que podremos escribir en el fichero cualquier tipo primitivo e incluso atributos de un objeto, siempre que sean tipos primitivos, ya que no escribe el objeto completo

Filtro PrintStream

La clase PrintStream añade la funcionalidad de poder enviar todos los tipos primitivos en formato de texto

En el ejemplo se han usado dos filtros, el FileOutputStream visto anteriormente y el PrintStream visto ahora

Los métodos print y println están sobrecargados para todos los tipos primitivos

System.out es de tipo PrintStream y por eso no es necesario referenciarlo al usar este filtro

La forma más habitual de usar OutputStream será combinando varios filtros

La clase InputStream

Clase abstracta de la que derivan los demás flujos de entrada

Fundamentalmente ofrece los siguientes métodos:

  • public abstract int read() throws IOException
  • public int read(byte b[]) throws IOException
  • public int read(byte b[], int off, int len) throws IOException
  • public int available() throws IOException
  • public void close() throws IOException

Hay varias clases que derivan de InputStream y redifinen el método read para darle una implementación distinta:

  • FileInputStream

    redefine el método read para leer bytes de un fichero

  • ByteArrayInputStream
  • StringInputStream
  • SequenceInputStream
  • PipedInputStream
  • Sun.net.TelnetInputStream

FilterInputStream

Un filtro de entrada es un InputStream al cual se le asocia en su constructor otro InputStream

Los filtros devuelven la información que a su vez han leído de su InputStream asociado, previa realización de algún tipo de transformación en la misma

De esta manera cada filtro añade una funcionalidad adicional al InputStream básico

Se pueden encadenar varios filtros para obtener varias funcionalidades combinadas

En el ejemplo se ha cargado el archivo datos.dat para lectura y utilizando el método read se han leido varios valores enteros que se almacenaban en la variable ch en las posiciones i + 1 y los hemos mostrado por pantalla

Así hemos podido leer el archivo que escribimos con el ejemplo de FileOutputStream

Filtro DataInputStream

Los métodos de la clase InputStream solo permiten leer bytes

Cualquier otro tipo deberán leerse como una secuencia de bytes y recomponerse antes de poder utilizarse

Fundamentalmente ofrece los siguientes métodos:

  • readBoolean()
  • readChar()
  • readByte()
  • readShort()
  • readInt()
  • readLong()
  • readFloat()
  • readLong()
  • readFloat()
  • readDouble()
  • readLine()
  • readFully(byte[] b)

En el ejemplo se han usado dos filtros, el FileInputStream visto anteriormente y el DataInputStream visto ahora

La clase DataInputStream añade la funcionalidad de poder recibir todos los tipos primitivos directamente

Lo que nos ha permitido mostrar datos primitivos contenidos en el fichero directamente en la pantalla

Así que leer del fichero cualquier tipo primitivo e incluso atributos de un objeto, siempre que sean tipos primitivos, ya que no lee el objeto completo

La forma más habitual de usar InputStream será combinando varios filtros

Utilidades en Java

Utilidades

Existe un conjunto de utilidades dentro del lenguaje Java

Se encuentran en el paquete java.util

Son clases útiles y de uso cotidiano, entre las que destacamos:

  • Colecciones
    • Collection
      • List
      • Set
      • Map
  • Fechas
    • Date
    • Calendar
  • Properties
  • Scanner

Collection

El API de colecciones de Java incluye clases que son capaces de albergar conjuntos de objetos

Representan contenedores de objetos

Son estructuras dinámicas, crecen a medida que se añaden elementos y trabajan con Objects

  • Se puede añadir cualquier cosa

  • Al recuperar el elemento se debe hacer casting

Básicamente hay dos conjuntos:

  • Clases que implementan Collection

    • Representan contenedores de objetos

    • Básicamente hay listas (List) y conjuntos (Set)

  • Clases que implementan Map

Algunos de sus métodos más importantes son:

  • boolean add(Object o)

    añade un elemento a la colección

  • boolean addAll(Collection c)

    añade un grupo de objetos (otra colección) a la colección

  • void clear()

    vacía la colección

  • boolean contains(Object o)

    chequea si un elemento se encuentra dentro de la colección

  • boolean isEmpty()

    chequea si la colección está vacía

  • Iterator iterator()

    devuelve un java.util.Iterador para recorrer los elementos de la colección

  • boolean remove(Object o)

    quita un elemento de la colección

  • int size()

    número de objetos almacenados

  • Object[] toArray(Object[] a)

    devuelve un array con los elementos de la colección

List

Sirve como interface para clases que representan listas de datos o vectores dinámicos

Sus elementos son accesibles por medio de un indice

Hereda los métodos de Collection y además añade:

  • void add(int index, Object element)

    añade un elemento en una posición determinada

  • Object get(int index)

    devuelve el elemento que ocupa una posición determinada

  • int indexOf(Object o)

    devuelve el indice de la primera aparición del elemento en la lista (o -1 si no lo encuentra)

  • Object remove(int index)

    elimina el elemento que ocupa una posición determinada

  • Object set(int index, Object element)

    cambia el elemento que ocupa una posición determinada por otro elemento

Al añadir un objeto con add(Object), el elemento se añade al final

Al eliminar un objeto con remove(Object), todos los demás se desplazan para no dejar huecos

Los objetos List más habituales son Vector (es threadsafe) y ArrayList (no es threadsafe)

Set

Sirve como interface para clases que representan conjuntos de datos

No pueden tener elementos duplicados

Sus elementos no son accesibles por un indice

No añade métodos nuevos

Los objetos Set más habituales son HashSet y TreeSet

Map

Representan tablas de datos

Todo mapa se compone de un conjunto de entradas compuestas de:

  • Una clave, sirve para recuperar el elemento
  • Un valor

Los métodos más importantes son:

  • void clear()

    vacía la estructura

  • boolean containsKey(Object key)

    devuelve true si hay algún objeto almacenado con esa clave

  • boolean containsKey(Object value)

    devuelve true si el Map contiene ese objeto

  • Object get(Object key)

    permite recuperar un determinado elemento a partir de su clave

  • boolean isEmpty()

    indica si el Map está vacío

  • Set keySet()

    devuelve un conjunto de claves del Map

  • Object put(Object key, Object value)

    almacena un objeto en el mapa con una clase determinada. Si la clave ya esta almacenada se sustituye por un objeto asociado por el nuevo

  • void putAll(Map t)

    incluye un submapa en el mapa

  • Object remove(Object key)

    elimina un objeto de clave determinada

  • int size()

    devuelve el número de objetos almacenados

  • Collection values()

    devuelve el conjunto de valores almacenados en el mapa

Se implementan como tablas Hash

Los más habituales son HashMap (no es threadsafe) y Hashtable (es threadsafe)

Iterator

Hay colecciones a las que se accede por medio de un índice, otras no

Iterator permite recorrer colecciones de objetos independientemente de cómo estén organizadas

Otra ventaja, permite eliminar objetos sin tener que gestionar los indices

Los métodos más importantes son:

  • boolean hasNext()

    devuelve True si el iterador tiene algún elemento más

  • Object next()

    devuelve el siguiente elemento de la iteración

  • void remove()

    elimina de la colección el último elemento devuelto por el iterador

Comparación de recorrido de una Colección
for con índice
Iterador

Fechas

Hay dos clases que permiten trabajar con fechas:

  • Date

    casi todos sus métodos están en desuso por lo que no es muy recomendable su uso

  • Calendar

    es una interface por lo que es muy fácil darle el comportamiento que queramos

Ambas representan internamente el tiempo mediante un valor de tipo long que representa el número de milisegundos desde la el 1 de Enero de 1970 a las 00:00:00 GMT

Ese valor puede ser obtenido usando System.currentTimeMillis()

Calendar permite muchas más operaciones con fechas que Date, aunque es más difícil de construir (al ser una interfaz deberemos hacer nosotros mismos su implementación)

Date

Es fácil de construir:

  • new Date()

    construye un objeto Date con la fecha actual del sistema

Métodos que no están en desuso:

  • boolean after(Date when)

    comprueba si la fecha es posterior a la entregada como parámetro

  • boolean before(Date when)

    comprueba si la fecha es anterior a la entregada como parámetro

  • long getTime()

    devuelve el número de milisegundos desde el 1 de Enero de 1970 a las 00:00:00 GTM

  • void setTime(long time)

    cambia la fecha por otra fecha en milisegundos

Métodos en desuso:

  • int getDay()

    devuelve el día de la semana para esa fecha o devuelve el día que representa ese instante en la zona local

    Día de la semana
    Día Domingo Lunes Martes Miércoles Jueves Viernes Sábado
    Valor 0 1 2 3 4 5 6
  • getMonth()

    devuelve el mes para esa fecha empezando por 0

    Meses
    Mes Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre Noviembre Diciembre
    Valor 0 1 2 3 4 5 6 7 8 9 10 11
  • getYear()

    devuelve el año para esa fecha a la que se le ha restado 1900

  • getHours()

    devuelve la hora para esa fecha, la cuál estará comprendida entre 0 y 23

  • getMinutes()

    devuelve los minutos para esa fecha, los cuales estarán comprendidos entre 0 y 59

  • getSeconds()

    devuelve los segundos para esa fecha, los cuales estarán comprendidos entre 0 y 61, los valores 60 y 61 sólo aparecerán cuando la MVJ se salte algunos segundos en una cuenta

Calendar

Clase abstracta para representar fechas en distintos tipos de calendarios

Incluye un montón de constantes para todos los días de la semana, meses, campos de una fecha…

En occidente para obtener la fecha del sistema utilizaremos el GregorianCalendar:

  • new GregorianCalendar()
  • new GregorianCalendar(2007, 1, 5)

    Hay que prestar atención a la fecha:

    • es año, mes, día

    • Corresponde al 5 de Febrero de 2007 y no al 5 de Enero de 2007 como podría parecer a primera vista

      Enero corresponde al valor 0

También podemos obtener la fecha del sistema mediante el método estático Calendar.getInstance()

Los métodos más destacable son:

  • void add(int field, int amount)

    suma (o resta, según el signo del operando) tiempo a una fecha

  • boolean after(Object when)

    comprueba si la fecha es posterior a la entregada como parámetro

  • boolean before(Object when)

    comprueba si la fecha es anterior a la entregada como parámetro

  • int get(int field)

    devuelve el valor de algún campo de la fecha, el cual especificamos con el parámetro field

  • Date getTime()

    convierte el tipo Calendar a Date

  • void set(int field, int value)

    cambia de valor un campo de la fecha

  • void set(int year, int month, int date)

    cambia el año, el mes y el día de la fecha

  • void setTime(Date date)

    a partir de un Date, crea un Calendar

Properties

Habitualmente el comportamiento de las aplicaciones puede cambiar en función de parámetros

Es muy cómodo colocar esos parámetros de configuración en ficheros para que la aplicación los lea según los necesite

Java dispone de la clase Properties que automatiza esta gestión

Cada línea del fichero de propiedades guarda una variable y tiene el formato:

Vamos a usar el fichero llamado conexion.properties que tendrá el siguiente contenido:

Para mostrar el uso de Properties con el siguiente ejemplo:

Scanner

Se puede utilizar la clase Scanner para leer tipos primitivos y Strings usando expresiones regulares

La clase Scanner recoge los caracteres pulsados desde el teclado hasta encontrar un delimitador que por defecto es el espacio

Los datos recogidos son convertidos a los diferentes tipos usando el método next<tipo>(), para recoger textos como String usaremos sólamente next()

De esta forma podremos recoger del teclado o de otros medios, los tipos de datos que necesitemos

En el ejemplo hemos leído un int desde el teclado

En el ejemplo hemos leído varios long desde un fichero llamado numeros.txt

En el ejemplo hemos leído varias palabras desde un String

Mostrándonos por pantalla:

Es decir, las palabras que contenía el String, cada una en una línea cada vez porque usamos println para mostrarlas por pantalla

Como podemos ver, si en cambio, hubiésemos utilizado print las habría mostrado todas en la misma línea sin ningún delimitador entre ellas

En el ejemplo hemos leído varias palabras desde un String cambiando el delimitador espacio por el delimitador 1

Para definir el nuevo delimitador usamos el método useDelimiter y le pasamos de parámetro una cadena que contiene una expresión regular, en este caso \\s*1\\s*

Mostrando por pantalla lo mismo que en el ejemplo anterior a pesar de usar un delimitador distinto

JDBC

JDBC

JDBC es un conjunto de clases e interfaces Java para la ejecución de sentencias SQL

Es la parte CLI de Java (Call-Level Interface)

Fue desarrollado conjuntamente por JavaSoft, Sybase, Informix e IBM entre otros

JDBC permite la manipulación de cualquier base de datos SQL

No es necesario hacer un programa específico para manipular Oracle, Sybase, etc…

Nuestro programa puede manipular cualquier base de datos

Uniendo Java con JDBC obtenemos programas que se pueden ejecutar en cualquier plataforma y que pueden manipular cualquier base de datos

Las clases e interfaces JDBC se encuentran dentro del paquete java.sql

Proceso de trabajo con JDBC

El proceso de trabajo en JDBC consiste en los siguientes pasos:

  1. Conectarse a la base de datos (mediante DriverManager)

  2. Emitir sentencias SQL (mediante Statement, PreparedStatement, CallableStatement)

  3. Procesamiento de los resultados (ResultSet)

Driver JDBC

Cada base de datos concreta implementa la interface JDBC de una manera particular, al igual que las clases auxiliares y de utilidad que sean necesarias para esa base de datos

Por eso son necesarios los drivers de bases de datos

Una misma aplicación podrá conectar con distintas bases de datos simplemente cambiando ese driver (no debemos confundir el driver de la base de datos con la clase Driver, pues son cosas distintas)

El interface Driver especifica los métodos que todo driver JDBC debe implementar, para ello se pueden cargar los drivers de dos maneras:

  1. Cuando se inicia la clase Driver, el DriverManager consulta la propiedad jdbc.drivers

    Dicha propiedad contiene una lista de drivers (las clases) que deben ser cargadas

    Para ello deberá ejecutarse un comando como este (para usar ODBC) en la línea de comandos:

  2. Si se desea introducir un nuevo driver después de que el DriverManager se halla inicializado se deberá usar el método forname de la clase Class

    Para ello deberemos incluir en nuestro código la siguiente instrucción (para usar ODBC):

    Es aconsejable que se use de forma static en nuestra aplicación (porque sólo se cargará el driver una vez, al cargar la clase)

Ejemplos de drivers
Base de datos Driver
ODBC sun.jdbc.odbc.JdbcOdbcDriver
Oracle oracle.jdbc.driver.OracleDriver
SQLServer com.microsoft.jdbc.sqlserver.SQLServerDriver
MySQL com.mysql.jdbcDriver

Tipos de driver:

  • Puente JDBC-ODBC

    Traduce JDBC a ODBC y lo retransmite al driver ODBC de la máquina

    Es el driver ODBC el que realmente se comunica con la base de datos

    Está incluido en el JDK pero no incluye JDBC2

    Inconvenientes:

    • Es útil para realizar pruebas, pero es lento en producción

    • Es necesario el driver ODBC en el cliente (menor portabilidad)

  • Driver JDBC sobre driver nativo de la BD

    Retransmite JDBC al driver nativo instalado en la máquina

    El driver nativo es el que realmente se comunica con al base de datos

    Inconveniente:

    • necesidad de driver nativo (menor portabilidad)
  • Driver Java sobre red

    Traduce las llamadas JDBC a un protocolo de red independiente de la plataforma que contacta con el servidor

    El servidor traduce esas peticiones al protocolo concreto de cada base de datos

    Se usa un middleware en el servidor de red que es capaz de conectar a los clientes puros Java a muchas bases de datos diferentes

    Ventaja:

    • es rápido, independiente de la plataforma y no requiere de instalación en el cliente

  • Driver puro Java con protocolo nativo

    Traduce las llamadas JDBC al protocolo específico de la base de datos contactando directamente con ella

Obtener conexiones

Mediante el driver concreto realizaremos las conexiones, pero las solicitudes de conexión deben ser realizadas mediante el DriverManager

Una vez que la clase Driver ha sido cargada y registrada, el DriverManager puede establecer conexiones con la base de datos mediante estos dos pasos:

  1. Se llama al método DriverManager.getConnection(url, user, pass); y se obtiene un objeto de tipo Connection

  2. El DriverManager prueba los drivers registrados para ver si puede establecer la conexión y si no fue posible, lanza una SQLException

Una misma aplicación, puede tener varias conexiones a la misma base de datos o varias conexiones a otras bases de datos (hasta el máximo permitido por la base de datos)

Los parámetros que admite getConnection son la url (que es un subprotocolo que utiliza la base de datos para realizar la conexión), el user (el nombre de usuario que va a conectarse) y el pass (la contraseña que utiliza el usuario para conectarse)

El parametro url tiene el siguiente formato:

El subprotocolo es particular de cada base de datos y lo utiliza el DriverManager para buscar el driver adecuado para manipularla

El subnombre depende del subprotocolo concreto

El driver será el encargado de interpretarlo y le ayudará a localizar la base de datos

Nuestra aplicación no trabajará con el driver concreto, sino que lo hará el DriverManager

De esta manera las aplicaciones pueden trabajar con el objeto Connection sin preocuparse por el tipo de base de datos con la cual estemos trabajando (no hay que hacer modificaciones en el código, solo cambiar el parámetro url)

La conexión debe cerrarse siempre al finalizar, porque sino se consumen recursos de forma innecesaria, aunque se pueden reutilizar conexiones, no es aconsejable, es mejor usar siempre una conexión nueva

En el ejemplo se ha utilizado un esqueleto de conexión para Oracle

Para utilizar la conexión debemos crear un objeto de tipo Connection, el cual representa una sesión abierta con la base de datos

El objeto provee de un contexto con el que poder emitir sentencias SQL y obtener resultados

El objeto Connection debe inicializarse inicialmente a null, así podremos comprobar si hubo conexión porque incluiremos un bloque try para gestionar errores, el cuál lanzará SQLException

Para cerrar la conexión debe hacerse mediante el método close(), dentro de un finaly (el cual también tendrá su bloque try que lanza SQLException)

El interface Statement

La conexión nos permite crear objetos Statement mediante el método createStatemen()

Los objetos Statement permiten la ejecución de sentencias SQL y la obtención de resultados (mediante los objetos ResultSet)

Los métodos para ejecutar SQL aceptan String como parámetro (JDBC es CLI), los cuales pueden componerse dinámicamente en función de valores contenidos en variables

Para componer el String SQL habrá que concatenar los distintos fragmentos SQL (los estáticos y las variables)

Hay tres formas de ejecutar sentencias SQL:

  1. Mediante el método executeQuery(<sql>) para consultas mediante la sentencia SELECT que produce túplas como resultado (devuelve un objeto de tipo ResultSet)

    • Es como una tabla que almacena el resultado de la consulta

    • Tiene una serie de métodos de consulta

    • Es como un cursor (en terminología PL / SQL)

  2. Mediante el método executeUpdate(<sql>) para actualizaciones mediante las sentencias INSERT, DELETE, UPDATE, así como comandos DDL (CREATE, DROP, ALTER TABLE, ADD) y bloques PL / SQL (entre los bloques begin y end)

    • Devuelve un entero que indica el número de filas modificadas por el comando

    • Con los comandos DDL devuelve 0

  3. Mediante el método execute(<sql>) que ejecuta cualquier sentencia SQL

    • Si se utilizó un QUERY, devolverá True

      • El ResultSet se puede obtener mediante el método getResultSet()

    • Si se utilizó un UPDATE, devolverá False

      • El total de filas modificadas se pueden obtener mediante el método getUpdateCount()

Después de procesar el resultado, se debe cerrar el Statement mediante el método close()

Este método cierra también el ResultSet asociado, de todas formas, Sun recomendaba cerrar el ResultSet de forma explícita para evitar errores no deseados

Un mismo Statement puede reutilizarse en una misma conexión para ejecutar distintas sentencias

El interface ResultSet

El objeto ResultSet actúa como un cursor dentro de los resultados

La primera vez que se lee apunta encima del primer resultado, pero no lo lee, por eso hay que leer la primera fila avanzando mediante el método next()

El método next() devuelve True si pudo avanzar o False si no pudo

Para obtener los valores del ResultSet se utilizan los métodos get que tienen el siguiente formato get<tipo>(<columna>)

Para nombrar las columnas podemos o hacerlo por su nombre o por un indice, el cual empieza en 1, su numeración viene dada por el orden en que fue introducida en el SELECT

Hay que prestar atención a las fechas, porque se guardan como java.sql.Date, no como java.util.Date como se podría esperar

Conviene cerrar el ResultSet aunque se cierra implícitamente al cerrar o reutilizar el Statement que lo creó

Cuando se ha leído un null de SQL usando uno de los métodos get<tipo>, éste devuelve:

  • Un valor null de Java para aquellos métodos que devuelven objetos Java (getString(), getBigDecimal(), getDate(), getTime(), getTimestamp(), getObject(), etc)

  • Un valor 0 para aquellos métodos que devuelven tipos numéricos (getByte(), getShort(), getInt(), getLong(), getFloat(), getDouble() )

  • Un valor False para el método getBoolean()

Para determinar si un valor dado era null, primero debe intentar leerse la columna y usar el método de ResulSet wasNull() para saber si fue null

Devolverá True si lo era, False en caso contrario

El interface PreparedStatement

Cada vez que se lanza un Statement la base de datos debe interpretarla y calcular un plan de consulta

Pero al usar PreparedStatement, se puede ejecutar múltiples veces, obteniendo un aumento de rendimiento al tener la consulta ya analizada y optimizada

No todas las bases de datos soportan PreparedStatement, hay que leer la documentación de la misma para saber si se pueden usar

Los objetos PreparedStatement derivan del Statement obtenido de la conexión, mediante el método prepareStatement(<sql>);

Un PreparedStatement sirve para lanzar instrucciones SQL precompiladas, podremos parametrizar una o más entradas mediante el operador ?, cuyos valores podrán ser modificados en distintas ejecuciones de la instrucción

Parametrizar las entradas es útil cuando desconocemos los valores de ciertos tipos de datos SQL en la base de datos de destino,

El valor parametrizado obtiene el valor correcto del driver de la base de datos, por lo que no deberemos preocuparnos por los tipos

Antes de que la instrucción SQL pueda ser ejecutada deberemos asignarle un valor a los parámetros de entrada

Para asignar valores usaremos los métodos set<tipo>(columna, valor); siendo el tipo compatible con el del parámetro

Para ejecutarlo se usan los métodos execute de Statement

En el ejemplo se ha utilizado un PreparedStatement para Oracle

Javascript

Javascript

JavaScript, al igual que Java o VRML, surgió para extender las capacidades del lenguaje HTML y conseguir páginas web dinámicas

JavaScript no es un lenguaje de programación propiamente dicho

Es un lenguaje script u orientado a documento, como pueden ser los lenguajes de macros que tienen muchos procesadores de texto. Nunca podrás hacer un programa escrito en JavaScript, tan sólo podrás mejorar tu página Web insertando código javascript en la misma

Utilidad de JavaScript

JavaScript sirve principalmente para mejorar la gestión de la interfaz cliente / servidor. Un script JavaScript insertado en un documento HTML permite reconocer y tratar localmente, es decir, en el cliente, los eventos generados por el usuario. Estos eventos pueden ser el recorrido del propio documento HTML o la gestión de un formulario

Por ejemplo: cuando la página HTML es un formulario que permite acceder a una agenda telefónica, se puede insertar un script que verifique la validez de los parámetros proporcionados por el usuario. Esta prueba se efectúa localmente y no necesita un acceso a la red

Por otro lado, también se podrá utilizar JavaScript para efectuar varias opciones a la vez; por ejemplo, visualizar dentro de un documento HTML un vídeo o ejecutar un applet de Java…

Diferencias con Java

La principal diferencia entre JavaScript y Java, es que este último es un lenguaje de programación completo. Aunque comparten la misma sintaxis

JavaScript es un lenguaje que se integra directamente en las páginas HTML. Tiene como características principales las siguientes:

  • Es interpretado (no compilado) por el cliente, es decir, se ejecuta directamente el programa fuente, al contrario que en los lenguajes compilados, no se genera ni código objeto ni ejecutable para cada ordenador en el que se quiera ejecutar dicho programa
  • Está basado en objetos. No es, como Java, un lenguaje de programación orientada a objetos (OOP). JavaScript no emplea ni clases ni herencia, ni otras técnicas típicas de la OOP
  • Su código se integra en las páginas HTML, incluido en las propias páginas
  • No es necesario declarar los tipos de variables que van a utilizarse, JavaScript realiza una conversión automática de tipos
  • Las referencias a objetos se comprueban en tiempo de ejecución. Esto es consecuencia de que JavaScript no es un lenguaje compilado
  • No es posible trabajar directamente con ficheros, ni escribir automáticamente al disco duro. Por eso se dice que JavaScript es un lenguaje seguro para los usuarios de internet

Utilización de JavaScript en un documento HTML

La inserción de un documento HTML se realiza mediante la marca SCRIPT utilizando la sintaxis:

Los atributos de esta marca son:

  • type=“text/javascript”
    Precisa el lenguaje del script. Este atributo es obligatorio en ausencia del atributo SRC
  • src=url
    El atributo SRC precisa el URL del script a insertar en el documento. Este atributo es opcional, porque el script puede insertarse directamente en un documento HTML

Podrá especificarse para insertar en un documento un script de un lenguaje determinado y que cuyo código fuente se encuentra en un archivo especificado en un determinado url. Puede sernos útil si queremos que varias de nuestras páginas web compartan los mismos scripts sin tener que insertarlos en cada una, repitiendo el código

A continuación enunciaremos algunos puntos a tener en cuenta respecto a la introducción de JavaScript en un documento HTML:

  • El script insertado mediante la marca SCRIPT es evaluado por el cliente tras la visualización de la página HTML. Las funciones definidas no se ejecutan inmediatamente, dependen de los eventos asociados a la página
  • La inserción del script mediante la marca SCRIPT puede colocarse en cualquier lugar del documento HTML pero se recomienda colocarla en la cabecera, es decir, en la zona definida por el HEAD. De este modo, el script está definido desde el principio del documento, lo que garantiza que éste sea visible en todo el documento
  • Si se definen, además del script mediante el atributo SRC, scripts en el propio documento, el cliente evaluará en primer lugar el insertado mediante el atributo SRC y seguidamente los incluidos en el documento
  • Los URL correspondientes a un JavaScript poseen generalmente la extensión .js
  • Es preferible delimitar los scripts insertados en un documento por comentarios HTML para asegurarse de que el contenido del script no aparecerá en los clientes que no reconozcan la marca SCRIPT. Por ejemplo:
  • El lenguaje JavaScript no es case sensitive, es decir, no distingue mayúsculas de minúsculas salvo en las cadenas de caracteres literales

Por último, comentar otra forma de introducir scripts en documentos HTML, y es incluir estos script como controladores de eventos de algunas marcas, como pueden ser la marcas de imágenes, anclas, links, botones, etc. Veamos un ejemplo:

Ir al índice

Como puede verse, dentro de esta marca, como atributo de esta, se pone un controlador de eventos y después del signo igual y entre comillas se incluye el código de JavaScript. Ahora bien, también es posible llamar a una función desde el HEAD del documento. Se recomienda esta segunda opción ya que es una manera más limpia y clara de escribir páginas. Se conseguiría el mismo resultado que en el ejemplo anterior:

Las versiones de JavaScript

El lenguaje fue inventado por Brendan Eich en la empresa Netscape Communications. Apareció por primera vez en el Netscape Navigator 2.0. La cual incluía JavaScript bajo el nombre de Mocha, cuando apareció esta versión de Navigator se le llamaba LiveScript. Finalmente fue rebautizado como JavaScript en un anuncio conjunto entre Sun Microsystems y Netscape, el 4 de diciembre de 1995

Tradicionalmente, se venía utilizando en páginas web HTML, para realizar tareas y operaciones en el marco de la aplicación únicamente cliente, sin acceso a funciones del servidor. JavaScript se ejecutaba en el navegador del usuario al mismo tiempo que las sentencias iban descargándose junto con el código HTML. Lo que quería Netscape es que JavaScript fuera un lenguaje de guiones, fácil de usar y que cualquier persona pudiera utilizarlo. Después de 2 años JavaScript se convirtió en una de las herramientas más utilizadas por los desarrolladores web, utilizándose casi en el 90% de los desarrollos web, incluso más que Java y Activex

Netscape introdujo una implementación de script del lado del servidor con Netscape Enterprise Server, lanzada en diciembre de 1994 (poco después del lanzamiento de JavaScript para navegadores web)

En el año de 1996 Microsoft empezó a mostrar gran interés en competir con JavaScript por lo que lanzó el lenguaje llamado Jscript, que era la implementación de ECMAScript, muy similar al JavaScript de Netscape, pero con ciertas diferencias en el modelo de objetos del navegador que hacian a ambas versiones incompatibles. Pero la versión 4.0 del Internet Explorer soportaba sin ningún problema, la versión 1.1 de JavaScript. Sin embargo, Jscript no pudo competir directamente con Javascript

A mediados de 1997, Netscape promocionó JavaScript y lanzó la versión 1.2. Esta nueva versión incluía nuevos componentes que daban gran potencial al lenguaje, pero lamentablemente esta versión solo funcionaba en la última versión del Navigator. Los autores propusieron que fuera adoptado como estándar de «the European Computer Manufacturer’s Association» (ECMA, con la implementación ECMAScript), que a pesar de su nombre no era europeo sino internacional, con sede en Ginebra. Poco después también fue propuesto como un estándar ISO

La versión 1.3 fue una pequeña revisión de la 1.2, que fue incluida en la versión 4.07 del Netscape Navigator

Para evitar estas incompatibilidades, el World Wide Web Consortium diseñó el estándar Document Object Model (DOM, ó en castellano, Modelo de Objetos del Documento), que incorporan Konqueror, las versiones 6 de Internet Explorer y Netscape Navigator, Opera versión 7, y Mozilla desde su primera versión

A partir de mediados de la década de los 2000, ha habido una proliferación de implementaciones de JavaScript para el lado servidor. Node.js es uno de los notables ejemplos de JavaScript en el lado del servidor, siendo usado en proyectos importantes

La llegada de Ajax devolvió a JavaScript a la fama y atrajo la atención de muchos programadores. Como resultado de esto hubo una proliferación de un conjunto de frameworks y librerías de ámbito general, mejorando las prácticas de programación con JavaScript, y aumentado el uso de JavaScript fuera de los navegadores web, con la proliferación de entornos JavaScript del lado del servidor. En enero de 2009, el proyecto CommonJS fue inaugurado con el objetivo de especificar una librería para uso de tareas comunes principalmente para el desarrollo fuera del navegador web

En junio de 2015 se cerró y publicó el estándar ECMAScript 6 (última versión hasta la fecha) con un soporte irregular entre navegadores y que dota a JavaScript de características avanzadas que se echaban de menos y que son de uso habitual en otros lenguajes como, por ejemplo, módulos para organización del código, verdaderas clases para programación orientada a objetos, expresiones de fecha, iteradores, generadores o promesas para programación asíncrona

Los comentarios en JavaScript

Los comentarios en el código permiten insertar observaciones del autor para describir las distintas partes del programa. El intérprete de JavaScript los ignora y por ello poseen una sintaxis particular

Se distinguen los comentarios en una sola línea, precedidos por la barra oblicua doble // y los comentarios en varias líneas delimitados por los símbolos /* y por */. Por ejemplo:

Identificadores y palabras reservadas

Conocer cuál es la sintaxis de los identificadores y cuales son las palabras reservadas es algo necesario antes de empezar a escribir un programa en un lenguaje de programación determinado

Los identificadores de un lenguaje son la ristra de caracteres que le asignamos a los nombres de variables, constantes, funciones, objetos, etc…, que nosotros definimos en dicho lenguaje, estos son necesarios para poder invocar a dichos elementos en lugares posteriores a su definición

Los identificadores deben seguir las siguientes reglas:

  • El identificador debe empezar por una letra o por el carácter ‘_’
  • Los caracteres siguientes, además de letras o el carácter ‘_’, pueden ser cifras

Hay que tener en cuenta que el uso de mayúsculas o minúsculas no es importante, porque JavaScript no diferencia de los nombres de mayúsculas o minúsculas en los identificadores. Veamos algunos ejemplos de nombres de variables:

Primeramente decir que las palabras reservadas son palabras especiales que se utilizan para aumentar la legibilidad y separar las entidades sintácticas. Estas palabras no pueden usarse como identificadores

A continuación veremos un cuadro en el que se muestran todas las palabras reservadas existentes en JavaScript, estas palabras tienen o tendrán un significado especial dentro del lenguaje:

Palabras reservadas
Abstract
Boolean
Break
Byte
Case
Cath
Char
Class
Const
Continue
Default
Do
Double
Else
Extends
False
Final
Finally
Float
For
Function
Goto
If
Implements
Import
In
Instaceof
Int
Interface
Long
Native
New
Null
Package
Private
Protected
Public
Return
Short
Static
Super
Switch
Synchronized
This
Throw
Throws
Transient
True
Try
Var
Void
While
With