sábado, 26 de mayo de 2012

5.5 Creación y manejo de excepciones definidas por el usuario.

Las excepciones predefinidas cubren las situaciones de error más habituales con las que nos podemos encontrar, relacionadas con el propio lenguaje y el hardware. Cuando se desarrollan aplicaciones existen otras situaciones de error de más ‘alto nivel’ relacionadas con la funcionalidad de nuestros programas. Imaginemos una aplicación informática que controla la utilización de los remontes de
una estación de esquí: los pases de acceso a los remontes son personales e intransferibles y dispondrán de un código de barras que los identifica. Cada vez que un usuario va a hacer uso de un remonte debe introducir su pase de acceso en una máquina de validación, que a cciona un torno y devuelve el pase.

 El sistema puede constar de un ordenador central al que le llegan telemáticamente los datos  correspondientes a los códigos de barras de los pases que en cada momento se están introduciendo en cada máquina de validación d e cada remonte; si un código de barras está en regla, el ordenador envía una orden de liberar el torno para permitir al usuario acceder al remonte. El ordenador central habitualmente recibirá códigos correctos utilizados en momentos adecuados, sin embargo, en ciertas ocasiones nos encontraremos con situaciones anómalas:

1 Código de barras ilegible

2 Código de barras no válido (por ejemplo correspondiente a un pase caducado)

3 Código de barras utilizado en otro remonte en un periodo de tiempo demasiado breve

4 etc.

DEFINICIÓN DE UNA EXCEPCIÓN DEFINIDA POR EL PROGRAMADOR.

En programación orientada a objetos lo más adecuado es que las excepciones sean objetos, por lo que en Java definiremos las excepciones como clases. Nuestras clases de excepción, en general, heredarán de la clase Exception. En las clases que nos creemos a partir de Exception, incluiremos al menos el constructor vacío y otro que contenga un String como argumento. Este String se inicializa automáticamente con el nombre de la clase; la inicialización se realiza en la superclase Throwable. El texto que pongamos al hacer uso del segundo constructor se añadirá al nombre de la clase insertado por la superclase.

UTILIZACIÓN DE UNA EXCEPCIÓN DEFINIDA POR EL PROGRAMADOR.

Una vez que disponemos de una excepción propia, podremos programar la funcionalidad de nuestras aplicaciones provocando (lanzando) la excepción cuando detectemos alguna de las situaciones anómalas asociadas.


Ejemplo:

//Demostracion del mecanismo de manejo de excepciones
//try...catch...finally

public class UsoDeExcepciones {
public static void main(String args[]){
try{
lanzaExcepcion();//llama al metodo lanzaExcepcion
}//fin de try
catch(Exception excepcion){ //excepcion lanzada por lanzaExcepcion
System.err.println("La excepcion se manejo en main ");
}//fin de catch
noLanzaExcepcion();
}//fin de main
//demuestra los bloques try...catch...finally

public static void lanzaExcepcion() throws Exception{
try{//lanza una excepcion y la atrapa de inmediato
System.out.println("Metodo lanzaExcepcion");
throw new Exception();//genera la excepcion
}//fin de try

catch (Exception excepcion){ //atrapa la excepcion lanzada en el bloque try
System.err.println("La excepcion se manejo en el metodo lanzaExcepcion ");
throw excepcion;//vuelve a lanzar para procesarla mas adelante
//no se llegaria al codigo que se coloque aqui, la excepcion se vuelve a lanzar en el bloque catch
}//fin catch
finally{//se ejecuta sin importar lo que ocurra en los bloques try..catch
System.err.println("Se ejecuto finally en lanzaExcepcion ");
}//din de finally
//no se llega al codigo que se coloque aqui, la excepcion se vuelve a lanzar en el bloque catch
}//fin del metodo lanzaException

//demuestra el uso de finally cuando no ocurre una excepcion
public static void noLanzaExcepcion(){
try{//el bloque try no lanza una excepcion
System.out.println("Metodo noLanzaExcepcion");
}//fin de try

catch(Exception excepcion){//no se ejecuta
System.err.println(excepcion);
}//fin de catch

finally{//se ejecuta sin importar lo que ocurra en los bloques try..catch
System.err.println("Se ejecuto Finally en noLanzaExcepcion");
}//fin de bloque finally

System.out.println("Fin del metodo noLanzaExcepcion");
}//fin del metodo noLanzaExcepcion

}//fin de la clase UsoDeExcepciones

5.4 Gestión de excepciones: manejo de excepciones, lanzamiento de excepciones.

Lanzamiento de excepciones: throw - throws

Muchas veces el programador dentro de un determinado método deberá comprobar si alguna condición de excepción se cumple, y si es así lanzarla. Para ello se utilizan las palabras reservadas throw y throws.
Por una parte la excepción se lanza mediante la sentencia throw:

if ( condicion_de_excepcion == true )
throw new miExcepcion
();

Se puede observar que hemos creado un objeto de la clase miExcepcion, puesto que las excepciones son objetos y por tanto deberán ser instanciadas antes de ser lanzadas.

Aquellos métodos que pueden lanzar excepciones, deben cuáles son esas excepciones en su declaración. Para ello se utiliza la sentencia throws:

tipo_devuelto miMetodoLanzador() throws miExcep1, miExcep2 {
// Codigo capaz de lanzar excepciones miExcep1 y miExcep2
}

Se puede observar que cuando se pueden lanzar en el método más de una excepción se
deben indicar en sus declaraciones separadas por comas.

Ejemplo:

Clase base
class Excepciones {

public static void main( String[] args ) {
// Para leer un fichero
FileInputStream entrada = null;

Lanzadora lanza = new Lanzadora();
int leo;

try {
entrada = new FileInputStream("fich.txt");
while ( ( leo = entrada.read() ) != -1 )
lanza.lanzaSiNegativo( leo );
System.out.println( "Todo fue bien" );
} catch ( MiExcepcion e ){ // Personalizada
System.out.println( "Excepcion: " + e.getMessage() );
} catch ( IOException e ){ // Estándar
System.out.println( "Excepcion: " + e.getMessage() );
} finally {
entrada.close(); // Así el fichero siempre queda cerrado
System.out.println( "Fichero cerrado" );
}
}
}

Clase derivada

class MiExcepcion extends Exception {
MiExcepcion(){
super(); // constructor por defecto de Exception
}
MiExcepcion( String cadena ){
super( cadena ); // constructor param. de Exception
}
}
// Esta clase lanzará la excepción
class Lanzadora {
void lanzaSiNegativo( int param ) throws MiExcepcion {
if ( param < 0 )
throw new MiExcepcion( "Numero negativo" );
}
}
class Excepciones {
public static void main( String[] args ) {
// Para leer un fichero
Lanzadora lanza = new Lanzadora();
FileInputStream entrada = null;
int leo;

try {
entrada = new FileInputStream( "fich.txt" );
while ( ( leo = entrada.read() ) != -1 )
lanza.lanzaSiNegativo( leo );
entrada.close();
System.out.println( "Todo fue bien" );
} catch ( MiExcepcion e ){ // Personalizada
System.out.println( "Excepcion: " + e.getMessage() );
} catch ( IOException e ){ // Estándar
System.out.println( "Excepcion: " + e.getMessage() );
} finally {
if ( entrada != null )

try {
entrada.close(); // Siempre queda cerrado
} catch ( Exception e ) {
System.out.println( "Excepcion: " + e.getMessage() );
}
System.out.println( "Fichero cerrado." );
}
}
}

5.3 Propagación De Excepciones.

Las instrucciones que tenemos dentro de un bloque try a menudo contienen llamadas a métodos que a su vez pueden realizar llamadas a otros métodos y así sucesivamente. Cualquiera de los métodos llamados puede provocar una excepción y cualquiera de los  métodos puede, o no, tratarla (con bloques catch). Ó Una excepción no tratada en un bloque se propaga hacia el bloque llamante. Este mecanismo de propagación continúa mientras no se trate la excepción o se llegue al método de nivel superior. Si la excepción no se trata en el método de nivel superior, se imprime un mensaje de error por consola.

Ejemplo:

try {
// codigo
Origen.CopiaFichero(Destino);
// codigo
}
catch (IOException e) {
System.out.println (“Error de lectura ¿Desea intentarlo de Nuevo?”);
..........
}
public void CopiaFichero (TipoFichero Destino) {
try {
// codigo
12
}
catch (IOException e) {
// cerrar ficheros, etc.
throw e;
}

5.2 Tipos de excepciones.

Existen varios tipos fundamentales de excepciones:

Error: Excepciones que indican problemas muy graves, que suelen ser no recuperables y no deben casi nunca ser capturadas.

Exception: Excepciones no definitivas, pero que se detectan fuera del tiempo de ejecución.

RuntimeException: Excepciones que se dan durante la ejecución del programa. 



.
Todas las excepciones tienen como clase base la clase Throwable, que está incluida en el paquete java.lang, y sus métodos son:

Trowable( String mensaje ); Constructor. La cadena es opcional

Throwable fillInStackTrace(); Llena la pila de traza de ejecución.

String getLocalizedMessage(); Crea una descripción local de este objeto.

String getMessage(); Devuelve la cadena de error del objeto.

void printStackTrace( PrintStream_o_PrintWriter s ); Imprime este objeto y su traza en el flujo del parámetro s, o en la salida estándar (por defecto).

String toString; Devuelve una breve descripción del objeto.


La superclase de todas las excepciones es la clase Throwable. Sólo las instancias de esta clase o alguna de sus subclases pueden ser utilizadas como excepciones. La clase Trowable tiene dos clases derivadas: Error y Exception.
La clase Exception sirve como superclase para crear excepciones de propósito específico (adaptadas a nuestras necesidades).

La clase Error sirve de superclase para una serie de clases derivadas ya definidas que nos informan de situaciones anormales relacionadas con errores de muy difícil recuperación producidos en el sistema.

La clase Exception tiene un amplio número de clases derivadas proporcionadas por el SDK, por ejemplo existen excepciones predefinidas para el uso de ficheros, de SQL, etc. De todas estas subclases, RuntimeExeption tiene una característica propia: no es necesario realizar un tratamiento explícito de estas excepciones (de todas las demás clases derivadas de Exception si es necesario). Esto es debido a que, al igual que con las excepciones derivadas de Error, existen pocas posibilidades de recuperar situaciones .

Entre las clases derivadas de RuntimeException se encuentran: anómalas de este tipo.


El siguiente gráfico muestra las clases más importantes en el uso de excepciones y su jerarquía . En las clases no sombreadas es obligatorio realizar un tratamiento explícito de las excepciones, en las clases sombreadas no es necesario este tratamiento


5.1 Definición.

En todo programa existen errores inesperados en tiempo de ejecución, y también errores que no consideramos debido a nuestra propia inexperiencia como programadores. Unos de estos errores ocurren por ejemplo, al intentar acceder a un elemento del arreglo que está fuera del límite de nuestro arreglo, o cuando intentamos acceder a un archivo inexistente, entre otros.

 Normalmente estos errores interrumpen el flujo de ejecución de nuestros programas, hasta el extremo de provocar la terminación del programa en forma inmediata. Java hace uso de las excepciones1.1 para poder controlar los errores en tiempo de ejecución. En Java, casi todo los tipos de errores que puedan surgir en tiempo de ejecución lanzan excepciones, es decir, cuando ocurre un error dentro de un método de JAva, este método crea un objeto Exception, dicho objeto contiene información sobre la excepción, que incluye su tipo y el estado del programa cuando ocurrió el error. El sistema de ejecución es el responsable de buscar algún código para manejar el error. El manejo de excepciones en Java sigue una estructura como esta:

try {
//Codigo donde puede ocurrir un error
}
catch (ExcepcionA ex) { // Que se va a hacer en caso que
se lanze una Excepcion A }
...
catch (ExcepcionZ ex) { // Que se va a hacer en caso que
se lanze una Excepcion Z }

Mediante las excepciones se podrá evitar repetir continuamente código, en busca de un posible error, y avisar a otros objetos de una condición anormal de ejecución durante un programa.

Las excepciones señalan errores o situaciones poco habituales en la ejecución de un programa, por ejemplo una división de un valor entre cero, un intento de acceso a un String declarado, pero no instanciado, etc.

Ejemplo :

import java.util.Scanner;
import java.util.InputMismatchException;
public class usodeExcepciones {
    public static int cociente (int numerador, int denominador) throws ArithmeticException{
        return numerador/denominador;
    }
    public static void main(String[] args) {
        Scanner explorador = new Scanner(System.in);
        boolean continuarCiclo=true;
   do{
   try{
       System.out.print("Introduce un numero entero= ");
       int numerador = explorador.nextInt();
       System.out.print("Introduce un denominador entero= ");
       int denominador = explorador.nextInt();
       int resultado = cociente (numerador, denominador);
       System.out.printf("Resultado de %d / %d = %d", numerador, denominador, resultado);
       System.out.println();
       continuarCiclo=false;
       //fin del bloque try
   }
   catch(InputMismatchException objError){
       System.err.printf("Exception: %s\n", objError);
       explorador.nextLine();
       System.out.println("Debe de introducir valores enteros. Intente de nuevo");
   }
   catch(ArithmeticException objaritmetico){
       System.out.printf("Exception: %s\n", objaritmetico);
       System.out.println("Cero es un denominador invalido. Intente de nuevo");
   }
   }while (continuarCiclo=true);
         }
}

5 Excepciones.

El término excepción es un forma corta da la frase “suceso excepcional” y puede definirse de la siguiente forma.

Definición:

Una excepción es un evento que ocurre durante la ejecución del programa que interrumpe el flujo normal de las sentencias.
Muchas clases de errores pueden utilizar excepciones — desde serios problemas de hardware, como la avería de un disco duro, a los simples errores de programación, como tratar de acceder a un elemento de un array fuera de sus límites. Cuando dicho error ocurre dentro de un método Java, el método crea un objeto ‘exception’ y lo maneja fuera, en el sistema de ejecución. Este objeto contiene información sobre la excepción, incluyendo su tipo y el estado del programa cuando ocurrió el error. El sistema de ejecución es el responsable de buscar algún código para manejar el error. En terminología java, crear una objeto exception y manejarlo por el sistema de ejecución se llama lanzar una excepción.

Algunas excepciones mas comunes


4.5 Reutilización de código.

Lo primero que se les viene a la cabeza a los estudiantes (y a muchos profesionales) cuando se les menciona la reutilización del código es el famoso copiar y pegar al que se han acostumbrado en la programación estructurada, y de echo muchos lo hacen en poo, lo cual es una de las practicas que más encarece el desarrollo de software. Como todo en Java, el problema se resuelve con las clases. Para reutilizar el código creamos nuevas clases pero, en lugar de partir de cero, partimos de clases, relacionadas con nuestra clase, que han sido ya creadas y depuradas. El truco está en usar las clases sin ensuciar el código existente.

Una forma de hacer esto es crear objetos de nuestras clases existentes dentro de la nueva clase. Esto se conoce como composición porque la nueva clase está compuesta de objetos de clases existentes. Estamos reutilizando la funcionalidad del código, y no la forma.
Otra forma es crear una nueva clase como un tipo de una clase ya existente. Tomamos la forma de la clase existente y añadimos código a la nueva, sin modificar la clase existente. Esta forma de crear nuevos objetos se llamada herencia, y lo que hacemos es extender la clase en la que nos basamos para crear la nueva.


Composición:

Hasta ahora hemos usado la composición de cierta manera, ej. cuando hacemos una interfaz gráfica de usuario, nuestra clase de interfaz gráfica esta compuesta por un frame, unos panel, botones, etc. todos estos objetos componen el objeto de interfaz gráfica. Es decir que la composición consiste en poner manejadores de objetos dentro de nuestra clase, estos manejadores de objetos no serán otra cosa que instancias de las clases en las que nos estamos basando para crear la nueva clase.

4.4 Variables polimórficas (plantillas): definición, uso y aplicaciones.

En Java, las variables que contienen objetos son variables polimórficas. El término «polimórfico»  (literalmente: muchas formas) se refiere al hecho de que una misma variable puede contener objetos de diferentes tipos (del tipo declarado o de cualquier subtipo del tipo declarado). El polimorfismo aparece en los lenguajes orientados a objetos en numerosos contextos, las variables polimórficas constituyen justamente un primer ejemplo.

Observemos la manera en que el uso de una variable polimórfica nos ayuda a simplificar nuestro método listar. El cuerpo de este método es:

for (Elemento elemento : elementos)
elemento.imprimir();
En este método recorremos la lista de elementos (contenida en un ArrayList mediante la variable elementos), tomamos cada elemento de la lista y luego invocamos su método imprimir.El uso de herencia en este ejemplo ha eliminado la necesidad de escribir dos ciclos en el método listar. La herencia evita la duplicación de código no sólo en las clases servidoras sino también en las clases clientes de aquellas.

public class CD
{
private String title;
private String artist;
private String comment;
CD(String theTitle, String theArtist)
{
title = theTitle;
artist = theArtist;
comment = " ";
}
void setComment(String newComment)
{ ... }
String getComment()
{ ... }
void print()
{ ... }
...
}


public class DVD
{
private String title;
private String director;
private String comment;
DVD(String theTitle, String theDirector)
{
title = theTitle;
director = theDirector;
comment = " ";
}
void setComment(String newComment)
{ ... }
String getComment()
{ ... }
void print()
{ ... }
...
}

class Database {
private ArrayList<CD> cds;
private ArrayList<DVD> dvds;
...
public void list()
{
for(CD cd : cds) {
cd.print();
System.out.println();  }
for(DVD dvd : dvds) {
dvd.print();
System.out.println();  }
}
}

4.3 Interfaces: definición, implementación de interfaces, herencia de interfaces.

El concepto de Interface lleva un paso más adelante la idea de las clases abstractas. En Java una interface es una clase abstracta pura, es dcir una clase donde todos los métodos son abstractos (no se implementa ninguno).

Permite al diseñador de clases establecer la forma de una clase (nombres de métodos, listas de argumentos y tipos de retorno, pero no bloques de código). Una interface puede también contener datos miembro, pero estos son siempre static y final. Una interface sirve para establecer un 'protocolo' entre clases. Para crear una interface, se utiliza la palabra clave interface en lugar de class. La interface puede definirse public o sin modificador de acceso, y tiene el mismo significado que para las clases. Todos los métodos que declara una interface son siempre public. Para indicar que una clase implementa los métodos de una interface se utiliza la palabra clave implements. El compilador se encargará de verificar que la clase efectivamente declare e implemente todos los métodos de la interface.

Una clase puede implementar más de una interface. Declaración y uso Una interface se declara:

interface nombre_interface {
tipo_retorno nombre_metodo ( lista_argumentos ) ;
. . .
}

Por ejemplo:

interface InstrumentoMusical {
void tocar();
void afinar();
String tipoInstrumento();
}

Y una clase que implementa la interface:

class InstrumentoViento extends Object implements
InstrumentoMusical {
void tocar() { . . . };
void afinar() { . . .};
String tipoInstrumento() {}
}

class Guitarra extends InstrumentoViento {
String tipoInstrumento() {
return "Guitarra";
}
}


Referencias a Interfaces

Es posible crear referencias a interfaces, pero las interfaces no pueden ser instanciadas. Una referencia a una interface puede ser asignada a cualquier objeto que implemente la interface.

Extensión de interfaces
Las interfaces pueden extender otras interfaces y, a diferencia de las clases, una interface puede extender más de una interface. La sintaxis es:

interface nombre_interface extends nombre_interface , . . . {
tipo_retorno nombre_metodo ( lista_argumentos ) ;
. . .
}

Agrupaciones de constantes

Dado que, por definición, todos los datos miembros que se definen en una interface son static y final, y dado que las interfaces no pueden instanciarse resultan una buena herramienta para implantar grupos de constantes.

public interface Meses {
int ENERO = 1 , FEBRERO = 2 . . . ;
String [] NOMBRES_MESES = { " " , "Enero" , "Febrero" , . . . };
}

Esto puede usarse simplemente:

System.out.println(Meses.NOMBRES_MESES[ENERO]);

4.2 Clases abstractas: definición, métodos abstractos, implementación de clases abstractas, modelado de clases abstractas.

Las clases abstractas en Java


Una de las características más útiles de cualquier lenguaje orientado a objetos es la posibilidad de declarar clases que definen como se utiliza solamente, sin tener que implementar método. Esto en Java se hace mediante interfaces y con clases abstractas.

Una clase abstracta es una clase de la que no se puede crear objetos. La utilidad de estas clases estriba en que otras clases hereden de ésta, por lo que con ello conseguiremos reutilizar código. Para declarar una clase como abstracta utilizamos la palabra clave abstract.

En método abstract no pude ser static, ya que estos no pueden ser redifinidos por las subclases.

Clases abstractas


Una clase que declara la existencia de métodos pero no la implementación de dichos métodos (o sea, las llaves { } y las sentencias entre ellas), se considera una clase abstracta.

Una clase abstracta puede contener métodos no-abstractos pero al menos uno de los métodos debe ser declarado abstracto.

Para declarar una clase o un metodo como abstractos, se utiliza la palabra
reservada abstract.

abstract class ejemplo

{
abstract void miMetodo(int var1, int var2);
String miOtroMetodo( ){ ... }
}

Una clase abstracta no se puede instanciar pero si se puede heredar y las clases hijas serán las encargadas de agregar la funcionalidad a los métodos abstractos. Si no lo hacen así, las clases hijas deben ser también abstractas.

4.1 Definición.

Conceptos sobre polimorfismo y programación orientada a objetos

El polimorfismo, en programación orientada a objetos, se refiere a la posibilidad de acceder a un variado rango de funciones distintas a través del mismo interfaz. O sea, un mismo identificador puede tener distintas formas (distintos cuerpos de función, distintos comportamientos) dependiendo del contexto en el que se halle. El polimorfismo se puede establecer mediante sobrecarga, sobreescritura y enlace dinámico.

Sobrecarga

Este término se refiere al uso del mismo identificador u operador en distintos contextos y con distintos significados. Si para cada funcionalidad necesitada fuese necesario escribir un método, el código resultante sería inmanejable. Supongamos que los desarrolladores de Java hubiesen creado un método para escribir en pantalla una cadena de texto, otro diferente para escribir un entero, otro para un doble, y así para todas las combinaciones posibles, sería casi imposible conocer dichos métodos en totalidad. En cambio, con “System.out.print()” o “System.out.println()” podemos escribir cualquier mensaje en pantalla.

Este tipo de codificación nos es permitido gracias a la sobrecarga, la cual se aplica a métodos y constructores.

La sobrecarga de métodos hace que un mismo nombre pueda representar distintos métodos con distinto tipo y número de parámetros, manejados dentro de la misma clase. En el ámbito de la POO, la sobrecarga de métodos se refiere a la posibilidad de tener dos o más métodos con el mismo nombre pero distinta funcionalidad. Es decir, dos o más métodos con el mismo nombre realizan acciones diferentes y el compilador usará una u otra dependiendo de los parámetros usados. Esto también se aplica a los constructores (de hecho, es la aplicación más habitual de la sobrecarga).

Sobreescritura

La sobreescritura se aplica a los métodos y está directamente relacionada a la herencia; se refiere a la redefinición de los métodos de la clase base en las subclases.

Enlace dinámico

Esto permite invocar operaciones en objetos obviando el tipo actual de éstos hasta el momento de ejecutar el código. O sea, nos permite definir elementos como un tipo e instanciarlos como un tipo heredado.







4 Polimorfismo.

El concepto de Polimorfismo es uno de los fundamentos para cualquier lenguaje orientado a Objetos, las mismas raíces de la palabra pueden ser una fuerte pista de su significado: Poli = Multiple, morfismo= Formas , esto implica que un mismo Objeto puede tomar diversas formas.
A través del concepto de Herencias ("Inheritance") es posible ilustrar este comportamiento:


El poder manipular un Objeto como si éste fuera de un tipo genérico otorga mayor flexibilidad al momento de programar con Objetos, el término Polimorfismo también es asociado con un concepto llamado Late-Binding (Ligamiento Tardío), observe el siguiente fragmento de código:

       Figura a = new Circulo();
     Figura b = new Triangulo();

Inicialmente se puede pensar que este código generaría un error debido a que el tipo de referencia es distinta a la instancia del objeto, sin embargo, el fragmento anterior es correcto y demuestra el concepto de Polimorfismo.

3.6 Redefinición de métodos en clases derivadas

El lenguaje java permite redefinir miembros de la clase base en las clases derivadas, pero el compilador emite una advertencia cuando detecta una redefinición. Una advertencia (warning) es un mensaje del compilador acerca de un posible problema. Sin embargo, en este caso sí se genera código ejecutable (a diferencia del mensaje de error). Redefinición de campos. El siguiente ejemplo muestra cómo reutilizar los identificadores de los campos de la clase base en una clase derivada.
Ejemplo:

class Base

public static void Main( )
{

Punto a = new Punto( );
Punto3D b = new Punto3D( );

a.x = 10 ;
a.y = 20 ;

b.x = 30 ;
b.y = 40 ;
b.z = 50 ;

}
}

Clase punto

class Punto
{
public int x;
public int y;

}

class Punto3D : Punto
{

public int x ;
public int y ;
public int z ;

}

viernes, 25 de mayo de 2012

3.5 Constructores y destructores en clases derivadas.

Constructores en clases derivadas 

Al instanciar objetos de clases derivadas se inicia una cadena de invocaciones a constructores en las cuales el constructor de la clase derivada, antes de realizar sus propias tareas, invoca (ya sea implícita o explícitamente) al constructor de su clase base. Similarmente, si la clase base fue derivada de otra clase, el constructor de la clase base debe invocar al constructor de la clase ubicada en el siguiente nivel superior de la jerarquía, y así sucesivamente. El último constructor invocado en la cadena es el constructor de la clase Object, cuyo cuerpo se ejecuta primero. El cuerpo del constructor de la clase derivada se ejecuta al final. El constructor de cada clase base inicializa las variables de instancia que el objeto de la clase derivada hereda. 

Destructores en clases derivadas 

Cuando remueve de la memoria un objeto de una clase derivada, el recolector de basura invoca al destructor del objeto. Esto inicia una cadena de invocaciones a destructores, en donde el destructor de la clase derivada y los destructores de las clases bases directas e indirectas se ejecutan en orden inverso al que se ejecutaron los constructores, esto es, primero se ejecuta el destructor de la clase derivada y al final se ejecuta el destructor de la clase base ubicada en el nivel superior de la jerarquía. La ejecución de los destructores debe liberar todos los recursos que el objeto adquirió, antes de que el recolector de basura reclame la memoria de ese objeto. Cuando el recolector de basura invoca al destructor de un objeto de una clase derivada, ese destructor realiza su tarea y después invoca al destructor de la clase base. El proceso se repite hasta que se invoca al destructor de la clase Object.


Ejemplo:

Clase base

package ejemploconstructor;

public class Main {

    public static void main(String[] args) {

 persona Persona=new persona ("ejemplo \n","de\n","constructor");
              Persona.muestraNombre();
    }
}

Clase escuela

package ejemploconstructor;


public class escuela {
   public String nombre;
   public String direccion;
   public String cedula;

//contructor
public escuela(String nombre, String direccion, String cedula){

    this.nombre=nombre;
    this.direccion=direccion;
    this.cedula=cedula;
}

public void muestraNombre(){
    System.out.printf("Nombre es : %s",this.nombre);
     System.out.printf("apellido es : %s",this.direccion);
      System.out.printf("correo es : %s",this.cedula);
      System.out.println();
}
protected void finalize(){
    System.out.print("Datos destruidos");
}
}

Clase persona

package ejemploconstructor;


public class persona {
   private String nombre;
   private String apellido;
   private String correo;
//contructor
public persona(String nombre, String apellidos, String correo){

    this.nombre=nombre;
    this.apellido =apellidos;
    this.correo=correo;
}
public void muestraNombre(){
    System.out.printf("Nombre es : %s",this.nombre);
     System.out.printf("apellido es : %s",this.apellido);
      System.out.printf("correo es : %s",this.correo);
      System.out.println();
}
protected void finalize(){
    System.out.print("Datos destruidos");
}
}

3.4 Referencia al objeto de la clase base


La jerarquía de clases que describen las figuras planas

Consideremos las figuras planas cerradas como el rectángulo, y el círculo. Tales figuras comparten características comunes como es la posición de la figura, de su centro, y el área de la figura, aunque el procedimiento para calcular dicha área sea completamente distinto. Podemos por tanto, diseñar una jerarquía de clases, tal que la clase base denominada Figura, tenga las características comunes y cada clase derivada las específicas. La relación jerárquica se muestra en la figura
La clase Figura es la que contiene las características comunes a dichas figuras concretas por tanto, no tiene forma ni tiene área. Esto lo expresamos declarando Figura como una clase abstracta, declarando la función miembro area abstract.
Las clases abstractas solamente se pueden usar como clases base para otras clases. No se pueden crear objetos pertenecientes a una clase abstracta. Sin embargo, se pueden declarar variables de dichas clases.
En el juego del ajedrez podemos definir una clase base denominada Pieza, con las características comunes a todas las piezas, como es su posición en el tablero, y derivar de ella las características específicas de cada pieza particular. Así pues, la clase Pieza será una clase abstracta con una función abstract denominada mover, y cada tipo de pieza definirá dicha función de acuerdo a las reglas de su movimiento sobre el tablero.

Uso de la jerarquía de clases

Creamos un objeto c de la clase Circulo situado en el punto (0, 0) y de 5.5 unidades de radio. Calculamos y mostramos el valor de su área.

        Circulo c=new Circulo(0, 0, 5.5);
        System.out.println("Area del círculo "+c.area());

Creamos un objeto r de la clase Rectangulo situado en el punto (0, 0) y de dimensiones 5.5 de anchura y 2 unidades de largo. Calculamos y mostramos el valor de su área.

        Rectangulo r=new Rectangulo(0, 0, 5.5, 2.0);
        System.out.println("Area del rectángulo "+r.area());

Veamos ahora, una forma alternativa, guardamos el valor devuelto por new al crear objetos de las clases derivadas en una variable f del tipo Figura (clase base).
        Figura f=new Circulo(0, 0, 5.5);
        System.out.println("Area del círculo "+f.area());
        f=new Rectangulo(0, 0, 5.5, 2.0);
        System.out.println("Area del rectángulo "+f.area());




3.3 Reutilización de miembros heredados.


La reutilización de código se refiere al comportamiento y a las técnicas que garantizan que una parte o la totalidad de un programa informático existente se pueda emplear en la construcción de otro programa. De esta forma se aprovecha el trabajo anterior, se economiza tiempo, y se reduce la redundancia.

La manera más fácil de reutilizar código es copiarlo total o parcialmente desde el programa antiguo al programa en desarrollo. Pero es trabajoso mantener múltiples copias del mismo código, por lo que en general se elimina la redundancia dejando el código reusable en un único lugar, y llamándolo desde los diferentes programas. Este proceso se conoce como abstracción. La abstracción puede verse claramente en las bibliotecas de software, en las que se agrupan varias operaciones comunes a cierto dominio para facilitar el desarrollo de programas nuevos. Hay bibliotecas para convertir información entre diferentes formatos conocidos, acceder a dispositivos de almacenamiento externos, proporcionar una interfaz con otros programas, manipular información de manera conocida (como números, fechas, o cadenas de texto).
Para que el código existente se pueda reutilizar, debe definir alguna forma de comunicación o interfaz. Esto se puede dar por llamadas a una subrutina, a un objeto, o a una clase.

Como se ha comentado anteriormente la clase descendiente puede añadir sus propios atributos y métodos pero también puede sustituir u ocultar los heredados. En concreto:
1. Se puede declarar un nuevo atributo con el mismo identificador que uno heredado, quedando este atributo oculto. Esta técnica no es recomendable.
2. Se puede declarar un nuevo método de instancia con la misma cabecera que el de la clase ascendiente, lo que supone su sobreescritura. Por lo tanto, la sobreescritura o redefinición consiste en que métodos adicionales declarados en la clase descendiente con el mismo nombre, tipo de dato devuelto y número y tipo de parámetros sustituyen a los heredados.
3. Se puede declarar un nuevo método de clase con la misma cabecera que el de la clase ascendiente, lo que hace que éste quede oculto. Por lo tanto, los métodos de clase o estáticos (declarados como static) no pueden ser redefinidos.
4. Un método declarado con el modificador final tampoco puede ser redefinido por una clase derivada.
5. Se puede declarar un constructor de la subclase que llame al de la superclase de forma implícita o de mediante la palabra reservada super.
6. En general puede accederse a los métodos de la clase ascendiente que han sido redefinidos empleando la palabra reservada super delante del identificador del método. Este mecanismo sólo permite acceder al metodo perteneciente a la clase en el nivel inmediatamente superior de la jerarquía de clases.



3.2 Clasificación. herencia simple, herencia múltiple.


Herencia 

La herencia es un mecanismo que permite la definición de una clase a partir de la definición de otra ya existente. La herencia permite compartir automáticamente métodos y datos entre clases, subclases y objetos. 
La herencia está fuertemente ligada a la reutilización del código en la OOP. Esto es, el código de cualquiera de las clases puede ser utilizado sin más que crear una clase derivada de ella, o bien una subclase. 
Hay dos tipos de herencia: Herencia Simple y Herencia Múltiple. La primera indica que se pueden definir nuevas clases solamente a partir de una clase inicial mientras que la segunda indica que se pueden definir nuevas clases a partir de dos o más clases iniciales. Java sólo permite herencia simple.


Herencia Múltiple en Java

Todos sabemos que la herencia es un mecanismo propio de la programación orientada a objetos POO. Nos permite crear clases a partir de otra, las cuales las unen vínculos sumamente estrechos, casi de familia. Es por ello que se llama herencia.


La herencia múltiple en java no es soportada nativamente. Sin embargo muchos autores y desarrolladores la simulan utilizando la palabra reservada implements e interfaces, que sirve para implementar o cubrir una clase con respecto a otra.




jueves, 24 de mayo de 2012

3.1 Definición: Clase base, Clase derivada.


La clase base

Vamos a poner un ejemplo del segundo tipo, que simule la utilización de liberías de clases para crear un interfaz gráfico de usuario como Windows 3.1 o Windows 95.
Supongamos que tenemos una clase que describe la conducta de una ventana muy simple, aquella que no dispone de título en la parte superior, por tanto no  puede desplazarse, pero si cambiar de tamaño actuando con el ratón en los bordes derecho e inferior.
La clase Ventana tendrá los siguientes miembros dato: la posición x e y de la ventana, de su esquina superior izquierda y las dimensiones de la ventana: ancho y alto.

package ventana;


public class Ventana {
protected int x;
protected int y;
protected int ancho;
protected int alto;


public Ventana(int x, int y, int ancho, int alto) {
this.x=x;
this.y=y;
this.ancho=ancho;
this.alto=alto;
}
public void mostrar(){
System.out.println("posición : x="+x+", y="+y);
System.out.println("dimensiones : w="+ancho+", h="+alto);
}
public void cambiarDimensiones(int dw, int dh){
ancho+=dw;
alto+=dh;
}
}



La clase derivada

Incrementamos la funcionalidad de la clase Ventana definiendo una clase derivada denominada VentanaTitulo. Los objetos de dicha clase tendrán todas las características de los objetos de la clase base, pero además tendrán un título, y se podrán desplazar (se simula el desplazamiento de una ventana con el ratón).



La clase derivada heredará los miembros dato de la clase base y las funciones miembro, y tendrá un miembro dato más, el título de la ventana.



public class VentanaTitulo extends Ventana{

protected String titulo;

public VentanaTitulo(int x, int y, int w, int h, String nombre) 
{
super(x, y, w, h);
titulo=nombre;
}

extends es la palabra reservada que indica que la clase VentanaTitulo deriva, o es una subclase, de la clase Ventana.  La primera sentencia del constructor de la clase derivada es una llamada al constructor de la clase base mediante la palabra reservada super. La llamada super (x, y, w, h);
En la clase derivada se define una función que tiene el mismo nombre y los mismos parámetros que la de la clase base. Se dice que redefinimos la función mostrar en la clase derivada. La función miembro mostrar de la clase derivada VentanaTitulo hace una llamada a la función mostrar de la clase base Ventana, mediante super.mostrar();

3. Herencia.


La herencia en java

Java permite el empleo de la herencia, característica muy potente que permite definir una clase tomando como base a otra clase ya existente. Esto es una de las bases de la reutilización de código, en lugar de copiar y pegar.

En java, como ya vimos la herencia se especifica agregando la claúsula extends después del nombre de la clase. En la claúsula extends indicaremos el nombre de la clase base de la cuál queremos heredar.

Al heredar de una clase base, heredaremos tanto los atributos como los métodos, mientras que los constructores son utilizados, pero no heredados.

Ejemplo :

Clase Base
package ejemploherencia;

public class Main {
    public String dueño;
    protected int puerta;
    protected int ruedas;

    protected void caracteristicas(){
        System.out.print(" Es deporitvo");
    }  
    public static void main(String[] args) {
      boolean acceso=true;
      auto obj=new auto(acceso);
        obj.tipo();
    }
}

Clase auto


package ejemploherencia;



public class auto extends Main {
    private boolean descapotable;
    public auto(boolean acceso){
        descapotable=acceso;
    }

public void tipo(){
    if(descapotable){
        caracteristicas();

   }
    else
        System.out.print("no es deportivo");
}
}


2.7 Sobrecarga de operadores: Concepto y utilidad, operadores Unarios y Binarios.


La sobrecarga de operadores es la capacidad para transformar los operadores de un lenguaje como por ejemplo el +, -, etc, cuando se dice transformar se refiere a que los operandos que entran en juego no tienen que ser los que admite el lenguaje por defecto. Mediante esta técnica podemos sumar dos objetos creados por nosotros o un objeto y un entero, en vez de limitarnos a sumar números enteros o reales, por ejemplo.
La sobrecarga de operadores ya era posible en c++ y en otros lenguajes, pero sorprendentemente java no lo incorpora, así que podemos decir que esta característica es una ventaja de c# respecto a java, aunque mucha gente, esta posibilidad, no lo considera una ventaja porque complica el código.

A la hora de hablar de operadores vamos a distinguir entre dos tipos, los unarios y los binarios. Los unarios son aquellos que solo requieren un operando, por ejemplo a++, en este caso el operando es 'a' y el operador '++'. Los operadores binarios son aquellos que necesitan dos operadores, por ejemplo a+c , ahora el operador es '+' y los operandos 'a' y 'c'. Es importante esta distinción ya que la programación se hará de forma diferente.

Los operadores que podemos sobrecargar son los unarios, +, -, !, ~, ++, --; y los binarios +, -, *, /, %, &, |, ^, <<, >>. Es importante decir que los operadores de comparación, ==, !=, <, >, <=, >=, se pueden sobrecargar pero con la condición que siempre se sobrecargue el complementario, es decir, si sobrecargamos el == debemos sobrecargar el !=

Operadores Unarios

En esta sección se verá cómo sobrecargar los operadores unarios, es decir aquellos que toman un solo operando, como por ejemplo a++. El prototipo de los métodos que van a sobrecargar operadores unarios será:
public static Operando operator++(Operando a)
 Como antes sustituyendo el ++ por cualquier operador unario. El ejemplo dentro de nuestra clase de números complejos sería:

public static ComplexNum operator++(ComplexNum a) 
         {
         
           float auximg = a.Img;
           float auxreal = a.Real;
         
           return new ComplexNum(++auxreal, ++auximg);
         }

Operadores binarios

Para empezar vamos a sobrecargar el operador suma('+') para que al sumar dos objetos de la clase ComplexNum, es decir dos números complejos obtengamos un número complejo que será la suma de ambas partes. Cabe destacar que los prototipos para sobrecargar operadores serán:
public static Operando operator+(Operando a, Operando b)
Este es el prototipo para el operador +, el resto de operadores binarios van a seguir el mismo patrón. Por tanto el código del método de sobrecarga será el siguiente:

public static ComplexNum operator+(ComplexNum a, ComplexNum b) 
         {
           return new ComplexNum(a.Real + b.Real, a.Img + b.Img);
         }

Este método sobrecarga el operador suma para que podamos sumar dos números complejos. Un dato a tener en cuenta es que los métodos que sobrecargan operadores deben ser static. Como se ve en el código los operandos son 'a' y 'b', que se reciben como parámetro y el resultado de la operación es otro número complejo que es el que retorna el método. Por tanto se limita a crear un nuevo número complejo con ambas partes operadas. De la misma forma podemos crear la sobrecarga del operador resta('-') para que lleve a cabo la misma función:

public static ComplexNum operator-(ComplexNum a, ComplexNum b) 
         {
           return new ComplexNum(a.Real - b.Real, a.Img - b.Img);
         }

Como vemos el método es idéntico solo que sustituyendo los + por -. En este caso el trabajo que hacemos dentro del método es trivial pero podría ser tan complejo como se quisiera.

2.6 Sobrecarga de métodos.


Un método sobrecargado se utiliza para reutilizar el nombre de un método pero con diferentes argumentos (opcionalmente un tipo diferente de retorno). Las reglas para sobrecargar un método son las siguientes:

  •  Los métodos sobrecargados deben de cambiar la lista de argumentos.
  •  Pueden cambiar el tipo de retorno.
  •  Pueden cambiar el modificador de acceso.
  •  Pueden declarar nuevas o más amplias excepciones.
  •  Un método puede ser sobrecargado en la misma clase o en una subclase.


Veamos un método que se desea sobrecargar:

public void cambiarTamano(int tamano, String nombre, float patron){ }

Los siguientes métodos son sobrecargas legales del método cambiarTamano():

public void cambiarTamano(int tamano, String nombre){}
public int cambiarTamano(int tamano, float patron){}
public void cambiarTamano(float patron, String nombre) throws IOException{}

Cómo invocar un método sobrecargado:

Lo que define qué método es el que se va a llamar son los argumentos que se envían al mismo durante la llamada. Si se invoca a un método con un String como argumento, se ejecutará el método que tome un String como argumento, si se manda a llamar al mismo método pero con un float como argumento, se ejecutará el método que tome un float como argumento y así sucesivamente. Si se invoca a un método con un argumento que no es definido en ninguna de las versiones sobrecargadas entonces el compilador arrojará un mensaje de error.
Reglas de la sobrecarga y sobreescritura de métodos:
Ahora que hemos visto ambas formas de reescribir métodos, revisemos las reglas y diferencias entre ambos tipos de reescritura:
  • Argumentos: En un método sobrecargado los argumentos deben de cambiar mientras que en un método sobreescrito NO deben cambiar.
  •  El tipo de retorno: En un método sobrecargado el tipo de retorno puede cambiar, en un método sobreescrito NO puede cambiar, excepto por subtipos del tipo declarado originalmente.
  •  Excepciones: En un método sobrecargado las excepciones pueden cambiar, en un método sobreescrito pueden reducirse o eliminarse pero NO deben de arrojarse excepciones nuevas o más amplias.
  •  Acceso: En un método sobrecargado puede cambiar, en un método sobreescrito el acceso NO debe de hacerse más restrictivo (puede ser menos restrictivo).
  •  Al invocar: En un método sobrecargado los argumentos son los que determinan qué método es el que se invocará, en un método sobreescrito el tipo de objeto determina qué método es elegido.


Ejemplo :

clase base

package sobrekarga;


public class Main {


    public static void main(String[] args) {
        // TODO code application logic here
        articulo objarticulo=new articulo();
        objarticulo.fijarprecio("70");
        objarticulo.mostrarprecio();


        edad objedad= new edad();
        objedad.poneredad();
        objedad.mostraredad();
    }
}

Clase articulo

package sobrekarga;

public class articulo {
    private double precio;

public void fijarprecio(){
precio=5.50;

}
public void fijarprecio(String precio){

System.out.println("No es un valor");
}
public void fijaprecio (double precio){
    this.precio=precio;
}
public void mostrarprecio(){
    System.out.printf("precio es %.2f",precio);
    System.out.println();
}
}

Clase edad

package sobrekarga;

public class edad {
private int edad;

 public void poneredad(){
     edad= 22;
}
 public void poneredad(String edad){
     System.out.println("no es una edad");
 }
 public void poneredad(int edad){
     this.edad= edad;
 }
 public void mostraredad(){
     System.out.printf("la edad es : %d ",edad);
     System.out.println();
 }
}