Java
Java
1. Lo primero es antes 3
1.1. Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2.1. Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.2. Declaración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.3. Inicialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.4. Ámbito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3. Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.1. Aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.2. De comparación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.3. Lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6. Cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.8. Condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.8.1. La instrucción if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.9. Bucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1
2.10. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3. Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.4.1. Paquetes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.5. Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.5.1. Constructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.8. Enumeraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.10. Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4. Aspectos avanzados 40
4.1. Clases internas (o anidadas) . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2. Applets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.3.2. Oyentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2
Tema 1
Lo primero es antes
1.1. Motivación
Cris, he estado pensando un rato y creo que aprender un lenguaje de programación
puede llegar a ser una tarea sencilla dijo de pronto el gran lósofo Leonardo Mesa ante
la estupefacción de todos los reunidos en aquella sala.
½Yo aún diría más, querido Leo! respondió su colega Cristino Leopardo, levantando
la jarra de cerveza sin alcohol con una mano y sujetando el sombrero con la otra ½Me
atrevería a armar que hasta puede resultar divertido!.
Sin duda, estas sabias palabras son ciertas, pero no puede faltar la siguiente coletilla:
siempre que se le dedique el esfuerzo necesario. Y es que, con un lenguaje de programación,
sucede igual que con cualquier idioma hablado, el único secreto para su aprendizaje consiste
en practicar lo suciente. La principal peculiaridad de Java es que se trata de un lenguaje
orientado a objetos, lo que implica estructurar nuestro pensamiento en base a clases y objetos,
y en cómo estos se relacionan entre sí en este sentido, destaca por encima de todos el
concepto de herencia .
3
Vamos a empezar con el típico programa con el que se inicia todo principiante y, para
ello, teclearemos en un editor de texto cualquiera, el siguiente código:
Lo primero que debemos tener en cuenta es que un programa codicado en Java dis-
tingue mayúsculas de minúsculas; hay que tener cuidado con eso.
Las instrucciones se ejecutan de manera secuencial y deben terminar con el símbolo ` '. ;
Los bloques código encerrado entre llaves denen el código asociado a ciertas
estructuras de Java y, normalmente, contienen más de una instrucción. Los bloques no
terminan con un ` '. ;
Cualquier programa en Java está formado por, al menos, una clase. Al principio debe-
remos escribir cada una de ellas en un chero aparte, cuyo nombre coincida con el de la
clase y su extensión sea .java. En nuestro ejemplo, solo existe una clase denominada
Hola y, por tanto, el chero debería llamarse [Link].
Todo programa para consola en Java debe contener una única clase que codique en
su interior el método main(). Esta función desempeña un papel destacado, ya que
constituye el punto de ejecución inicial del programa.
Para poder compilar y ejecutar un programa en Java, necesitamos el JDK (Java De-
velopment kit ), que se ditribuye gratuitamente. Una vez instalado, dispondremos del
comando javac, que permite compilar un chero fuente y generar un nuevo chero
con extensión .class. Se trata de un chero escrito en un lenguaje intermedio que
más adelante podrá ser interpretado por la JVM (Java Virtual Machine ) mediante el
comando java. De esta forma, se consigue que un programa escrito ½una única vez!
pueda ser ejecutado en cualquier equipo con la JVM instalada, independientemente
del microprocesador utilizado.
Por tanto, para compilar nuestro chero fuente y obtener el chero [Link], hay
que ejecutar el siguiente comando desde la consola:
4
$ javac Hola . java
$ java Hola
Si ha salido por consola el mensaje Hola, mundo , debéis sentiros llenos de orgullo y
satisfacción, vamos... ½como reyes!
5
Tema 2
2.1. Comentarios
Un comentario es una línea de texto (o un fragmento) que el programa ignora a la hora de
ejecutar el script. Se utiliza para explicar el código contenido en el script o para hacer pruebas
inhabilitando temporalmente alguna línea de código. Para crear un comentario simplemente
se escribe una doble barra justo en el punto de la línea donde queremos que comience a
ignorarse el texto hasta el nal de la misma. Pueden verse ejemplos de este tipo de comentario
en las líneas 6 y 8 del [Link].
Existe otro tipo de comentario que abarca un bloque de líneas consecutivas, escribiendo
los caracteres /* al inicio del bloque y */ al nal del mismo. Un ejemplo lo constituyen las
tres primeras líneas del algoritmo anterior (todas ellas se consideran comentarios).
La última variante de comentario que admite Java se conoce como Javadoc, y permite
documentar variables, métodos y clases (ya veremos qué son) de forma que esa información
sea visible con solo situar el ratón sobre dicho elemento en el propio código. Veámoslo con
un ejemplo:
2.2. Variables
Una variable es un nombre que se asigna a un conjunto de bytes consecutivos ubicados en
memoria RAM, cuyo contenido se puede modicar. En esencia, una variable es un almacén
6
temporal de datos. La cantidad de bytes que ocupa en RAM viene determinado por su tipo.
2.2.1. Tipos
Como acabamos de mencionar, el tipo de una variable delimita el espacio en RAM que
puede ocupar un dato almacenado en dicha variable. Los tipos de Java se pueden clasicar
en tres categorías, que pasamos a diseccionar en los siguientes apartados.
Simples
Los tipos de datos simples, también llamados básicos o primitivos, se describen en la
tabla que se muestra a continuación:
Tipo Descripción
boolean Ocupa un byte para almacenar true o false
byte Ocupa un byte. Almacena un entero entre -128 y 127
char Ocupa dos bytes. Almacena un carácter unicode
short Ocupa dos bytes. Entero entre -32768 y 32767
int Usa cuatro bytes para almacenar un entero
long Usa ocho bytes para almacenar un entero
oat Usa cuatro bytes para almacenar un número decimal
double Usa ocho bytes para almacenar un número decimal
void Se trata de un tipo especial que indica ausencia de tipo
En la siguiente gura vamos a ver cómo deberíamos visualizar dos variables simples, de
acuerdo con la representación RAM en forma de cajas que acabamos de ver.
La primera podría ser una variable de tipo byte que almacena un 5 en la posición de
memoria `X ' y que, por tanto, ocuparía un byte es decir, una caja de memoria.
7
La segunda podría estar representando a una variable de tipo short, ocuparía dos bytes y
estaría guardando un 83 en la posición `Y ' en la imagen, en lugar de dos cajas, se muestra
una más grande.
Arrays
Un array es un tipo de variable especial que agrupa un número de variables del mismo
1
tipo , almacenadas de manera consecutiva en memoria RAM. Se puede acceder de forma
individual a cada una de las variables del grupo, a través del nombre del array seguido de
un índice entre corchetes. El índice correspondiente a la primera variable es el 0, el de la
segunda si existe es el 1, y así sucesivamente.
Objetos
Una variable de tipo objeto es especial en el sentido de que no almacena directamente
el objeto, sino que guarda la dirección donde este se encuentra ubicado. Si te suenan los
punteros de C, puedes imaginar una variable de tipo objeto como un puntero en C; si no
te suenan no pasa nada, imagínala como un enlace duro a un chero de Linux. Entraremos
más en detalle con los objetos en el tema dedicado a ellos.
8
2.2.2. Declaración
Antes de poder utilizar una variable, hay que declararla. Ello consiste, simplemente, en
indicar al compilador el tipo de la variable y su nombre, para que reserve el espacio necesario
en RAM. Una vez declarada, todavía no la podremos usar hasta que le asignemos un valor
inicial lo estudiaremos en el siguiente apartado. Las variables de tipo simple se declaran
en un paso, mientras que los arrays y los objetos requieren dos. Veámoslo:
Vamos a abrir un paréntesis para prestar atención a la última declaración que aparece
en la línea 30. Se aprecia cómo un objeto se está utilizando como una variable normal, salvo
por un pequeño detalle: ½el tipo del objeto es una clase! Por tanto, podemos ir imaginando
las clases como un tipo de variable un poco especial.
2.2.3. Inicialización
Al declarar una variable, el compilador solo debe reservar el espacio necesario en RAM y
asociarle un nombre, para futuras referencias. Ese espacio reservado seguramente contendrá
una ristra arbitraria de ceros y unos, por lo que el programador debe asegurarse de no hacer
uso de la variable hasta que se le asigne un valor inicial apropiado. A este proceso se le
conoce con el nombre de inicialización.
9
16 v e c t o r = new int [ 1 0 ] ; //... reserva espacio en RAM para albergar 10 enteros consecutivos...
17 v e c t o r [ 1 ] = 3 4 ; // ... y, nalmente, inicializamos el segundo elemento a 34.
18 ...
19 ClaseA o b j e t o ; //Declaramos una variable del tipo ClaseA,...
20 o b j e t o = new ClaseA ( ) ; //... reservamos espacio en RAM y más adelante se inicializaría.
21 ...
Java nos va a ahorrar tiempo y espacio a la hora de escribir código, unicando la decla-
ración y la inicialización en una sola instrucción. Veamos cómo, reescribiendo el fragmento
anterior, esta vez de manera abreviada:
2.2.4. Ámbito
Llegados a este punto, deberíamos explicar cuál es el alcance o ámbito de una variable.
Podríamos denirlo como el territorio del programa donde esa variable es conocida y que,
en cierto modo, también determina su tiempo de vida.
Sin embargo, para realizar una clasicación completa de los ámbitos, es primordial refe-
rirnos al concepto de clase, que todavía desconocemos, así que vamos a retrasar su desarrollo
hasta disponer de todo el conocimiento. Momentáneamente nos quedaremos con la idea de
que un ámbito viene denido, además de por el concepto de clase, por el de bloque, elemento
este constituido por el código delimitado entre dos llaves de apertura y cierre emparejadas
como ya explicamos.
Por tanto, podemos concluir de manera parcial, claro, que cada bloque de código
establece un ámbito y, por consiguiente, cualquier variable será accesible únicamente desde
el bloque en el que está denida.
2.3. Constantes
Una constante es una variable que, tras ser inicializada, permanece inmutable durante
toda su existencia. En otras palabras: es una variable no variable. Se declara e inicializa igual
que una variable convencional, salvo por dos detalles sintácticos:
10
Por convenio, se deberían escribir en mayúsculas para distinguirlas de las variables
de un vistazo.
Como es lógico, una constante no puede aparecer nunca salvo cuando se inicializaen
el lado izquierdo de una asignación, siempre deberá hacerlo en el lado derecho. Veamos un
ejemplo donde se declara y se utiliza una constante:
2.4. Operadores
Ya hemos visto cómo se realiza la primera asignación a una variable, recordemos que lo
habíamos denominado inicialización. Por desgracia, no es todo tan simple como ahí se ha
presentado. A la hora de asignar el resultado de un cálculo a una variable, existen aspectos
relacionados con los operadores implicados, sus prioridades y precedencias, así como alguna
que otra restricción adicional, que nos pueden complicar la vida si no estamos muy atentos.
Por esta razón deberíamos dominar dichas particularidades, si pretendemos programar con
un mínimo de soltura y cierta seguridad.
2.4.1. Aritméticos
Antes de entrar en materia con los operadores aritméticos, es necesario aclarar un par de
cosas sobre una asignación muy habitual que nos encontraremos en casi todos los programas
independientemente del lenguaje empleado y que tiene innumerables variantes:
La primera vez que alguien se topa con esta instrucción, suele quedar extrañado por el
hecho de que la variable aparece en ambos lados de la igualdad.
Una regla que puede ayudarnos a entender más fácilmente el sentido de este tipo de
asignaciones, dice así:
11
Cuando una variable aparece en el lado derecho de una asignación, está haciendo
referencia a su contenido y, cuando aparece en el lado izquierdo, a su ubicación
en RAM.
Por tanto, la forma correcta de interpretar las asignaciones donde una misma variable
aparece en ambos miembros de la igualdad, es la siguiente:
12
Cuando el operador ++ afecta a una variable no incluida en una asignación, como sucede
en las líneas 26 y 27 del fragmento anterior, el resultado nal de la operación es idéntico
en ambos casos: la variable se incrementa en una unidad. Sin embargo, no ocurre lo mismo
cuando la operación aparece en el lado derecho de una asignación. En este caso, el incremento
de la variable tiene lugar antes de empezar a evaluar la expresión si los signos aparecen antes
de la variable (a su izquierda), o bien se incrementa después de haber terminado la evaluación
si los signos aparecen después del nombre de la variable (a su derecha).
Vamos a ilustrar lo recién explicado con más ejemplos, para que no haya dudas:
2.4.2. De comparación
El resto de operadores son bastante intuitivos y no inducen a error. Veamos cuáles son:
2.4.3. Lógicos
13
Tabla 2.3: Operadores lógicos
operadores multiplicativos: *, /, %
operadores aditivos: +,
Or simple: |
Or doble: ||
En caso de que aparezcan dos operadores consecutivos con igual precedencia, hay que
tener en cuenta el orden en que se asocian los operadores, para saber cuál se aplica primero.
Para ello se sigue una regla muy sencilla: Todos los operadores binarios asocian de izquierda
a derecha.
14
14 System . out . p r i n t l n ( a / b ∗ c ) ; //Como asocian de izquierda a derecha, se obtendría 4.
15 //Para alterar la asociatividad, también se emplearían paréntesis.
16 System . out . p r i n t l n ( a / ( b ∗ c ) ) ; //¾Cuál sería el resultado?
17 ...
2.6. Cadenas
Las cadenas de texto no son un tipo simple de Java, sino que se tratan de objetos de
la clase String. Además, estos objetos tienen multitud de métodos para el tratamiento de
cadenas. Como todavía no sabemos nada de objetos ni de métodos, vamos a diferir su estudio
detallado hasta que podamos entenderlo.
15
2.7.1. Como parámetros
De igual manera que pasábamos datos a un chero por lotes en MS-DOS a través de
parámetros, también lo podemos hacer a un programa escrito en Java.
Esos parámetros se recogen en la variable args que es un array de cadenas y, por ejemplo,
se podrían visualizar por pantalla. Vemos cómo se haría:
2.8. Condicionales
Todo lenguaje de programación dispone de instrucciones que pueden alterar la naturaleza
secuencial de los programas. Estas instrucciones son capaces de generar bifurcaciones en el
código, de modo que la ejecución del programa tome un camino u otro, dependiendo de
cierta condición.
Vamos a estudiar las dos instrucciones que realizan esta tarea en Java.
16
2.8.1. La instrucción if
La sintaxis de esta instrucción admite dos variantes principales. Vamos a ver la más
simple:
En este caso, si la condición es cierta, se ejecutan las instrucciones del bloque. Si es falsa,
no se ejecuta nada y se pasa a la instrucción siguiente.
i f ( cond ) {
//Instrucciones
}
else {
//Instrucciones
}
En este caso, si la condición es cierta, se ejecutan las instrucciones del primer bloque y,
si es falsa, se ejecutan las del segundo bloque.
Es posible anidar varias instrucciones if y, además, pueden existir varios niveles de anida-
miento. Sin embargo, cuando hay que codicar una bifurcación múltiple, quizá sea más
cómodo usar la instrucción switch que se explica en el punto siguiente.
Pero antes de dar por cerrada la instrucción if, aprovechamos para introducir un operador
de asignación que habíamos obviado intencionadamente en el apartado correspondiente, ya
que en este encaja mucho mejor. Se trata del operador ternario ?: cuya sintaxis es la mostrada
a continuación:
v a r i a b l e = cond ? v a l o r 1 : v a l o r 2 ;
i f ( cond ) {
variable = valor1 ;
}
else {
variable = valor2 ;
}
Esta instrucción proporciona una forma más sencilla para manejar las bifurcaciones múl-
tiples, siendo una alternativa al uso de sentencias if-else consecutivas. Veamos su sintaxis:
switch ( exp ) {
case v1 :
//Instrucciones
break ;
case v2 :
//Instrucciones
17
break ;
...
case vN :
//Instrucciones
break ;
2.9. Bucles
Los bucles son otra forma de alterar el ujo natural de ejecución de sentencias. Para un
ser humano, la realización de una acción repetitiva o un conjunto de ellas, puede resultar
cansado y hasta aburrido. Sin embargo, os puedo asegurar que una computadora, si tuviera
un corazoncito que le dotara de sentimientos, gozaría hasta la extenuación realizando este
tipo de tareas.
...
while ( cond ) {
//Instrucciones
}
...
Además de break, dentro de un bucle podemos toparnos con una instrucción continue,
que hace que evalúe de nuevo la condición del while sin haber ejecutado los comandos del
18
cuerpo posteriores a continue. En otras palabras, la ejecución pega un salto a la siguiente
vuelta del bucle.
Ese mismo programa se puede escribir de manera más compacta utilizando la instrucción
for. Lo único que cambia es la apariencia, pues ambos son totalmente equivalentes y el
resultado de la ejecución es idéntico. Aprovechamos la coyuntura para presentarlo a través
de este ejemplo:
Existen variantes de este bucle que le aportan mayor potencia y exibilidad, pero las
veremos más adelante a través de ejercicios.
La última instrucción para generar bucles que vamos a estudiar, es una variación del while
en la que se invierte el orden de funcionamiento: primero se ejecuta el cuerpo del bucle y
luego se evalúa la condición para conocer si se deben dar más vueltas o no. La forma general
de esta instrucción es:
...
do {
//Instrucciones
19
} while ( cond ) ;
...
2.10. Funciones
Hay veces que, en varios puntos de un mismo programa, se debe ejecutar una secuencia
de instrucciones idéntica. En estos casos, para evitar la repetición de código, se utiliza una
técnica que consiste en asignar un nombre a esa secuencia de instrucciones y sustituir las
apariciones de dicha secuencia por el nombre asignado.
Debe existir una o más llamadas a dicha función a lo largo del programa (la denición
por sí misma, no hace nada, no ejecuta el código que contiene).
Una función opcionalmente puede admitir argumentos de forma similar a como lo
hacía un programa y además está capacitada para devolver un resultado, si se considera
oportuno.
nombre ( v a l o r 1 , . . . , v a l o r N) ;
20
2.10.1. Paso de parámetros
Ya hemos dicho que a una función se le pueden pasar parámetros de cualquiera de los
tipos vistos de manera opcional.
En caso de que pasemos algún parámetro a una función, debemos saber que existen dos
formas diferentes de hacerlo:
Paso por valor : Con esta técnica, el argumento de la llamada también denominado
parámetro real se copia en una variable local de la función llamada parámetro
formal y todo cálculo que se haga dentro de la función, se realiza sobre la copia. La
principal consecuencia de esto es que el argumento original no se ve afectado cuando
la función termina.
Paso por referencia : Mediante esta otra técnica, lo que se copia en la variable local de
la función el parámetro formal no es el argumento sino su dirección en memoria.
Ahora cualquier cálculo que se haga dentro de la función, se realizará directamente
sobre el argumento y, por tanto, sí se verá afectado.
Hay lenguajes de programación que permiten establecer qué técnica utilizar cuando se va
a pasar un parámetro a una función. Java, en cambio, obliga a realizar paso de parámetros
por valor siempre. Sin embargo, cuando pasamos un array o un objeto como parámetro de
una función, a efectos prácticos funciona como si se tratara de un paso por referencia, debido
a la naturaleza de esos dos tipos.
21
Tema 3
La idea era que este entorno pudiera ser fácilmente ampliable por el usuario, utilizando
para ello un sencillo lenguaje de programación enfocado a la forma de pensar de las personas.
Así es como nació el lenguaje Smalltalk.
Ninguna de las versiones de Smalltalk aparecidas en la década de los 70, gozó de gran
popularidad. Solo a nales de los 80 surgió cierto interés por la orientación a objetos, pasando
a aplicarse en sistemas y lenguajes ya existentes por ejemplo, Bjarne Stroustrup creó C++
a partir de C . En los años 90 esta tecnología llegó al terreno comercial y se puso de moda,
provocando su incorporación en muchas aplicaciones y sistemas operativos.
En la actualidad, cualquier programador conoce las nociones básicas sobre las que se
apoya la orientación a objetos. Sin embargo, no son tantos los que realmente entienden las
razones que hacen de esta tecnología una herramienta de programación superior al resto si
hubiera que denirlo con tres palabras, estas serían reutilización de código .
½Yo sí las entiendo, yo sí! gritó Cristino mientras hacía aspavientos con los brazos.
Por desgracia, no es este el objetivo del presente manual por otro lado ligero así que
aparcaremos el asunto hasta encontrar un momento más propicio para contarlo.
3.2. Clases
Las clases son la base de la programación orientada a objetos. De hecho, todo el código de
una aplicación debe estar incluido en una clase. A su vez, una clase está formada por atributos
que no son otra cosa que variables y por métodos que no dejan de ser funciones.
22
También es posible que una clase contenga otras clases, bajo ciertas condiciones que veremos
en los ejercicios.
Como ya dijimos al inicio del manual, podemos considerar las clases como una especie de
plantillas que nos van a permitir crear objetos, que son los verdaderos protagonistas de este
paradigma de programación. Veamos un ejemplo de clase que nos permitirá generar objetos
coche (de momento solo se muestra el esqueleto, pero se va a desarrollar de forma completa
a través de los ejercicios):
23
Figura 3.2: Objetos creados a partir de la clase Coche.
Dentro de una clase cualquiera pueden aparecer unos métodos especiales denominados
constructores, que facilitarán la inicialización de los objetos de dicha clase. Sin embargo, no
los vamos a presentar ahora sino un poco más adelante, cuando veamos la herencia.
24
3.3. Objetos
½Por n...! ½Ha llegado el momento de estudiar la entidad que da nombre al estilo de
programación en el que nos encontramos inmersos!
Sabemos que un objeto se genera a partir de una clase. Simplicando un poco, podemos
imaginar una clase como el tipo de una variable y un objeto como una variable de dicho tipo
que se inicializa mediante el operador new. Es importante que dediques el tiempo necesario
para asimilar esto que acabamos de decir... Tómate tu tiempo.
Ahora, sin más dilación, vamos a estudiar un ejemplo donde se hace uso de una clase y
un objeto derivado de ella:
Bonito, ¾verdad?
½Es lindoooo! dijo Leo con lágrimas en los ojos Pero... ½no me enteré de nada!
25
Si has sido un alumno avispado, te habrás dado cuenta de que hemos creado un chero
.java con dos clases: Coche y Principal. Es posible denir más de una clase en un chero .java,
con la condición de que solo una de ellas sea pública. Sin embargo, al compilar, apareceran
dos cheros .class, uno por cada clase: [Link] y [Link].
Se trata de un valor especial toman las variables de tipo objeto cuando no han sido
inicializadas es decir, se ha declarado la variable pero no se le ha asignado ningun objeto
con el operador new , o bien cuando la variable sí tenía asignado un objeto pero se eliminó
la referencia al mismo.
Resumiendo, si una variable contiene el valor null, realmente signica que se encuentra
vacía. Esta circunstancia es fuente de numerosos errores, ya que no se puede operar sobre
una variable asumiendo que contiene un objeto, no siendo así.
Hasta ahora no lo habíamos dicho, pero al conjunto de atributos y métodos de una clase
se les conoce con el nombre genérico de miembros.
Hay que tener claro que cuando denimos un atributo en una clase y creamos objetos,
cada uno de estos objetos tendrá un valor posiblemente distinto para dicho atributo. Por
ejemplo, si pensamos en el atributo color de la clase Coche anterior, el objeto coche1 puede
tener color rojo y el objeto coche2 ser blanco. Repetid mentalmente lo que acabamos de
decir...
Cada objeto tiene sus propios valores, cada objeto... repetía Leo Mesa en su cabeza,
como un mantra que le absorbía misteriosamente los pensamientos.
Sin embargo, en ocasiones conviene que alguno de los atributos de una clase tenga un
valor común para todos sus objetos. Esto se consigue declarando el atributo como estático
y, para ello, basta con preceder su declaración por la palabra reservada static. Los atributos
estáticos se pueden utilizar sin necesidad de crear un objeto de la clase.
De la misma manera, los métodos de una clase también se pueden declarar estáticos lo
hemos visto en la función main() . Sin embargo, el signicado es distinto.
26
Por último, aunque no es tan habitual, también puede aparecer algún bloque de código
denido como estático. La sintaxis empleada es: static {código } y provocará que el código
entre llaves se ejecute una única vez cuando se cargue la clase.
En este punto, debería haber surgido una pregunta: Si un miembro estático se puede
utilizar sin objetos de por medio, ¾qué sintaxis se emplea?
Normalmente, cualquier clase tiene denidos varios atributos y métodos. Sin embargo,
solo algunos de ellos van a ser de utilidad a la hora de hacer uso de dicha clase; el resto los ha
podido emplear el programador que ha creado la clase para proporcionar cierta funcionalidad
a los primeros o para organizar mejor el código pero, para el uso convencional de la clase, no
se van a necesitar en absoluto. La encapusulación permite hacer invisibles a estos últimos,
dejando a la vista únicamente los atributos y métodos necesarios para la manipulación de la
clase.
Una analogía que nos puede ayudar a entender mejor en qué consiste la encapsulación
gira en torno al conocimiento que debe abarcar un conductor de coche para circular con
su automóvil. Está claro que el proceso de arranque del vehículo involucra ciertos procesos
químicos, eléctricos y mecánicos, que el conductor no tiene por qué conocer en profundidad
para poder utilizar su coche. Ni tan siquiera debe conocer todas las piezas que conforman el
motor, ni qué papel juega cada una de ellas.
Leo, la junta de la trócola de mi seiscientos no tiene secretos para mí... ½que lo sepas!
dijo susurrando Cristino a su amigo.
Simplicando un poco, basta con que un piloto se desenvuelva bien con el manejo de los
pedales, la palanca de cambios y los giros del volante. Digamos que esa es la interfaz que
presenta un coche para interactuar con su conductor.
3.4.1. Paquetes
Ji, ji, ji se rio Leo, encogiéndose de hombros mientras se tapaba la boca con ambas
manos.
Un paquete no es más que una manera de organizar las clases en realidad, los cheros
.class dentro de directorios de manera que, por ejemplo, todas las clases del paquete
denominado nombre deberán estar ubicadas en el directorio nombre. De la misma manera,
si el nombre del paquete consta de dos componentes, por ejemplo, nombre1.nombre2, todas
27
sus clases se guardarán en el directorio nombre1/nombre2. Como consecuencia de lo anterior,
podrán existir clases con igual nombre en distintos paquetes, evitándose así posibles conictos
de nomenclatura.
Android, sin ir más lejos, usa por defecto el paquete [Link], es decir, un nombre
de dominio en orden inverso.
Para que una clase pertenezca a un paquete, hay que escribir como primera línea del
chero y en minúsculas: package nombre
protected: La aparición de esta etiqueta hace que sea visible desde cualquier clase del
mismo paquete y desde sus clases derivadas. No es aplicable a clases.
Por defecto: Cuando no aparece ninguna etiqueta, las clases, métodos o atributos
serán visibles desde cualquier clase del propio paquete.
private: Si lo precedemos por esta etiqueta, solo será visible desde la propia clase. No
es aplicable a clases.
En este punto, conviene destacar que es una práctica habitual impedir el acceso directo
a los atributos de un objeto, declarándolos privados. Para poder llevar a cabo un acceso
indirecto a los mismos, se crean un par de métodos públicos por atributo: uno para obtener
el valor del atributo denominado getter y otro para modicarlo llamado setter .
Piensa, como ejercicio, qué parámetro debe recibir y qué valor devolver, cada uno de ellos.
3.5. Herencia
Es la principal característica de un lenguaje orientado a objetos. Consiste en denir una
nueva clase a partir de otra existente, mediante la sintaxis:
28
c l a s s Nueva extends V i e j a {
//Código adicional
}
A partir de este momento, la nueva clase denominada clase hija o subclase hereda
de forma automática todos los atributos y métodos de la clase existente llamada clase
padre o superclase . Además, se pueden crear nuevos atributos y métodos que extiendan la
funcionalidad de la clase original.
Conviene tener presente que cualquier clase en Java, hereda implícitamente de la clase
Object, es decir, esa es la clase raíz de cualquier árbol de herencia. Como dicha clase im-
plementa ciertos métodos (como, por ejemplo, toString() ), cualquier otra clase dispondrá
también de ellos.
La herencia múltiple consiste en que una clase herede de varios padres, pero en Java no
funciona. Por tanto, toda clase derivada en Java solo tendrá un padre. A esto se le conoce
como herencia simple.
3.5.1. Constructores
Cuando dentro de un método de la clase se quiere hacer referencia al objeto que lo está
ejecutando, se utiliza la palabra this. Veremos algún ejemplo de uso en los ejercicios.
Allí donde se puede utilizar un objeto de una clase, también se puede usar un objeto de
cualquier subclase suya, pero no al revés .
29
1
cambiemos la rma del método (si no lo hacemos así, se crearía un nuevo método en lugar
de redenir el de la clase padre y no nos enteraríamos).
Veamos un ejemplo:
30
21
22 h . l l o r a r ( ) ; //Piensa qué mostraría esta instrucción
23 h . s o s ( ) ; //Piensa si tiene sentido
24 }
25 }
La sobrecarga de un método consiste en denir dos o más métodos con igual nombre
pero que varían o bien en cuanto al número de argumentos, o bien en cuanto a su tipo. El
compilador de Java sabrá cuál tiene que ejecutar analizando dichos argumentos.
3.8. Enumeraciones
Una enumeración es un nuevo tipo de datos que establece cuáles son los posibles valores
que puede tomar una variable de dicho tipo. Realmente, podemos considerar un tipo enume-
rado como una clase especial, cuyos objetos escritos en mayúsculas por convenio vienen
31
denidos en una lista y pueden contener atributos (en este caso, el constructor de la clase
debe ser privado para evitar la creación de nuevos objetos).
Hay que resaltar que toda enumeración hereda de la clase [Link] por lo que
dispondrán de una serie de métodos heredados de dicha clase. Destacan los siguientes:
Un ejemplo simple es el de una variable que debe almacenar los días de la semana (sin
atributos) y no puede tomar otro valor distinto. Veamos cómo se deniría un tipo enumera-
ción para dicha variable:
Veamos ahora un ejemplo en el que los objetos albergan atributos y, por tanto, también se
creará el correspondiente constructor. La idea es asociar a cada objeto el nombre abreviado
y el número de letras del nombre completo. Se haría como indicamos a continuación:
32
11
12 //Ahora vamos a denir los atributos que tienen cada uno de esos objetos
13 private f i n a l S t r i n g nomAbreviado ;
14 private f i n a l int numLetras ;
15
16 //Le toca el turno al constructor
17 private DiaSemana2 ( S t r i n g nomAbreviado , int numLetras ) {
18 this . nomAbreviado = nomAbreviado ;
19 this . numLetras = numLetras ;
20 }
21
22 //Por último, creamos los getters
23 public S t r i n g getNomAbreviado ( ) {
24 return nomAbreviado ;
25 }
26
27 public int getNumLetras ( ) {
28 return numLetras ;
29 }
30 }
33
(el perro camina y el gorrión vuela). Estos métodos deberían declararse como abstractos y
codicarse luego en cada una de las clases derivadas de manera apropiada.
Otro ejemplo típico de concepto que debería considerarse como abstracto es el de vehículo.
A partir de él, se pueden derivar otros concretos como coches, motos, bicicletas, aviones, etc.
3.10. Interfaces
Podemos imaginarnos una interface como un caso extremo de clase abstracta, en el
sentido de que todos sus métodos son abstractos. Además, cuando se implementen en una
clase particular, habrá que denirlos obligatoriamente como públicos.
34
Eso sí, la sintaxis es muy diferente con respecto a una clase abstracta:
Para que una clase pueda utilizar la interfaz, debe indicarse con la palabra reservada
implements, como se indica en el siguiente ejemplo:
Es habitual que una clase herede de otra e implemente una interfaz (o más) de forma
simultánea. La sintaxis utilizada es:
Como se observa en el ejemplo anterior, una clase puede implementar más de una interfaz
a la vez debiendo aparecer separadas por comas.
Además, también se puede utilizar la herencia entre interfaces o, dicho con otras palabras,
una interfaz puede heredar de otra de la misma forma que lo hacen las clases.
35
Las excepciones se clasican jerárquicamente, de manera que todas descienden de la clase
Exception. Para manejar excepciones, se emplean una serie de instrucciones organizadas en
tres bloques, precedidos por las palabras reservadas try, catch y nally respectivamente, que
tratan de reaccionar para que el programa salga airoso ante la situación de error.
try: Este bloque debe contener las instrucciones que son susceptibles de producir un
error.
nally: Este bloque es opcional y sus instrucciones se ejecutarán siempre, haya habido
error o no.
36
Una excepción muy común es NullPointerException. Aparece cuando se intenta realizar
alguna operación sobre un objeto no inicializado recuerda, aquel que contiene el valor
null .
Un servidor en nuestro caso MySQL que contenga una BD con alguna tabla a la
que acceder y, además, un usuario con permisos para manipular dicha BD. Todo eso
se puede congurar con facilidad mediante la herramienta web PhpMyAdmin.
Un driver basado en JDBC para conectar con la BD anterior. Como nosotros vamos
a utilizar MySQL, habrá que bajar el driver JDBC Driver for MySQL (Connector/J)
desde la página de MySQL. Una vez descargado y descomprimido el driver, hay que in-
cluirlo en el proyecto de Eclipse como un archivo JAR externo (Build path 7→ Congure
Build Path 7→ Libraries 7→ Add External JARs ).
A continuación, vamos a mostrar el código necesario para conectar con una BD llamada
Prueba a través del usuario Ana cuya contraseña es Pass. Una vez realizada la conexión,
primero insertaremos un registro en la tabla Alumnos que debe existir y que contiene
los campos Nombre, Apellido y Edad. Después, utilizaremos un bucle para mostrar todos los
registros de la tabla, incluido el recién añadido:
37
20 cn = ( Connection ) DriverManager . getConnection ( s t r , "Ana" , " Pass " ) ;
21
22 //Si cn no es nulo, la conexión ha tenido éxito
23 i f ( cn != null ) {
24
25 //stmt sirve para ejecutar sentencias de modicación
26 Statement stmt = ( Statement ) cn . c r e a t e S t a t e m e n t ( ) ;
27
28 //Aquí se puede ejecutar un DELETE o un UPDATE en vez del INSERT
29 s t r = "INSERT INTO Alumnos VALUES( ' Luis ' , ' Ruiz ' , 1 8 ) " ;
30 stmt . e x e c u t e ( s t r ) ;
31
32 //El objeto rs (cursor) almacena el resultado de una SELECT
33 s t r = "SELECT ∗ FROM Alumnos" ;
34 R e s u l t S e t r s = ( R e s u l t S e t ) stmt . executeQuery ( s t r ) ;
35
36 S t r i n g datos = "" ;
37 while ( r s . next ( ) ) {
38 datos = datos + r s . g e t I n t ( "Edad" ) + " "
39 + r s . g e t S t r i n g ( "Nombre" ) + " "
40 + r s . g e t S t r i n g ( " A p e l l i d o " ) + "\n" ;
41 }
42
43 System . out . p r i n t l n ( datos ) ;
44 rs . close ( ) ;
45 }
46
47 //Si se produce un error al intentar la conexión, mostramos el mensaje
48 } catch ( Exception e ) {
49 System . out . p r i n t l n ( e . getMessage ( ) ) ;
50 }
51
52 }
53
54 }
Si has prestado atención, te habrás dado cuenta de que el programa anterior se está
conectando a un servidor local (...mysql://localhost...). Si quisiéramos conectar con un ser-
vidor remoto, este debería estar congurado de forma que admitiera conexiones externas.
Para conseguirlo, habría que modicar el chero /etc/mysql/[Link].d/[Link] para
que la línea que contiene el texto:
Además, el usuario que se conecta Juan en el ejemplo anterior debería tener una
cuenta en el servidor remoto con todos los permisos necesarios activados.
Para que MySQL reconozca las tildes y la letra ñ, hay que modicar el chero [Link],
añadiendo las siguientes líneas en las secciones indicadas:
[mysqld]
character-set-filesystem=UTF8
38
character-set-server=UTF8
[client]
default-character-set=UTF8
[mysqldump]
default-character-set=UTF8
39
Tema 4
Aspectos avanzados
Una clase puede contener una o más clases internas. La relación que existe entre una
clase interna y su correspondiente clase externa la desgranamos en los siguientes apartados:
La clase interna solo es accesible desde la externa; no es visible desde ningún otro lugar
aunque esté declarada como pública.
40
Desde la clase interna sí se puede acceder directamente a los miembros de la clase
externa.
41
Además de los cheros [Link] y [Link], al compilar el ejemplo anterior
también aparece un tercer chero que representa a la clase interna, con el curioso nombre
Externa$[Link].
Una clase interna también se puede denir en el interior de cualquier bloque de código, en
particular, dentro de un bucle o un método. En este caso, se le da el nombre de clase interna
local. Su uso es especialmente útil para la gestión de eventos en un applet. Lo veremos en
breve.
Una clase interna anónima es una clase interna que no dispone de nombre. Es un paso
más hacia la simplicación de la escritura de código empleado para el manejo de eventos,
pero su sintaxis resulta muy confusa hasta que nos habituamos a utilizarla.
Ya sabemos que una interfaz no se puede instanciar directamente, sin embargo, gracias
a las clases anónimas esto estará permitido.
42
12 }
13 }
14
15 public void metodoE ( ) {
16 I n t e r n a i = new I n t e r n a ( ) ;
17 i . mensaje ( ) ;
18 }
19 }
20
21 public c l a s s E j C l a s e I f a z I n t e r n a {
22 public s t a t i c void main ( S t r i n g [ ] a r g s ) {
23 C l a s e I f a z I n t e r n a i i = new C l a s e I f a z I n t e r n a ( ) ;
24 i i . metodoE ( ) ;
25 }
26 }
Ahora vamos a crear un programa que realiza exactamente lo mismo, pero nos ahorramos
nombrar una clase. Presta mucha atención al código porque ahora sí podemos instanciar una
interfaz directamente, gracias a las clases anónimas:
43
4.2. Applets
Un applet o pequeña aplicación es un tipo especial de programa Java, diseñado para
ejecutarse dentro de una página web. Los applets llegaron con la intención de proporcionar
dinamismo a la web pero, poco a poco, fueron desplazados por otras soluciones más e-
cientes. Sin embargo, aquí los vamos a estudiar porque la programación de applets es una
aproximación simplicada a la programación en Android. Los usaremos, por tanto, como un
trampolín hacia nuestro objetivo nal, la creación de aplicaciones en Android.
Vamos a crear un applet llamado [Link] con el siguiente código y, tras compilarlo,
usaremos el chero [Link] como se muestra más abajo.
<!DOCTYPE html>
<html>
<head>
<title>Ejemplo de applet</title>
</head>
<body>
<applet code=[Link] width=300 height=200></applet>
</body>
</html>
Desde el visor de applets que proporciona el JDK, utilizando el siguiente comando y
el chero anterior:
¾Qué hace tan especial a un applet? Pues que, a diferencia de un programa Java de
consola, aquel carece de método main() ya que se ejecuta en el contexto de un navegador.
Por tanto, es el propio navegador quien va controlar el ciclo de vida del applet, utilizando
ciertos métodos con los que envía noticaciones al mismo. Veamos cuáles son:
init(): Se ejecuta cuando el applet acaba de ser cargado por el contenedor. Es invocado
una única vez y se utiliza para realizar inicializaciones básicas.
44
start(): Inicia la ejecución del applet. Es invocado tras init(). Este método también
se ejecuta, por ejemplo, cuando el usuario restaura una ventana del navegador previa-
mente minimizada.
stop(): Detiene la ejecución del applet al menos temporalmente. Se lanza cuando
el usuario minimiza la ventana del navegador, por ejemplo.
destroy(): Hace lo contrario que init(). Se ejecuta una única vez al nal de la vida del
applet para liberar los recursos antes de ser eliminado por el contenedor. Normalmente
realiza acciones inversas a las efectuadas en init().
Para crear una clase que implemente un applet, esta debe derivar de la clase Applet que
primero habrá que importar del paquete [Link] . La clase Applet tiene los métodos
init(), start(), stop() y destroy() vacíos y nuestro applet deberá sobreescribir los que interese,
añadiendo el código necesario.
Como ejemplo, vamos a construir un applet que escriba por la consola un mensaje indi-
cando que se han ejecutado esos métodos. Es necesario ejecutar este applet desde el visor,
ya que si no, los mensajes no serán visibles:
45
En general, hay una forma sencilla de gestionar los eventos y otra bastante más compleja.
Java utiliza la segunda debido a que resulta mucho más eciente que la primera. Para en-
tender bien en qué consiste esta técnica, es fundamental conocer qué elementos intervienen
y el papel juega cada uno. Vamos a intentar describirlo:
La fuente: Es el objeto sobre el que se ha realizado una acción y que, a su vez, genera
un objeto evento. El ejemplo típico es el de un botón que ha sido pulsado, sin embargo,
cualquier componente gráco de una aplicación puede convertirse en fuente.
El evento: Es el objeto que genera la fuente cuando detecta que se ha llevado a cabo
una acción sobre él. Contiene información de lo que ha sucedido.
Para que no haya dudas, vamos a describir el proceso completo: Un usuario hace clic en
un botón. Este genera un objeto evento con la correspondiente información y se lo envía a los
oyentes que tenga registrados. Estos, a su vez, deben implementar una interfaz conteniendo
un método que ejecute las instrucciones oportunas (lo que tenga que suceder al hacer clic en
el botón).
Todos los eventos derivan de la clase EventObject del paquete [Link]. Existen muchas
clases que representan eventos, pero nosotros solo vamos a ver algunas de las más utilizadas:
KeyEvent: Se genera un evento de esta clase cuando se pulsa una tecla. Contiene
las constates enteras KEY_PRESSED, KEY_TYPED y KEY_RELEASED, que di-
ferencian las acciones anteriores.
46
4.3.2. Oyentes
El objeto debe implementar alguna de las interfaces para gestionar los eventos. Es-
tas interfaces contienen los métodos que se ejecutarán cuando se produzca el evento
correspondiente.
Para concretar, si tiene lugar el evento ActionEvent, entonces un objeto oyente se re-
gistraría en el objeto fuente que ha generado dicho evento, con la siguiente instrucción:
[Link](oyente);
Es habitual que, en la instrucción anterior, el objeto oyente se dena utilizando una clase
anónima. Lo veremos con más profundidad en los ejercicios.
Por otro lado, hay que tener en cuenta que una misma fuente puede generar varios tipos
de eventos y que un oyente puede registrarse en varias fuentes y, por tanto, recibir diferentes
tipos de eventos.
MouseListener: Contiene, entre otros, estos tres métodos que se ejecutan cuando se
produce un evento MouseEvent, es decir, cuando se pulsa el ratón, se libera o se hace
clic, respectivamente:
47
void keyPressed(KeyEvent ke);
void keyTyped(KeyEvent ke);
void keyReleased(KeyEvent ke);
Veamos un par de ejemplos donde se crea un applet con un botón y se muestra un mensaje
al pulsarlo. Lo haremos sin usar clases anónimas (que no es lo recomendado):
Ahora le toca el turno a una variante donde la clase del oyente se dene dentro del método
init() ; todavía no usamos clases anónimas:
48
8
9 //Ahora creamos la clase del oyente dentro del método init()
10 c l a s s MiOyente implements j a v a . awt . event . A c t i o n L i s t e n e r {
11 public void a ct io n Pe rf o rm ed ( j a v a . awt . event . ActionEvent e ) {
12 System . out . p r i n t l n ( mensaje ) ;
13 }
14 }
15
16 //miBoton es el objeto fuente
17 j a v a . awt . Button miBoton = new j a v a . awt . Button ( " Pulsame " ) ;
18
19 //Creamos un oyente y lo registramos para recibir noticaciones del evento
20 miBoton . a d d A c t i o n L i s t e n e r ( new MiOyente ( ) ) ;
21
22 //Incluimos el botón en el applet
23 add ( miBoton ) ;
24 }
25 }
Recordemos que al denir una clase dentro de un método, se creaba una clase interna
local. Si compilamos el ejemplo anterior, observaremos que se crea una clase con el nombre
EjAppletBoton2$[Link]. El número que aparece tras el dólar serviría para diferen-
ciarla de otra clase con el mismo nombre, que estuviera denida en el interior de un método
distinto.
Para terminar, haremos uso de la técnica recomendada, que no es otra que usar clases
anónimas para denir el oyente. La siguiente imagen muestra de manera simplicada cómo
se usa; es muy similar a la instanciación de interfaces, salvo porque ahora la clase anónima
se dene como el argumento del método que registra al oyente:
El objetivo es que la gestión del evento esté lo más concentrada posible. A partir de
ahora, siempre emplearemos esta técnica. Presta mucha atención:
49
12 //Creamos la clase anónima, el objeto oyente y lo registramos, todo en una instrucción
13 miBoton . a d d A c t i o n L i s t e n e r ( new j a v a . awt . event . A c t i o n L i s t e n e r ( ) {
14 public void a ct io n Pe rf o rm ed ( j a v a . awt . event . ActionEvent e ) {
15 System . out . p r i n t l n ( mensaje ) ;
16 }
17 });
18
19 //Incluimos el botón en el applet
20 add ( miBoton ) ;
21 }
22 }
Registrar los cambios realizados en los archivos de un proyecto quién los ha hecho y
cuándo, de forma que, por ejemplo, se puedan deshacer dichos cambios y alcanzar
una versión anterior.
Hay varias herramientas de este tipo, como RCS, CVS o GitHub pero nosotros vamos a
estudiar Subversion.
Instalación:
50
Obtener una copia, parcial o completa, del repositorio (una especie de entorno de
trabajo privado): El directorio /ruta/copia no debe existir.
svn update
Realizar cambios en la copia local (que se actualizarán al ejecutar svn commit):
svn add f.c: Añade un chero al repositorio local.
svn copy f1.c f2.c: Copia un chero dentro del repositorio local.
svn move f1.c f2.c: Mueve un chero dentro del repositorio local.
svn merge: Mezcla los cambios de otros usuarios en nuestra copia, siempre que no
haya conictos.
svn commit [fichero descripción]: Se utiliza para enviar la copia local al reposi-
torio y para avisar a otros usuarios de nuestros cambios en un chero (conviene escribir
una descripción de los cambios).
Los IDE modernos como Eclipse o NetBeans permiten gestionar las operaciones
desde el menú, sin necesidad de acceder a la consola. Para ello, se debe instalar un plugin
adecuado en cada caso.
51