JAVA5
Para
crear un Interface, se debe escribir tanto la declaración como el cuerpo del
interface.
declaraciondeInterface
{
cuerpodeInterface
}
La
Declaración de Interface declara varios atributos del interface, como su
nombre o si se extiende desde otro interface. El Cuerpo de Interface contiene
las constantes y las declaraciones de métodos del Interface.
Como
mínimo, una declaración de interface contiene la palabra clave interface y
el nombre del interface que se va a crear.
interface
Contable {
.
. .
}
Una
declaración de interface puede tener otros dos componentes: el especificador de
acceso public y una lista de "superinterfaces". Un interface
puede extender otros interfaces como una clase puede extender o subclasificar
otra case. Sin embargo, mientras que una clase sólo puede extender una
superclase, los interfaces pueden extender de cualquier número de interfaces.
Así, una declaración completa de interface se parecería a esto.
[public]
interface Nombredenterface [extends listadeSuperInterfaces] {
.
. .
}
El
especificador de acceso public indica que el interface puede ser
utilizado por todas las clases en cualquier paquete. Si el interface no se
especifica como público, sólo será accesible para las clases definidas en el
mismo paquete que el interface.
La
clausula extends es similar a la utilizada en la declaración de una
clase, sin embargo, un interface puede extender varios interfaces (mientras una
clase sólo puede extender una), y un interface no puede extender clases. Esta
lista de superinterfaces es un lista delimitada por comas de todos los
interfaces extendidos por el nuevo interface.
Un
interface hereda todas las constantes y métodos de sus
superinterfaces a menos que el interface oculte una constante con el
mismo nombre o redeclare un método con una nueva declaración.
El
cuerpo del interface contiene las declaraciones de métodos para los métodos
definidos en el interface.
Todos
los valores constantes definidos en un interface son implícitamente públicos,
estáticos y finales. El uso de estos modificadores en una declaración de
constante en un interface está desaconsejado por falta de estilo. Similarmente,
todos los métodos declarados en un interface son implícitamente públicos y
abstractos.
Este
código define un nuevo interface llamado coleccion que contiene un valor
constante y tres declaraciones de métodos.
interface
coleccion {
int
MAXIMO = 500;
void
añadir(Object obj);
void
borrar(Object obj);
Object
buscar(Object obj);
int
contadorActual();
}
El
interface anterior puede ser implementado por cualquier clase que represente una
colección de objetos como pueden ser pilas, vectores, enlaces, etc...
Observe
que cada declaración de método está seguida por un punto y coma (;) porque un
interface no proporciona implementación para los métodos declarados dentro de
él.
Para
utilizar un interface se debe escribir una clase que lo implemente. Una clase
declara todos los interfaces que implementa en su declaración de clase. Para
declarar que una clase implementa uno o más interfaces, se utiliza la palabra
clave implements seguida por una lista delimitada por comas con los
interfaces implementados por la clase.
supongamos
que queremos escribir una clase que implemente un pila FIFO (primero en entrar,
primero en salir). Como una pila FIFO contiene otros objetos tiene sentido que
implemente el interface coleccion. La clase PilaFIFO declara que implementa el
interface coleccion de esta forma.
class
PilaFIFO implements coleccion {
.
. .
void
añadir(Object obj) {
.
. .
}
void
borrar(Object obj) {
.
. .
}
Object
buscar(Object obj) {
.
. .
}
int
contadorActual() {
.
. .
}
}
así
se garantiza que proporciona implementación para los métodos añadir(),
borrar(), buscar() y contadorActual().
Por
convención, la clausula implements sigue a la clausula extends si
es que ésta existe.
Observe
que las firmas de los métodos del interface coleccion
implementados en la clase PilaFIFO debe corresponder exactamente con las
firmas de los métodos declarados en la interface coleccion.
Cuando
se define un nuevo interface, en esencia se está definiendo un tipo de
referencia. Se pueden utilizar los nombres de interface en cualquier lugar donde
se usaría un nombre de dato de tipos primitivos o un nombre de datos del tipo
de referencia.
Por
ejemplo, supongamos que se ha escrito un programa de hoja de cálculo que
contiene un conjunto tabular de celdas y cada una contiene un valor. Querríamos
poder poner cadenas, fechas, enteros, ecuaciones, en cada una de las celdas de
la hoja. Para hacer esto, las cadenas, las fechas, los enteros y las ecuaciones
tienen que implementar el mismo conjunto de métodos. Una forma de conseguir
esto es encontrar el ancestro común de las clases e implementar ahí los métodos
necesarios. Sin embargo, esto no es una solución práctica porque el ancestro
común más frecuente es Object. De hecho, los objetos que puede poner en las
celdas de su hoja de cálculo no están relacionadas entre sí, sólo por la
clase Object. Pero no puede modificar Object.
Una
aproximación podría ser escribir una clase llamada ValordeCelda que
representara los valores que pudiera contener una celda de la hoja de cálculo.
Entonces se podrían crear distintas subclases de ValordeCelda para las cadenas,
los enteros o las ecuaciones. Además de ser mucho trabajo, esta aproximación
arbitraria fuerza una relación entre esas clases que de otra forma no sería
necesaria, y debería duplicar e implementar de nuevo clases que ya existen.
Se
podría definir un interface llamado CellAble que se parecería a esto.
interface
CellAble {
void
draw();
void
toString();
void
toFloat();
}
Ahora,
supongamos que existen objetos Linea y Columna que contienen un conjunto de
objetos que implementan el interface CellAble. El método setObjectAt() de
la clase Linea se podría parecer a esto.
class
Linea {
private
CellAble[] contents;
.
. .
void
setObjectAt(CellAble ca, int index) {
.
. .
}
.
. .
}
Observa
el uso del nombre del interface en la declaración de la variable miembro contents
y en la declaración del argumento ca del método. Cualquier objeto
que implemente el interface CellAble, sin importar que exista o no en el árbol
de clases, puede estar contenido en el array contents y podría ser
pasado al método setObjectAt().
Los
paquetes son grupos relacionados de clases e interfaces y proporcionan un
mecanismo conveniente para menejar un gran juego de clases e interfaces y evitar
los conflictos de nombres.
Además
de los paquetes de Java puede crear tus propios paquetes y poner en ellos
definiciones de clases y de interfaces utilizando la sentencia package.
Supongamos
que se está implementando un grupo de clases que representan una colección de
objetos gráficos como círculos, rectángulos, líneas y puntos. Además de
estas clases tendrás que escribir un interface Draggable para que en las clases
que lo implementen pueda moverse con el ratón. Si quieres que estas clases estén
disponibles para otros programadores, puedes empaquetarlas en un paquete,
digamos, graphics y entregar el paquete a los programadores (junto con
alguna documentación de referencia, como qué hacen las clases y los interfaces
y qué interfaces de programación son públicos).
De
esta forma, otros programadores pueden determinar fácilmente para qué es tu
grupo de clases, cómo utilizarlos, y cómo relacionarlos unos con otros y con
otras clases y paquetes.
Los
nombres de clases no tienen conflictos con los nombres de las clases de otros
paquetes porque las clases y los interfaces dentro de un paquete son
referenciados en términos de su paquete (técnicamente un paquete crea un nuevo
espacio de nombres).
Se
declara un paquete utilizando la sentencia package.
package
graphics;
interface
Draggable {
.
. .
}
class
Circle {
.
. .
}
class
Rectangle {
.
. .
}
La
primera línea del código anterior crea un paquete llamado graphics.
Todas las clases e interfaces definidas en el fichero que contiene esta
sentencia son miembros del paquete. Por lo tanto, Draggable, Circle,y Rectangle
son miembros del paquete graphics.
Los
ficheros .class generados por el compilador cuando se compila el fichero
que contiene el fuente para Draggable, Circle y Rectangle debe situarse en un
directorio llamado graphics en algún lugar del path CLASSPATH. CLASSPATH
es una lista de directorios que indican al sistema donde ha instalado varias
clases e interfaces compiladas Java. Cuando busque una clase, el intérprete
Java busca un directorio en su CLASSPATH cuyo nombre coincida con el
nombre del paquete del que la clase es miembro. Los ficheros .class para
todas las clases e interfaces definidas en un paquete deben estar en ese
directorio de paquete.
Los
nombres de paquetes pueden contener varios componentes
(separados por puntos). De hecho, los nombres de los paquetes de Java
tienen varios componentes: java.util, java.lang, etc...
Cada
componente del nombre del paquete representa un directorio en el sistema de
ficheros. Así, los ficheros .class de java.util están en un
directorio llamado util en otro directorio llamado java en algún
lugar del CLASSPATH.
Para
ejecutar una aplicación Java, se especifica el nombre de la aplicación Java
que se desea ejecutar en el interprete Java. Para ejecutar un applet, se
especifica el nombre del applet en una etiqueta <APPLET> dentro de
un fichero HTML. El navegador que ejecute el applet pasa el nombre del applet al
intérprete Java. En cualquier caso, la aplicación o el applet que se está
ejecutando podría estár en cualquier lugar del sistema o de la red.
Igualmemente, la aplicación o el applet pueden utilizar otras clases y objetos
que están en la misma o diferentes localizaciones.
Como
las clases pueden estar en cualquier lugar, se debe indicar al interprete Java
donde puede encontrarlas. Se puede hacer esto con la variable de entorno
CLASSPATH que comprende una lista de directorios que contienen clases Java
compiladas. La construcción de CLASSPATH depende de cada sistema.
Cuando
el interprete obtiene un nombre de clase, desde la línea de comandos, desde un
navegador o desde una aplicación o un applet, el interprete busca en todos los
directorios de CLASSPATH hasta que encuentra la clase que está buscando.
Se
deberá poner el directorio de nivel más alto que contiene las clases Java en
el CLASSPATH. Por convención, mucha gente tiene un directorio de clases en su
directorio raíz donde pone todo su código Java. Si tu tienes dicho directorio,
deberías ponerlo en el CLASSPATH. Sin embargo, cuando se trabaja con applets,
es conveniente poner el applet en un directorio clases debajo del
directorio donde está el fichero HTML que contiene el applet. Por esta, y otras
razones, es conveniente poner
el
directorio actual en el CLASSPATH.
Las
clases incluidas en el entorno de desarrollo Java están disponibles automáticamente
porque el interprete añade el directorio correcto al CLASSPATH cuando arranca.
Observa
que el orden es importante. Cuando el interprete Java está buscando una clase,
busca por orden en los directorios indicados en CLASSPATH hasta que encuentra la
clase con el nombre correcto. El interprete Java ejecuta la primera clase con el
nombre correcto que encuentre y no busca en el resto de directorios. Normalmente
es mejor dar a las clases nombres únicos, pero si no se puede evitar, asegúrate
de que el CLASSPATH busca las clases en el orden apropiado. Recuerda esto cuando
selecciones tu CLASSPATH y el árbol del código fuente.
Para
importar una clase específica o un interface al fichero actual (como la clase
Circle desde el paquete graphics creado en la sección anterior) se utiliza la
sentencia de import.
import
graphics.Circle;
Esta
sentencia debe estar al principio del fichero antes de cualquier definición de
clase o de interface y hace que la clase o el interface esté disponible para su
uso por las clases y los interfaces definidos en el fichero.
Si
se quieren importar todas las clases e interfaces de un paquete, por ejemplo, el
paquete graphics completo, se utiliza la sentencia import con un
caracter comodín, un asterisco '*'.
import
graphics.*;
Si
intenta utilizar una clase o un interface desde un paquete que no ha sido
importado, el compilador mostrará un error.
Observe
que sólo las clases e intefaces declarados como públicos pueden ser utilizados
en clases fuera del paquete en el que fueron definidos.
El
paquete por defecto (un paquete sin nombre) siempre es importado. El sistema de
ejecución también importa automáticamente el paquete java.lang.
Si
por suerte, el nombre de una clase de un paquete es el mismo que el nombre de
una clase en otro paquete, se debe
evitar la ambigüedad de nombres precediendo el nombre de la clase con el nombre
del paquete. Por ejemplo, previamente se ha definido una clase llamada Rectangle
en el paquete graphics. El paquete java.awt también contiene una
clase Rectangle. Si estos dos paquetes son importados en la misma clase, el
siguiente código sería ambigüo.
Rectangle
rect;
En
esta situación se tiene que ser más específico e indicar exactamente que
clase Rectangle se quiere.
graphics.Rectangle
rect;
Se
puede hacer esto anteponiento el nombre del paquete al nombre de la clase y
separando los dos con un punto.
El
entorno de desarrollo estandard de Java comprende ocho paquetes.
El
paquete de lenguaje Java, también conocido como java.lang, contiene las
clases que son el corazón del lenguaje Java. Las clases de este paquete se
agrupan de la siguiente manera.
Object
El
abuelo de todas las clases--la clase de la que parten todas las demás.
Tipos
de Datos Encubiertos
Una
colección de clases utilizadas para encubrir variables de tipos primitivos:
Boolean, Character, Double, Float, Integer y Long. Cada una de estas clases es
una subclase de la clase abstracta Number.
Strings
Dos
clases que implementan los datos de caracteres.
System
y Runtime
Estas
dos clases permiten a los programas utilizar los recursos del sistema. System
proporciona un interface de programación independiente del sistema para
recursos del sistema y Runtime da acceso directo al entorno de ejecución específico
de un sistema.
Thread
Las
clases Thread, ThreadDeath y ThreadGroup implementan las capacidades multitareas
tan importantes en el lenguaje Java. El paquete java.lang también define el
interface Runnable. Este interface es conveniente para activar la clase Java sin
subclasificar la clase Thread.
Class
La
clase Class proporciona una descripción en tiempo de ejecución de una clase y
la clase ClassLoader permite cargar clases en los programas durante la ejecución.
Math
Una
librería de rutinas y valores matemáticos como pi.
Exceptions,
Errors y Throwable
Cuando
ocurre un error en un programa Java, el programa lanza un objeto que indica qué
problema era y el estado del interprete cuando ocurrió el error. Sólo los
objetos derivados de la clase Throwable pueden ser lanzados. Existen dos
subclasses principales de Throwable: Exception y Error. Exception es la forma
que deben intentar capturar los programas normales. Error se utiliza para los
errores catastróficos--los programas normales no capturan Errores. El paquete
java.lang contiene las clases Throwable, Exception y Error, y numerosas
subclases de Exception y Error que
representan problemas específicos.
Process
Los
objetos Process representan el proceso del sistema que se crea cuando se utiliza
el sistema en tiempo de ejecución para ejecutar comandos del sistema. El
paquete java.lang define e implementa la clase genérica Process.
El
compilador importa automáticamente este paquete. Ningún otro paquete se
importa de forma automática.
El
paquete I/O de Java (java.io) proporciona un juego de canales de entrada
y salida utilizados para leer y escribir ficheros de datos y otras fuentes de
entrada y salida.
Este
paquete, java.util, contiene una colección de clases útiles. Entre
ellas se encuentan muchas estructuras de datos genéricas (Dictionary, Stack,
Vector, Hashtable) un objeto muy útil para dividir cadenas y otro para la
manipulación de calendarios. El paquete java.util también contiene el
interface Observer y la clase Observable que permiten a los objetos notificarse
unos a otros cuando han cambiado. Las clases de java.util no se cubre en
este tutorial aunque algunos ejemplos utilizan estas clases.
El
paquete java.net contiene definiciones de clases e interfaces que
implementan varias capacidades de red. Las clases de este paquete incluyen una
clase que implementa una conexión URL. Se pueden utilizar estas clases para
implementar aplicaciones cliente-servidor y otras aplicaciones de
comunicaciones.
Este
paquete contiene la clase Applet -- la clase que se debe subclasificar si se
quiere escribir un applet. En este paquete se incluye el interface AudioClip que
proporciona una abstración de alto nivel para audio.
Tres
paquetes componen las herramientas para Ventanas Abstractas: java.awt, java.awt.image,
y java.awt.peer.
El
paquete java.awt proporciona elementos GUI utilizados para obtener
información y mostrarla en la pantalla como ventanas, botones, barras de
desplazamiento, etc..
El
paquete java.awt.image contiene clases e interfaces para manejar imágenes
de datos, como la selección de un modelo de color, el cortado y pegado, el
filtrado de colores, la selección del valor de un pixel y la grabación de
partes de la pantalla.
El
paquete java.awt.peer contiene clases e interfaces que conectan los
componentes AWT independientes de la plataforma a su implementación dependiente
de la plataforma (como son los controles de Microsoft Windows ).
El
paquete java.lang contiene dos clases de cadenas: String y StringBuffer. Ya
hemos visto la clase String. La clase String se utiliza cuando se trabaja con
cadenas que no pueden cambiar. Por otro lado, StringBuffer, se utiliza cuando se
quiere manipular el contenido de una cadena.
El
método reverseIt() de la siguiente clase utiliza las clases String y
StringBuffer para invertir los caracteres de una cadena. Si tenemos una lista de
palabras, se puede utilizar este método en conjunción de un pequeño programa
para crear una lista de palabras rítmicas (una lista de palabras ordenadas por
las silabas finales). Sólo se tienen que invertir las cadenas de la lista,
ordenar la lista e invertir las cadenas otra vez.
class
ReverseString {
public
static String reverseIt(String source) {
int
i, len = source.length();
StringBuffer
dest = new StringBuffer(len);
for
(i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return
dest.toString();
}
}
El
método reverseIt() acepta un argumento del tipo String llamado source
que contiene la cadena que se va a invertir. El método crea un
StringBuffer, dest, con el mismo tamaño que source. Luego hace un
bucle inverso sobre los caracteres de source y los añade a dest,
con lo que se invierte la cadena. Finalmente el método convierte dest,
de StringBuffer a String.
¿Por
qué dos clases String?
class
ReverseString {
public
static String reverseIt(String source) {
int
i, len = source.length();
StringBuffer
dest
= new StringBuffer(len);
for
(i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return
dest.toString();
}
}
El
entorno de desarrollo Java proporciona dos clases para manipular y almacenar
datos del tipo carácter: String, para cadenas constantes, y StringBuffer, para
cadenas que pueden cambiar. String se utiliza cuando no se quiere que cambie el
valor de la cadena. Por ejemplo, si escribimos un método que necesite una
cadena de caracteres y el método no va a modificar la cadena, deberíamos
utilizar un objeto String. Normalmente, querremos utilizar Strings para pasar
caracteres a un método y para devolver caracteres desde un método. El método reverseIt()
toma un String como argumento y devuelve un String.
La
clase StringBuffer proporcinada para cadenas variables; se utiliza cuando
sabemos que el valor de la cadena puede cambiar. Normalmente utilizaremos
StringBuffer para constuir datos de caracteres como en el método reverseIt().
Como
son constantes, los Strings son más económicos (utilizan menos memoria) que
los StringBuffers y pueder ser compartidos. Por eso es importante utilizar
String siempre que sea apropiado.
class
ReverseString {
public
static String reverseIt(String source) {
int
i, len = source.length();
StringBuffer
dest = new StringBuffer(len);
for
(i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return
dest.toString();
}
}
El
método reverseIt() crea un StringBuffer llamado dest cuya
longitud inicial es la misma que la de source. StringBuffer dest declara
al compilador que dest se va a utilizar para referirse a un objeto del
tipo StringBuffer, el operador new asigna memoria para un nuevo objeto y StringBuffer(len)
inicializa el objeto.
Muchos
Strings se crean a partir de cadenas literales. Cuando el compilador encuentra
una serie de caracteres entre comillas (" y "), crea un objeto String
cuyo valor es el propio texto. Cuando el compilador encuentra la siguente
cadena, crea un objeto String cuyo valor es Hola
Mundo.
"Hola
Mundo."
También
se pueden crear objetos String como se haría con cualquier otro objeto Java:
utilizando new.
new
String("Hola Mundo.");
El
método constructor utilizado por reverseIt() para incializar dest requiere
un entero como argumento que indique el tamaño inicial del nuevo StringBuffer.
StringBuffer(int
length)
reverseIt()
podría
haber utilizado el constructor por defecto para dejar indeterminada la longitud
del buffer hasta un momento posterior. Sin embargo, es más eficiente
especificar la longitud del buffer si se conoce, en vez de asignar memoria cada
vez que se añadan caracteres al buffer.
class
ReverseString {
public
static String reverseIt(String source) {
int
i, len = source.length();
StringBuffer
dest = new StringBuffer(len);
for
(i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return
dest.toString();
}
}
Las
variables de ejemplar de un objeto están encapsuladas dentro del objeto,
ocultas en su interior, seguras frente a la inspección y manipulación por
otros objetos. Con ciertas excepciones bien definidas, los métodos del objeto
no son los únicos a través de los cuales un objeto puede inspeccionar o
alterar las variables de otro objeto. La encapsulación de los datos de un
objeto lo protege de la corrupción de otros objetos y oculta los detalles de
implementación a los objetos extraños. Esta encapsulación de datos detrás de
los métodos de un objeto es una de las piedras angulares de la programación
orientada a objetos.
Los
métodos utilizados para obtener información de un objeto son conocidos como métodos
accesores. El método reverseIt() utiliza dos métodos accesores de
String para obtener información sobre el string source.
Primero
utiliza el método accesor: length() para obtener la longitud de la
cadena source.
int
len = source.length();
Observe
que a reverseIt() no le importa si el String mantiene su longitud como un
entero, como un número en coma flotante o incluso si calcula la longitud al
vuelo. reverseIt() simplemente utiliza el interface público del método length()
que devuelve la longitud del String como un entero. Es todo lo que necesita
saber reverseIt().
Segundo,
utiliza el método accesor: charAt() que devuelve el carácter que está
situado en la posición indicada en su argumento.
source.charAt(i)
El
carácter devuelto por charAt() es el que se añade al StringBuffer dest.
Como la variable del bucle i empieza al final de source y avanza
hasta el principio de la cadena, los caracteres se añaden en orden inverso al
StringBuffer.