domingo, 5 de abril de 2009

CLASES EN JAVA

Concepto de Clase

Una clase es una agrupación de datos (variables o campos) y de funciones (métodos) que operan sobre esos datos.
public class Classname
{ ... // definición de variables y métodos
}

Un objeto (instance) es un ejemplar concreto de una clase.

Las características de una clase son las siguientes:
1) Todas las variables y funciones de Java deben pertenecer a una clase.

2) Si una clase deriva de otra (extends), hereda todas sus variables y métodos.

3) Java tiene una jerarquía de clases estándar de la que pueden derivar las clases que crean los usuarios.

4) En Java no hay herencia múltiple.

5) En un fichero se pueden definir varias clases, pero en un fichero no puede haber más que una clase public.

6) Si una clase contenida en un fichero no es public, no es necesario que el fichero se llame como la clase.
7) Los métodos de una clase pueden referirse de modo global al objeto de esa clase al que se aplican por medio de la referencia this.

8) Las clases se pueden agrupar en packages, introduciendo una línea al comienzo del fichero (package packageName;).

Variables miembro

Variables miembro de objeto

Variables miembro de clase (static). Son aquellas variables propias de la clase. Se crean anteponiendo la palabra static a su declaración, y se suelen utilizar para definir constantes comunes para todos los objetos de la clase.

Las variables de clase son lo más parecido que Java tiene a las variables globales de C/C++.

Variables finales

Variables que no pueden cambiar su valor a lo largo de la ejecución del programa. Puede ser considerada como una constante, y equivale a la palabra const de C/C++.

Java permite separar la definición de la inicialización de una variable final; es decir, la inicialización puede hacerse más tarde, en tiempo de ejecución, llamando a métodos o en función de otros datos.

Métodos (funciones miembro)

Los métodos son funciones definidas dentro de una clase.

La primera línea de la definición de un método se llama declaración o header; el código comprendido entre las llaves { ... } es el cuerpo o body del método.
public returnValue functionName(arg1,arg2,...)
{ // comienzo del método
... // cuerpo de la función
} // final del método

El header consta del cualificador de acceso (public, en este caso), del tipo del valor de retorno, del nombre de la función y de una lista de argumentos explícitos entre paréntesis, separados por comas.
Métodos de objeto
Se aplican siempre a un objeto de la clase por medio del operador punto (.). Dicho objeto es su argumento implícito.

Métodos de clase (static).

Métodos que no actúan sobre objetos concretos a través del operador punto. Los métodos de clase pueden recibir objetos de su clase como argumentos explícitos, pero no tienen argumento implícito ni pueden utilizar la referencia this.

Métodos sobrecargados (overloaded).

Métodos que tienen el mismo nombre, pero que se diferencian por el número y/o tipo de los argumentos.

Constructores

Un constructor es un método que se llama automáticamente cada vez que se crea un objeto de una clase. La principal misión del constructor es reservar memoria e inicializar las variables miembro de la clase.

Los constructores no tienen valor de retorno (ni siquiera void) y su nombre es el mismo que el de la clase. Su argumento implícito es el objeto que se está creando.

Una clase tiene varios constructores, que se diferencian por el tipo y número de sus argumentos (métodos sobrecargados). Se llama constructor por defecto al constructor que no tiene argumentos.

Inicializadores

Línea de actuación para evitar que haya variables sin inicializar correctamente, y estos pueden ser static (para la clase) o de objeto.

Inicializadores de clase (static).

Es un algo parecido a un método que se llama automáticamente al crear la clase. Se diferencia del constructor en que no es llamado para cada objeto, sino una sola vez para toda la clase.

Los tipos primitivos pueden inicializarse directamente con asignaciones en la clase o en el constructor, pero para inicializar objetos o elementos más complicados es bueno utilizar un inicializador, ya que permite gestionar excepciones (situaciones de error) con try... catch.

Los inicializadores static se crean dentro de la clase, como métodos sin nombre, sin argumentos y sin valor de retorno; tan solo es la palabra static y el código entre llaves { ... }.

Inicializadores de objeto.

No llevan la palabra static y se utilizan para las clases anónimas, que por no tener nombre no pueden tener constructor. En este caso, los inicializadores de objeto se llaman cada vez que se crea un objeto de la clase anónima.

Destrucción de objetos (liberación de memoria)

En Java no hay destructores como en C++. El sistema se ocupa automáticamente de liberar memoria de los objetos que ya han perdido la referencia, esto es, objetos que ya no tienen ningún nombre que permita acceder a ellos, por ejemplo por haber llegado al final del bloque en el que habían sido definidos, porque a la referencia se le ha asignado el valor null o porque a la referencia se le ha asignado la dirección de otro objeto. A esta característica se le llama recogida de basura (garbage collection).

Finalizadores

Son métodos que vienen a complementar la labor del garbage collector. Un finalizador es un método de objeto (no static), sin valor de retorno (void), sin argumentos, con nombre de función finalize(), y que se llama automáticamente cuando se va a destruir un objeto (antes de que la memoria sea liberada de modo automático por el sistema). Se utilizan para ciertas operaciones de terminación distintas de liberar memoria (por ejemplo: cerrar ficheros, cerrar conexiones de red, etc.)

Packages

Un package es una agrupación de clases.

Para que una clase pase a formar parte de un package llamado pkgName, hay que introducir en ella la sentencia: package pkgName; que debe ser la primera sentencia del fichero sin contar comentarios y líneas en blanco.

Los nombres de los packages se suelen escribir con minúsculas para distinguirlos de las clases.

Todas las clases que forman parte de un package deben estar en el mismo directorio. Los nombres compuestos de los packages están relacionados con la jerarquía de directorios en que se guardan las clases. Package permite que los nombres de las clases de Java sean únicos en Internet y así evitar conflictos.

Las clases de un package se almacenan en un directorio con el mismo nombre largo (path) que el package. Por ejemplo, la clase es.ceir.jgjalon.infor2.ordenar.QuickSort.class debería estar en el directorio CLASSPATH\es\ceir\jgjalon\infor2\ordenar\QuickSort.class, donde CLASSPATH es una variable de entorno del PC que establece la posición absoluta de los directorios en los que hay clases de Java (clases del sistema o de usuario), en este caso la posición del directorio es en los discos locales del ordenador.

Los packages se utilizan con las finalidades siguientes:

1) Para agrupar clases relacionadas.

2) Para evitar conflictos de nombres ya que el dominio de nombres de Java es la Internet.

3) Para ayudar en el control de la accesibilidad de clases y miembros.

Concepto de Herencia

Construir una clase a partir de otra. Para indicar que una clase deriva de otra se utiliza la palabra extends, como por ejemplo: class CirculoGrafico extends Circulo { ... }

Cuando una clase deriva de otra, hereda todas sus variables y métodos. Estas funciones y variables miembro pueden ser redefinidas(overridden) en la clase derivada, que puede también definir o añadir nuevas variables y métodos.

La clase Object

Es la raíz de toda la jerarquía de clases de Java. Como consecuencia, todas las clases tienen algunos métodos que han heredado de Object.

Clases y métodos abstractos

Una clase abstracta (abstract) es una clase de la que no se pueden crear objetos. Su utilidad es permitir que otras clases deriven de ella, proporcionándoles un marco o modelo que deben seguir y algunos métodos de utilidad general. Las clases abstractas se declaran anteponiéndoles la palabra abstract, como por ejemplo public abstract class Geometria { ... }

Una clase abstract puede tener métodos que no son abstract, pero si una clase tiene algún método abstract es obligatorio que la clase sea abstract.

Clases y métodos finales

Una clase declarada final no puede tener clases derivadas. Esto se puede hacer por motivos de seguridad y también por motivos de eficiencia, porque cuando el compilador sabe que los métodos no van a ser redefinidos puede hacer optimizaciones adicionales.

Análogamente, un método declarado como final no puede ser redefinido por una clase que derive de su propia clase.
Interfaces

Una interface es un conjunto de declaraciones de métodos (sin definición).

Ejemplo:

Código : Cruz Tipo

Hilos en Java

A veces necesitamos que nuestro programa Java realice varias cosas simultáneamente. Otras veces tiene que realizar una tarea muy pesada, por ejemplo, consultar en el listín telefónico todos los nombres de chica que tengan la letra n, que tarda mucho y no deseamos que todo se quede parado mientras se realiza dicha tarea. Para conseguir que Java haga varias cosas a la vez o que el programa no se quede parado mientras realiza una tarea compleja, tenemos los hilos (Threads).

Crear un Hilo

Crear un hilo en java es una tarea muy sencilla. Basta heredar de la clase Thread y definir el método run(). Luego se instancia esta clase y se llama al método start() para que arranque el hilo. Más o menos esto

public MiHilo extends Thread
{
public void run()
{
// Aquí el código pesado que tarda mucho
}
};
...
MiHilo elHilo = new MiHilo();
elHilo.start();
System.out.println("Yo sigo a lo mio");

Listo. Hemos creado una clase MiHilo que hereda de Thread y con un método run(). En el método run() pondremos el código que queremos que se ejecute en un hilo separado. Luego instanciamos el hilo con un new MiHilo() y lo arrancamos con elHilo.start(). El System.out que hay detrás se ejecutará inmediatamente después del start(), haya terminado o no el código del hilo.

Detener un hilo


Suele ser una costumbre bastante habitual que dentro del método run() haya un bucle infinito, de forma que el método run() no termina nunca. Por ejemplo, supongamos un. Cuando estás chateando, el programa que tienes entre tus manos está haciendo dos cosas simultáneamente. Por un lado, lee el teclado para enviar al servidor del chat todo lo que tú escribas. Por otro lado, está leyendo lo que llega del servidor del chat para escribirlo en tu pantalla. Una forma de hacer esto es "por turnos"

while (true)
{
leeTeclado();
enviaLoLeidoAlServidor();
leeDelServidor();
muestraEnPantallaLoLeidoDelServidor();
}

Esta, desde luego, es una opción, pero sería bastante "cutre", tendríamos que hablar por turnos. Yo escribo algo, se le envía al servidor, el servidor me envía algo, se pinta en pantalla y me toca a mí otra vez. Si no escribo nada, tampoco recibo nada. Quizás sea buena opción para los que no son ágiles leyendo y escribiendo, pero no creo que le guste este mecanismo a la mayoría de la gente.

Lo normal es hacer dos hilos, ambos en un bucle infinito, leyendo (del teclado o del servidor) y escribiendo (al servidor o a la pantalla). Por ejemplo, el del teclado puede ser así

public void run()
{
while (true)
{
String texto = leeDelTeclado();
enviaAlServidor(texto);
}
}

Esta opción es mejor, dos hilos con dos bucles infinitos, uno encargado del servidor y otro del teclado.

Ahora viene la pregunta del millón. Si queremos detener este hilo, ¿qué hacemos?. Los Thread de java tienen muchos métodos para parar un hilo: detroy(), stop(), suspend() ... Pero, si nos paramos a mirar la API de Thread, nos llevaremos un chasco. Todos esos métodos son inseguros, están obsoletos, desaconsejados o las tres cosas juntas.

¿Cómo paramos entonces el hilo?

La mejor forma de hacerlo es implementar nosotros mismos un mecanismo de parar, que lo único que tiene que hacer es terminar el método run(), saliendo del bucle.

Un posible mecanismo es el siguiente

public class MiHilo extends Thread
{
// boolean que pondremos a false cuando queramos parar el hilo
private boolean continuar = true;

// metodo para poner el boolean a false.
public void detenElHilo()
{
continuar=false;
}

// Metodo del hilo
public void run()
{
// mientras continuar ...
while (continuar)
{
String texto = leeDelTeclado();
enviaAlServidor(texto);
}
}
}

Simplemente hemos puesto en la clase un boolean para indicar si debemos seguir o no con el bucle infinito. Por defecto a true. Luego hemos añadido un método para cambiar el valor de ese boolean a false. Finalmente hemos cambiado la condición del bucle que antes era true y ahora es continuar.

Para parar este hilo, es sencillo

MiHilo elHilo = new MiHilo();
elHilo.start();
// Ya tenemos el hilo arrancado
...
// Ahora vamos a detenerlo

elHilo.detenElHilo();

lunes, 30 de marzo de 2009

Hashtable en JAVA

Hashtable

Una Hashtable es una implementación concreta de un Dictionary. Se puede utilizar una instancia de Hashtable para almacenar objetos arbitrarios que están indexados por cualquier otro objeto arbitrario. La utilización mas habitual de una Hashtable es utilizar una String como clave para almacenar objetos como valores. El ejemplo siguiente crea una Hashtable para almacenar información acerca de este libro:

import java.util.Dictionary;
import java.util.Hashtable;

class HTDemo {
public static void main(String args[]) {
Hashtable ht = new Hashtable();
ht.put("title", "Manual de Java");
ht.put("author", "Algunos");
ht.put("email", "nisesabe@puesalli.es");
show(ht);
}
static void show(Dictionary d) {
System.out.println("Título: " + d.get("title");
System.out.println("Autor: " + d.get("author");
System.out.println("E-mail: " + d.get("email");
}
}

La salida de este programa muestra cómo el método show, que toma un Dictionay abstracto como parámetro, es capaz de recuperar todos los valores que se han almacenado en el método main.

Properties

Properties en una subclase de Hashtable que añade algunos métodos adecuados para obtener valores que puede que no estén definidos. Se puede especificar un valor por omisión junto con el nombre en el método getProperty; por ejemplo, getProperty("nombre","valor por omisión"). Si no se encuentra la propiedad "nombre", se devuelve "valor por omisión". Además, cuando se construye un objeto Properties, se le puede pasar otra instancia de Properties en el constructor para que se utilice como propiedades por omisión de la nueva instancia.

Vector en JAVA

Vector

Vector es parte del paquete java.util de la librería estándar de clases de Java. Ofrece un servicio similar a un arreglo, ya que se pueden almacenar y accesar valores y referencias a través de un índice. Pero mientras un arreglo es de cierto tamaño dado, un objeto de tipo Vector puede dinámicamente crecer y decrecer conforme se vaya necesitando. Un elemento puede insertarse y eliminarse de una posición específica a través de la invocación de un sólo método.

A diferencia de un arreglo, un Vector no está declarado para ser de un tipo particular. Un objeto de tipo Vector maneja una lista de referencias a la clase Object, así no pueden almacenarse tipos de datos primitivos.


Algunos de los métodos de la clase Vector se muestran a continuación:

Vector ( )
Constructor: crea un vector inicialmente vacío
void addElement (Objet obj)
Inserta el objeto especificado al final del vector
void setElementAt (Object obj, int indíce)
Inserta el objeto específicado en el vector en la posición específicada
Object remove (int indíce)
Elimina el objeto que se encuentra en la posición específicada y lo regresa
boolean removeElement (Object obj)
Elimina la primera occurencia del objeto específicado en el vector
void removeElementAt (int indíce)
Elimina el objeto específicado en el índice del vector
void clear ( )
Elimina todos los objetos del vector
boolean contains (Object obj)
Regresa verdadero si el objeto dado pertenece al vector
int indexOf (Object obj)
Regresa el índice del objeto específicado. Regresa -1 si no fue encontrado el objeto
Object elementAt (int indíce)
Regresa el componente en el índice específicado
boolean isEmpty ( )
Regresa verdadero si el vector no contiene elementos
int size ( )
Regresa el número de elementos en el vector


Ejemplo:


import java.util.Vector;
/**
* Demuestra el uso de un objeto de la clase Vector
*/

public class Beatles
{
public static void main ()
{
Vector band = new Vector ();
band.addElement ("Paul");
band.addElement ("Pete");
band.addElement ("John");
band.addElement ("George");

System.out.println (band);

band.removeElement ("Pete");

System.out.println (band);
System.out.println ("En la posición 1 está: " + band.elementAt (1));

band.insertElementAt ("Ringo", 2);

System.out.println ("Tamaño de la banda: " + band.size ());
for (int i = 0; i <>

Si se necesitan añadir valores de datos primitivos a un Vector se pueden utilizar las clases conocidas como envoltorios que son: Integer, Long, Double y Float. Sus métodos de conversión respectivos son: intValue ( ), longValue ( ), doubleValue ( ) y floatValue ( ).

Bitset en JAVA

Los contenedores tipo bitset están pensados para almacenar bits. Es decir, conjuntos de N valores 0/1 denominados máscaras de bits ("Bitmask types"). En cierto sentido un bitset es el contenedor más simple del mundo, contiene un número máximo N (determinado en el momento de su definición) de elementos tipo bit. Podríamos considerar que un bitset es como una matriz de bits, de forma que podría establecerse un cierto paralelismo:

bit mb[125]; // L1: matriz de bits de 125 elementos

bitset <125> bs; // L2: bitset de 125 elementos


Naturalmente la sentencia L1 es solo didáctica, no es posible en C++ porque no existe un tipo bit nativo. En cambio L2 si es correcta; define un contenedor bs de tipo bitset de 125 elementos.

La clase proporciona una serie de métodos y operadores para manipular los miembros del contenedor. Dada la naturaleza de sus miembros, estas operaciones son muy limitadas, como cambiar un valor 0 por un 1 y cosas por el estilo. Son las operaciones de bits ("Bitwise operations") que hemos tenido ocasión de ver anteriormente.

Nota: al referirse a los valores de los miembros, además de cero y uno, es frecuente encontrar expresiones como fijar ("set") poner a 1, y limpiar ("reset/clear") poner a cero.


Podría pensarse que este contenedor es innecesario, ya que los campos de bits pueden ser adecuadamente representados por los tipos enteros sin signo int, short y long, y manejados mediante los operadores estándar de manejo de bits. Sin embargo, los objetos de la clase bitset presentan ciertas ventajas. Entre otras, que pueden manejar campos de bits de cualquier longitud, mientras que los operadores estándar de manejo de bits quedan restringidos como máximo a los tipos long. (32 bits en la mayoría de plataformas). Además, a cambio de un inevitable incremento del código respecto al que se deriva de utilizar directamente tipos simples, se consigue mayor comodidad de manejo. Por ejemplo, es posible crear directamente una máscara de bits en la forma:

string s1 = "10101101";
bitset<16> bs1(s1);
bitset<16> bs2("10101101");

mientras que la construcción de una máscara equivalente utilizando los métodos convencionales nos obligaría a hacer:

unsigned short mb = 62125;

lo que supone contar en binario para establecer que el número 62125 es el que corresponde a la máscara de bits deseada.

En cierto modo, este contenedor es un caso excepcional dentro de la STL. En el sentido que no proporciona ningún iterador para recorrer sus elementos. Pero esto no significa que no puede aplicársele ninguno de los algoritmos ofrecidos por la librería. Como en el caso de las matrices, sus elementos pueden ser accedidos mediante el operador subíndice [ ], que permite utilizarlo como si fuese un puntero, y por ende, un iterador.

La clase bitset

La descripción de esta familia de tipos responde al prototipo siguiente :

template class bitset { };


El tipo size_t depende de la implementación. Pero en el compilador Borland C++ 5.5 está definido como:

typedef unsigned int size_t;

Habida cuenta que en el citado compilador:

#define UINT_MAX ULONG_MAX // maximum unsigned int value

y que:

#define ULONG_MAX 4294967295UL // maximum unsigned long value

parece razonable pensar que este tipo de contenedor permite almacenar un número suficientemente grande de elementos (4 Gbits) para cubrir la mayoría de las necesidades prácticas.

La definición se encuentra en el fichero <bitset> que responde al siguiente esquema:

template class bitset;
template bitset

operator&(const bitset&, const bitset&);
template bitset

operator|(const bitset&, const bitset&);
template bitset

operator^(const bitset&, const bitset&);
template
basic_istream&
operator>>(basic_istream& is, bitset& x);
template
basic_ostream&
operator<<(basic_ostream& os, const bitset& x);


Como puede verse, aparte de la definición de la plantilla bitset propiamente dicha, esta cabecera contiene la definición de otras funciones-operador auxiliares para realizar operaciones entre objetos de tipo bitset. Por supuesto, todas estas definiciones se realizan el subespacio std. Observe que estas funciones-operador están definidas como funciones externas (no son métodos de la clase).

Como veremos a continuación, además de los anteriores, la clase bitset dispone de otra serie de métodos públicos auxiliares que permiten realizar diversas operaciones sobre los objetos (bits) alojados en estos contenedores. Pero debe recordar que la mayoría de operadores binarios solo son aplicables cuando el tamaño (número de bits) de ambos operandos coincide.