Está en la página 1de 41

PROGRAMACION ORIENTADA A OBJETOS

1.1 Conceptos

Conceptos

La programación orientada a objetos o POO (OOP según sus siglas en inglés) es un


paradigma de programación que usa los objetos en sus interacciones, para diseñar
aplicaciones y programas informáticos. Está basado en varias técnicas, incluyendo
herencia, cohesión, abstracción, polimorfismo, acoplamiento y encapsulamiento. Su uso se
popularizó a principios de la década de los años 1990. En la actualidad, existe una gran
variedad de lenguajes de programación que soportan la orientación a objetos.

Introducción

Los objetos son entidades que tienen un determinado estado, comportamiento


(método) e identidad:

 El estado está compuesto de datos o informaciones; serán uno o varios atributos a


los que se habrán asignado unos valores concretos (datos).
 El comportamiento está definido por los métodos o mensajes a los que sabe
responder dicho objeto, es decir, qué operaciones se pueden realizar con él.
 La identidad es una propiedad de un objeto que lo diferencia del resto; dicho con
otras palabras, es su identificador (concepto análogo al de identificador de una
variable o una constante).

Un objeto contiene toda la información que permite definirlo e identificarlo frente a otros
objetos pertenecientes a otras clases e incluso frente a objetos de una misma clase, al poder
tener valores bien diferenciados en sus atributos. A su vez, los objetos disponen de
mecanismos de interacción llamados métodos, que favorecen la comunicación entre ellos.
Esta comunicación favorece a su vez el cambio de estado en los propios objetos. Esta
característica lleva a tratarlos como unidades indivisibles, en las que no se separa el estado
y el comportamiento.

Los métodos (comportamiento) y atributos (estado) están estrechamente relacionados


por la propiedad de conjunto. Esta propiedad destaca que una clase requiere de métodos
para poder tratar los atributos con los que cuenta. El programador debe pensar
indistintamente en ambos conceptos, sin separar ni darle mayor importancia a alguno de
ellos. Hacerlo podría producir el hábito erróneo de crear clases contenedoras de información
por un lado y clases con métodos que manejen a las primeras por el otro. De esta manera se
estaría realizando una programación estructurada camuflada en un lenguaje de
programación orientado a objetos.

La POO difiere de la programación estructurada tradicional, en la que los datos y los


procedimientos están separados y sin relación, ya que lo único que se busca es el
procesamiento de unos datos de entrada para obtener otros de salida. La programación
estructurada anima al programador a pensar sobre todo en términos de procedimientos o
funciones, y en segundo lugar en las estructuras de datos que esos procedimientos manejan.
En la programación estructurada solo se escriben funciones que procesan datos. Los
programadores que emplean POO, en cambio, primero definen objetos para luego enviarles
mensajes solicitándoles que realicen sus métodos por sí mismos.
Conceptos fundamentales

La programación orientada a objetos es una forma de programar que trata de encontrar una
solución a estos problemas. Introduce nuevos conceptos, que superan y amplían conceptos
antiguos ya conocidos. Entre ellos destacan los siguientes:

Clase

Definiciones de las propiedades y comportamiento de un tipo de objeto concreto. La


instanciación es la lectura de estas definiciones y la creación de un objeto a partir de ellas.

Herencia

(Por ejemplo, herencia de la clase C a la clase D) es la facilidad mediante la cual la clase D


hereda en ella cada uno de los atributos y operaciones de C, como si esos atributos y
operaciones hubiesen sido definidos por la misma D. Por lo tanto, puede usar los mismos
métodos y variables públicas declaradas en C. Los componentes registrados como
"privados" (private) también se heredan, pero como no pertenecen a la clase, se mantienen
escondidos al programador y sólo pueden ser accedidos a través de otros métodos públicos.
Esto es así para mantener hegemónico el ideal de POO.

Objeto

Instancia de una clase. Entidad provista de un conjunto de propiedades o atributos (datos) y


de comportamiento o funcionalidad (métodos), los mismos que consecuentemente
reaccionan a eventos. Se corresponden con los objetos reales del mundo que nos rodea, o
con objetos internos del sistema (del programa). Es una instancia a una clase.

Método

Algoritmo asociado a un objeto (o a una clase de objetos), cuya ejecución se desencadena


tras la recepción de un "mensaje". Desde el punto de vista del comportamiento, es lo que el
objeto puede hacer. Un método puede producir un cambio en las propiedades del objeto, o
la generación de un "evento" con un nuevo mensaje para otro objeto del sistema.

Evento

Es un suceso en el sistema (tal como una interacción del usuario con la máquina, o un
mensaje enviado por un objeto). El sistema maneja el evento enviando el mensaje adecuado
al objeto pertinente. También se puede definir como evento la reacción que puede
desencadenar un objeto; es decir, la acción que genera.

Atributos

Características sobresalientes de un objeto del mundo real.

Mensaje

Una comunicación dirigida a un objeto, que le ordena que ejecute uno de sus métodos con
ciertos parámetros asociados al evento que lo generó.
Propiedad o atributo

Contenedor de un tipo de datos asociados a un objeto (o a una clase de objetos), que hace
los datos visibles desde fuera del objeto y esto se define como sus características
predeterminadas, y cuyo valor puede ser alterado por la ejecución de algún método.

Estado interno

Es una variable que se declara privada, que puede ser únicamente accedida y alterada por un
método del objeto, y que se utiliza para indicar distintas situaciones posibles para el objeto
(o clase de objetos). No es visible al programador que maneja una instancia de la clase.

Componentes de un objeto

Atributos, identidad, relaciones y métodos.

Identificación de un objeto

Un objeto se representa por medio de una tabla o entidad que esté compuesta por sus
atributos y funciones correspondientes.

En comparación con un lenguaje imperativo, una "variable" no es más que un contenedor


interno del atributo del objeto o de un estado interno, así como la "función" es un
procedimiento interno del método del objeto.

Características de la POO

Existe un acuerdo acerca de qué características contempla la "orientación a objetos". Las


características siguientes son las más importantes:

Abstracción

Denota las características esenciales de un objeto, donde se capturan sus comportamientos.


Cada objeto en el sistema sirve como modelo de un "agente" abstracto que puede realizar
trabajo, informar y cambiar su estado, y "comunicarse" con otros objetos en el sistema sin
revelar cómo se implementan estas características. Los procesos, las funciones o los
métodos pueden también ser abstraídos, y, cuando lo están, una variedad de técnicas son
requeridas para ampliar una abstracción. El proceso de abstracción permite seleccionar las
características relevantes dentro de un conjunto e identificar comportamientos comunes
para definir nuevos tipos de entidades en el mundo real. La abstracción es clave en el
proceso de análisis y diseño orientado a objetos, ya que mediante ella podemos llegar a
armar un conjunto de clases que permitan modelar la realidad o el problema que se quiere
atacar.

Encapsulamiento

Significa reunir todos los elementos que pueden considerarse pertenecientes a una misma
entidad, al mismo nivel de abstracción. Esto permite aumentar la cohesión de los
componentes del sistema. Algunos autores confunden este concepto con el principio de
ocultación, principalmente porque se suelen emplear conjuntamente.
Modularidad

Se denomina modularidad a la propiedad que permite subdividir una aplicación en partes


más pequeñas (llamadas módulos), cada una de las cuales debe ser tan independiente como
sea posible de la aplicación en sí y de las restantes partes. Estos módulos se pueden
compilar por separado, pero tienen conexiones con otros módulos. Al igual que la
encapsulación, los lenguajes soportan la modularidad de diversas formas.

Principio de ocultación

Cada objeto está aislado del exterior, es un módulo natural, y cada tipo de objeto expone
una interfaz a otros objetos que especifica cómo pueden interactuar con los objetos de la
clase. El aislamiento protege a las propiedades de un objeto contra su modificación por
quien no tenga derecho a acceder a ellas; solamente los propios métodos internos del objeto
pueden acceder a su estado. Esto asegura que otros objetos no puedan cambiar el estado
interno de un objeto de manera inesperada, eliminando efectos secundarios e interacciones
inesperadas. Algunos lenguajes relajan esto, permitiendo un acceso directo a los datos
internos del objeto de una manera controlada y limitando el grado de abstracción. La
aplicación entera se reduce a un agregado o rompecabezas de objetos.

Polimorfismo

Comportamientos diferentes, asociados a objetos distintos, pueden compartir el mismo


nombre; al llamarlos por ese nombre se utilizará el comportamiento correspondiente al
objeto que se esté usando. O, dicho de otro modo, las referencias y las colecciones de
objetos pueden contener objetos de diferentes tipos, y la invocación de un comportamiento
en una referencia producirá el comportamiento correcto para el tipo real del objeto
referenciado. Cuando esto ocurre en "tiempo de ejecución", esta última característica se
llama asignación tardía o asignación dinámica. Algunos lenguajes proporcionan medios
más estáticos (en "tiempo de compilación") de polimorfismo, tales como las plantillas y la
sobrecarga de operadores de C++.

Herencia

Las clases no están aisladas, sino que se relacionan entre sí, formando una jerarquía de
clasificación. Los objetos heredan las propiedades y el comportamiento de todas las clases a
las que pertenecen. La herencia organiza y facilita el polimorfismo y el encapsulamiento,
permitiendo a los objetos ser definidos y creados como tipos especializados de objetos
preexistentes. Estos pueden compartir (y extender) su comportamiento sin tener que volver
a implementarlo. Esto suele hacerse habitualmente agrupando los objetos en clases y estas
en árboles o enrejados que reflejan un comportamiento común. Cuando un objeto hereda de
más de una clase se dice que hay herencia múltiple.

Recolección de basura

La recolección de basura o garbage collector es la técnica por la cual el entorno de objetos


se encarga de destruir automáticamente, y por tanto desvincular la memoria asociada, los
objetos que hayan quedado sin ninguna referencia a ellos. Esto significa que el programador
no debe preocuparse por la asignación o liberación de memoria, ya que el entorno la
asignará al crear un nuevo objeto y la liberará cuando nadie lo esté usando. En la mayoría
de los lenguajes híbridos que se extendieron para soportar el Paradigma de Programación
Orientada a Objetos como C++ u Object Pascal, esta característica no existe y la memoria
debe desasignarse expresamente.

Tipos de clases

Una parte muy importante de la programación orientada a objetos son las clases, si no fuera
por ellas ni siquiera habría objetos. Cada una tiene sus propias características y ventajas. Un
programador que conoce estas características sabe cuando debe usar una y no otra, de
manera que su desarrolla un buen software. Los tipos de clases son:

 Clase definida por Atributo(Class. Public): Son muy comunes, accesibles desde
cualquier otra clase en la misma librería (de otro modo hay que importarlas).
 Clase definida por Operaciones(Class. Abstract): Aquellas que tienen por lo
menos un método abstracto. No implementan sus métodos, sino que dan las bases
para que sean implementados en la herencia.
 Clase de Servicio(Class. Final): Son las que terminan la cadena de herencia. Útiles
por motivos de seguridad y eficiencia de un programa, ya que no permiten crear
más sub-divisiones por debajo de esta clase.
 Class. Synchronizable: Especifica que sus métodos son sincronizados, evitando
problemas con los thread (hilo de ejecución), de forma que estos no pueden empezar
a correr un método si no ha acabado el otro.

Lección 1.1: La Programación orientada a objetos


Ha obtenido 20 punto(s) sobre 20 hasta ahora.
1.2 Ambito de una Variable

Hola mundo

Ámbito de una Variable


En programación, una variable está formada por un espacio en el sistema de almacenaje
(memoria principal de un ordenador) y un nombre simbólico (un identificador) que está
asociado a dicho espacio.
Ese espacio contiene una cantidad o información conocida o desconocida, es decir un valor.
El nombre de la variable es la forma usual de referirse al valor almacenado: esta separación
entre nombre y contenido permite que el nombre sea usado independientemente de la
información exacta que representa.

Tipos de datos
Debido a que las variables contienen o apuntan a valores de tipos determinados, las
operaciones sobre las mismas y el dominio de sus propios valores están determinadas por el
tipo de datos en cuestión. Algunos tipos de datos usados:
         Tipo de dato lógico.

·         Tipo de dato entero.

·         Tipo de dato de coma flotante (real, con decimales).


·         Tipo de dato carácter.

·         Tipo de dato cadena

Generalmente ejemplos de tipos primitivos son:


·         char (carácter)

·         int (entero)

·         float (real (coma flotante))

Otros tipos de datos que pueden ser considerados primitivos ya que la mayoría de lenguajes
de programación así los proporcionan (aunque no todos) son:
·         booleano (lógico: Verdadero, Falso)

·         string (cadena de caracteres)

·         Puntero (dirección de memoria)

Variables y paso de parámetros


Las variables pueden ser intercambiadas entre rutinas, por valor y por referencia:
·  Por valor .- Se copia el valor (el dato) de la variable en la zona de la pila de llamadas —
de ámbito local— que corresponde a la nueva subrutina llamada. Por tanto, esta
subrutina obtiene dicho valor pero no puede modificar la variable original. Esto
significa que si la variable sufre alteraciones dentro de esta rutina, para poder acceder a
dichas modificaciones al finalizar, deberá devolver el nuevo valor de la misma. Si no se
realiza esta operación, el valor de la variable será exactamente el mismo que tenía antes
de pasar por la función.

·  Por referencia .- No se pasa el valor directamente de la variable, si no una referencia


o puntero a la misma —que contiene la dirección de la zona de memoria donde se aloja
el contenido—, de tal modo que se opera directamente sobre la zona de memoria que la
contiene, lo cual implica que las modificaciones que sufra serán accesibles a posteriori.
Comportamientos diferentes,
asociados a objetos distintos,
pueden compartir el mismo
nombre; al llamarlos por ese
nombre se utilizará el
comportamiento
correspondiente al objeto que se
esté usando. O, dicho de otro
modo, las

Almacenamiento de variables en memoria


Las variables se representan con identificadores que hacen referencia a un lugar de la
memoria del programa en donde se almacena un dato. Una variable está asociada a un tipo
de datos, el cual y en función del tamaño del mismo determina la cantidad de bytes que
serán necesarios para almacenar la variable. En el caso de colecciones y al contrario que
con el resto de tipo de datos, ya sean primitivos u objetos complejos, la memoria asignada a
almacenar tales variables no se conoce de antemano, lo cual lleva a establecer políticas de
reserva de memoria:

 Reserva fija de memoria.- Implica predeterminar la cantidad de memoria que se


asignará a la colección. Es una política extremadamente rígida, ya que llegados al
final de la zona de memoria no se podrían almacenar nuevos elementos.
 Reserva variable de memoria.- Se dedica una zona de memoria, pudiendo ser de un
tamaño predeterminado o no, y en caso de sobrepasarse dicha zona de memoria se
vuelve a asignar otra zona, contigua o no, para impedir la restricción mencionada
arriba.

Ámbito
Respecto al ámbito de una variable, éste puede ser:

 Local: Cuando la misma sólo es accesible desde un único procedimiento hijo, no


pudiendo ser leída o modificada desde otro procedimiento hermano o desde el
propio procedimiento padre. Es posible declarar variables en bloques de condición,
bucles, etc de tal modo que sólo pueda accederse a ellas en el propio bloque.
 Global: Cuando la misma es accesible tanto desde rutinas o macros de la
aplicación, como en todos los procedimientos y funciones de la misma.

Si bien es cierto, que de una forma básica, se puede definir el ámbito de las variables de la
forma expuesta más arriba, existen grados de globalidad de las mismas, pudiendo ser
accesibles desde unos puntos u otros, o incluso pudiendo ser accesibles entre aplicaciones
distintas, llegando al caso de la  superglobalidad.
Ejemplo del ámbito de una variable en el lenguaje de programación Pascal.
programa ejm01;

uses sysutils;

//Defincion

type

A = Class

   private

        numeroEntero:integer; /* Variable Global a todos los Métodos */

  public

        procedure Metodo();

        procedure otrometedo();

end;

//Implementacion

procedure A.metodo();

var

 num,i:integer;// Variable Local a metodo. Puede accederse dentro de este método en


cualquier parte, pero no fuera del mismo.

begin

   num:=1;

   for i:=0 to numeroEntero do
         num:=num*i;

  result:=0;

end;

procedure A.otrometodo();

var

    num:integer;// Variable local a otroMetodo.

begin

     num:=1;

    writeln('Variable local num ',num);

end;

Ejemplo del ámbito de una variable en el lenguaje de programación Java.


 public class A {
    public Integer numeroEntero = new Integer(); /* Variable Global a todos los
Métodos */
 
    public Integer metodo() {
       int num = 1; // Variable Local a metodo. Puede accederse dentro de este
método en cualquier parte, pero no fuera del mismo.
       for (int i = 0;i<numeroEntero.intValue();i++) { // i es local al bucle for, sólo
puede ser accedida dentro del mismo.
           num *= i;
       }
       // i = 2; Esta línea provocaría error al no haber declarado la variable i. i fue
definida localmente al bucle for.
       return Integer.valueOf(num);
    }
 
    public void otroMetodo() {
       int num = 1; // Variable local a otroMetodo. num aquí es una variable distinta
a la variable num de metodo
       System.out.println("Variable local num: " + num);
    }
 }
Hay que tener en cuenta que en Java el caso de las variables globales a todos los métodos
que se encuentran en una clase, es algo peculiar, ya que estas realmente son  atributos que
definen un objeto de una clase determinada, en este caso la clase A tiene un atributo
llamado numeroEntero. El atributo es un concepto que define a un objeto de una clase
determinada, mientras que una variable sirve de apoyo a los procedimientos y no define
conceptualmente objetos.
1.3 Herencia

Herencia
En orientación a objetos la herencia es, después de la agregación o composición, el
mecanismo más utilizado para alcanzar algunos de los objetivos más preciados en el
desarrollo de software como lo son la reutilización y la extensibilidad. A través de ella los
diseñadores pueden crear nuevas clases partiendo de una clase o de una jerarquía de clases
preexistente (ya comprobadas y verificadas) evitando con ello el rediseño, la modificación y
verificación de la parte ya implementada. La herencia facilita la creación de objetos a partir
de otros ya existentes e implica que una subclase obtiene todo el comportamiento (métodos)
y eventualmente los atributos (variables) de su superclase.
Es la relación entre una clase general y otra clase más específica. Por ejemplo: Si
declaramos una clase párrafo derivada de una clase texto, todos los métodos y variables
asociadas con la clase texto, son automáticamente heredados por la subclase párrafo.
La herencia es uno de los mecanismos de los lenguajes de programación orientada a objetos
basados en clases, por medio del cual una clase se deriva de otra de manera que extiende su
funcionalidad. La clase de la que se hereda se suele denominar clase base, clase
padre, superclase, clase ancestro (el vocabulario que se utiliza suele depender en gran
medida del lenguaje de programación).
En los lenguajes que cuentan con un sistema de tipos fuerte y estrictamente restrictivo con
el tipo de datos de las variables, la herencia suele ser un requisito fundamental para poder
emplear el Polimorfismo, al igual que un mecanismo que permita decidir en tiempo de
ejecución qué método debe invocarse en respuesta a la recepción de un mensaje, conocido
como enlace tardío (late binding) o enlace dinámico (dynamic binding).
Clase abstracta
La herencia permite que existan clases que nunca serán instanciadas directamente. En el
ejemplo anterior, una clase "perro" heredaría los atributos y métodos de la clase
"mamífero", así como también "gato", "delfín" o cualquier otra subclase; pero, en ejecución,
no habrá ningún objeto "mamífero" que no pertenezca a alguna de las subclases. En ese
caso, a una clase así se la conocería como Clase Abstracta. La ausencia de instancias
específicas es su única particularidad, para todo lo demás es como cualquier otra clase.

Herencia y ocultación de información


En ciertos lenguajes, el diseñador puede definir qué variables de instancia y métodos de los
objetos de una clase son visibles. En C++ y java esto se consigue con las
especificaciones private, protected y public. Sólo las variables y métodos definidos como
públicos en un objeto serán visibles por todos los objetos. En otros lenguajes como
Smalltalk, todas las variables de instancia son privadas y todos los métodos son públicos.

Comportamientos diferentes, asociados a objetos distintos,


pueden compartir el mismo nombre; al llamarlos por ese
nombre se utilizará el comportamiento correspondiente al
objeto que se esté usando. O, dicho de otro modo, las

Dependiendo del lenguaje que se utilice, el diseñador también puede controlar qué
miembros de las superclases son visibles en las subclases. En el caso de java y C++ los
especificadores de acceso (private, protected, public) de los miembros de la superclase
afectan también a la herencia:
Private

Ningún miembro privado de la superclase es visible en la subclase.

Protected

Los miembros protegidos de la superclase son visibles en la subclase, pero no visibles para
el exterior.
Public

Los miembros públicos de la superclase siguen siendo públicos en la subclase.

Redefinición de métodos

En la clase derivada se puede redefinir algún método existente en la clase base, con el
objeto de proveer una implementación diferente. Para redefinir un método en la subclase,
basta con declararlo nuevamente con la misma signatura (nombre y parámetros). Si se
invoca un cierto método de un objeto que no está definido en su propia clase, se dispara la
búsqueda hacia arriba en la jerarquía a la que dicha clase pertenece. Sin embargo, si
existieran dos métodos con la misma signatura, uno en la clase y otro en una superclase, se
ejecutaría el de la clase, no el de la superclase.
Cuando se redefine un método en una clase es posible acceder explícitamente al método
original de su superclase, mediante una sintaxis específica que depende del lenguaje de
programación empleado (en muchos lenguajes se trata de la palabra clave super).
Ejemplo en Pascal:

program Herencia;

uses
  sysutils;
type
    Mamifero = Class
    private
      patas:integer;
      nombre:String;
    public
      constructor Mamifero(nom:String;pat:integer);
      procedure imprimirPatas();
  end;

  Perro = Class(Mamifero)
    public
      constructor Perro(nom:String);
  end;

  Gato = Class(Mamifero)
    public
      constructor Gato(nom:String);
  end;

//Implementacion de las Clases

constructor Gato.Gato(nom: String);
begin
  self.Mamifero(nom,4);
end;

constructor Perro.Perro(nom: String);
begin
  self.Mamifero(nom,4);
end;

constructor Mamifero.Mamifero(nom:String;pat:integer);
begin
  nombre:=nom;
  patas:=pat;
end;

procedure Mamifero.imprimirPatas();
begin
  writeln(' Tiene ',patas,' patas - Mamifero');
end;

var
  Perrito:Perro;
begin
  Perrito:=Perro.Perro('Pantaleon');
  Perrito.imprimirPatas();//Está en la clase mamífero
end.   

Ejemplo en Java:

import java.io.*;

public class Mamifero{


private int patas;
private String nombre;

public void imprimirPatas(){


System.out.print(" Tiene " + patas + " patas\n",
"Mamifero");
}

public Mamifero(String nombre, int patas){


this.nombre = nombre;
this.patas = patas;
}
}

public class Perro extends Mamifero {


public Perro(String nombre){
super(nombre, 4);
}
}

public class Gato extends Mamifero {


public Gato(String nombre){
super(nombre, 4);
}
}

public class CrearPerro {


public static void main(String[] args) {
Perro perrito = new Perro("Pantaleon");
perrito.imprimirPatas(); /*Está en la clase
mamífero*/
}
}

1.4 Manejo de excepciones

Introducción
Una excepción en términos de lenguaje de programación es la indicación de un problema
que ocurre durante la ejecución de un programa. Sin embargo la palabra excepción se
refiere que este problema ocurre con poca frecuencia generalmente cuando existe algún
dato o instrucción que no se apega al funcionamiento del programa por lo que se produce un
error. El manejo de excepciones permite al usuario crear aplicaciones tolerantes a fallas y
robustos (resistentes a errores) para controlar estas excepciones y que pueda seguir
ejecutando el programa sin verse afectado por el problema. En lenguaje java estas
excepciones pueden manejarse con las clases que extienden el paquete Throwable de
manera directa o indirecta, pero existen diversos tipos de excepciones y formas para
manejarlas.

Uso del manejo de excepciones


El manejo de excepciones ayuda al programador a remover el código para manejo de
errores de la línea principal de ejecución, además se pude elegir entre manejar todas las
excepciones, las de cierto tipo o de las de grupos relacionados, esto hace que la probabilidad
de pasar por alto los errores se reduzca y a la vez hace los programas más robustos. Pero es
importante utilizar un lenguaje de programación que soporte este manejo, de lo contrario el
procesamiento de errores no estará incluido y hará el programa más vulnerable. Este manejo
está diseñado para procesar errores que ocurren cuando se ejecuta una instrucción, algunos
ejemplos son: desbordamiento aritmético, división entre cero, parámetros inválidos de
método y asignación fallida en la memoria. Sin embargo no está diseñado para procesar
problemas con eventos independientes al programa como son pulsar una tecla o clic al
mouse. Las excepciones se dividen en verificadas y no verificadas. Es importante esta
división porque el compilador implementa requerimientos de atrapar o declarar para las
verificadas lo que hará que se detecten las excepciones automáticamente y de acuerdo al
lenguaje de programación utilizado se utilizará un método para corregirlas. Sin embargo
para las no verificadas se producirá un error indicando que deben atraparse y declararse. Por
eso el programador debe pensar en los problemas que pueden ocurrir cuando se llama a un
método y definir excepciones para verificarse cuando sean importantes. Las clases de
excepciones pueden derivarse de una superclase común, por lo que con un manejador para
atrapar objetos de la superclase, también se pueden atrapar todos los objetos de las
subclases de esa clase. Pero también, se pueden atrapar a cada uno de los tipos de las
subclases de manera individual si estas requieren ser procesadas diferente. A cada celula se
le conoce como compiladora de distintos.

Aserciones
Las aserciones ayudan a asegurar la validez del programa al atrapar los errores potenciales e
identificar los posibles errores lógicos del desarrollo. Estas pueden escribirse como
comentarios para apoyar a la persona que desarrolla el programa. Algunos ejemplos son:
Precondiciones y pos condiciones Estas características son utilizadas por los programadores
para hacer un análisis de lo esperado del programa antes y después de su ejecución. Son
importantes porque gracias a ellas se pueden detectar posibles fallas en el programa y
corregirlas. Las precondiciones son verdaderas cuando se invoca a un método, estas
describen las características del método y las expectativas que se tienen en el estado actual
del programa. Si no se cumplen las precondiciones el comportamiento del método es
indefinido por lo que se lanza una excepción que esté preparada o continuar con el
programa esperando el error. Las pos condiciones describen las restricciones en el entorno y
cualquier efecto secundario del método. Es recomendable escribirlas para saber que esperar
en un futuro si es que se hacen modificaciones.

Conclusión
El manejo de excepciones ayuda a lidiar con los errores de una aplicación por medio de la
manipulación del código para hacer programas más robustos. Además existen herramientas
que ayudan a manejarlas tal es el caso de los bloques try (intentar) que encierran el código
que puede lanzar una excepción y los bloques catch (atrapar) que lidian con las excepciones
que surjan. También existen técnicas que el programador utiliza para conocer el posible
funcionamiento del programa y detectar los errores que pueda contener.
Ejemplo de manejo de excepción en Java:

procedure TForm1.Button1Click(Sender : TObject);


begin
try
try
a := b / c;
finally
// Este código siempre se ejecuta, independientemente de si ocurre o no
una excepción.
end;
except
on e:EZeroDivide do
// Manejo de la excepción División por cero.
on e:Exception do
// Manejo de una excepción "genérica".
end;
end;

Clases de excepciones

 Las excepciones se pueden dividir en las siguientes categorías:


·         Conversión de tipo. Se producen cuando tratamos de convertir un tipo de dato en
otro, por ejemplo utilizando las funciones IntToStr, StrToInt, StrToFloat. Se dispara una
excepción EConvertError.

·         Tipo forzado (typecast). Se producen cuando tratamos de forzar el reconocimiento


de una expresión de un tipo como si fuera de otro usando el operador as. Si no son
compatibles, se dispara la excepción EInvalidCast.

·         Aritmética de punto flotante. Se producen al hacer operaciones con expresiones de


tipo real. Existe una clase general para este tipo de excepciones EmathError, pero Lazarus
utiliza sólo los descendientes de ésta:

o    EinvalidOp: el procesador encontró una instrucción inválida.

o    EzeroDivide: división por cero.

o    Eoverflow: se excede en más la capacidad aritmética (números demasiado grandes).

o    Eunderflow: se excede en menos la capacidad aritmética (números demasiados

o    queños).

·         Aritmética entera. Se producen al hacer operaciones con expresiones de tipo entero.


Existe una clase general definida para este tipo de excepciones llamada EintError, pero
Lazarus sólo utiliza los descendientes:

o    EDivByZero: división por cero.

o    ERangeError: número fuera del rango disponible según el tipo de dato. La


comprobación de rango debe estar activada (indicador $R).

o    EIntOverflow: se excede en más la capacidad aritmética (números demasiado grandes).


La comprobación de sobrepasamiento debe estar activada (indicador $O).

·         Falta de memoria. Se producen cuando hay un problema al acceder o reservar


memoria. Se definen dos clases:

o    EOutOfMemory: no hay suficiente memoria disponible para completar la operación.

o    EInvalidPointer: la aplicación trata de disponer de la memoria referenciada por un


puntero que indica una dirección inválida (fuera del rango de direcciones permitido a la
aplicación). Generalmente significa que la memoria ya ha sido liberada.

·         Entrada/Salida. Se producen cuando hay un error al acceder a dispositivos de


entrada/salida o archivos. Se define una clase genérica EInOutError con una propiedad
que contiene el código de error ErrorCode.

·         Hardware. Se producen cuando el procesador detecta un error que no puede manejar


o cuando la aplicación genera intencionalmente una interrupción para detener la ejecución.
El código para manejar estas excepciones no se incluye en las DLL compiladas, sólo en las
aplicaciones. Se define una clase base que no es directamente
utilizada EProcessorException. Las clases útiles son los descendientes:

o    EFault: es una clase base para todas las excepciones de faltas del procesador.

o    EGPFault: error de Protección General, cuando un puntero trata de acceder posiciones


de memoria protegidas.

o    EStackFault: acceso ilegal al segmento de pila del procesador.

o    EPageFault: el manejador de memoria de Windows tuvo problemas al utilizar el


archivo de intercambio.

o    EInvalidOpcode: el procesador trata de ejecutar una instrucción inválida.

o    EBreakpoint: la aplicación ha generado una interrupción de la ejecución (punto de


ruptura, utilizado por el debugger de Lazarus para inspeccionar las variables en un punto).

o    ESingleStep: la aplicación ha generado una interrupción de ejecución paso a paso.


Luego de cada paso de programa se produce la interrupción. Es utilizada también por el
debugger.

·         Excepciones silenciosas. Se disparan intencionalmente por la aplicación para


interrumpir el flujo de ejecución. No generan mensajes de error. Se define una
clase EAbort. Es digno de mencionar que esta excepción es automáticamente generada
cuando invocamos el procedimiento global Abort, que lo podemos usar para interrumpir la
ejecución del programa en cualquier punto. Por ejemplo, para dar un toque profesional a un
programa hay ocasiones en que nos interesa controlar la excepción pero que no se entere el
usuario del programa. Lo que no se puede hacer es abandonar la excepción con Break o
con Exit ya que puede ser peor el remedio que la enfermedad. Habría que como hemos
visto anteriormente usar Abort:

    try

      {sentencias}

    except

      Abort;

    end;

Como podemos ver en la imagen siguiente, la jerarquía de clases descendientes


de Exception es bastante amplia, sin embargo muchos de los descendientes son de uso
interno de los componentes y no los trabajamos directamente. 
Otras excepciones no tratadas anteriormente, pero que las he encontrado en otro blog
(Delphi al límite) son las siguientes:

EAccessViolation: Comprueba errores de acceso a memoria inválidos.


EBitsError: Previene intentos para acceder a arrays de elementos booleanos.
EComponentError: Nos informa de un intento inválido de registar o renombar un
componente.
EDatabaseError: Especifica un error de acceso a bases de datos.
EDBEditError: Error al introducir datos incompatibles con una máscara de texto.
EExternalException: Significa que no reconoce el tipo de excepción (viene de fuera).
EIntOutError: Representa un error de entrada/salida a archivos.
EInvalidGraphic: Indica un intento de trabajar con gráficos que tienen un formato
desconocido.
EInvalidOperation: Ocurre cuando se ha intentado realizar una operación inválida sobre un
componente.

EMenuError: Controla todos los errores relacionados con componentes de menú.


EOleCtrlError: Detecta problemas con controles ActiveX.
EOleError: Especifica errores de automatización de objetos OLE.
EPrinterError: Errores al imprimir.
EPropertyError: Ocurre cuando se intenta asignar un valor erroneo a una propiedad del
componente.
ERegistryExcepcion: Controla los errores en el resigtro.

Bien, con todo lo que hasta ahora hemos podido ver podríamos tratar las excepciones que se
produzcan y responder a cada clase de manera diferente si fuera necesario. Este tratamiento
se utiliza para responder a distintas excepciones en el lugar donde se producen, de manera
tal que podamos recuperarnos del error y poder proseguir el código. El ejemplo más común
es el error de lectura de un archivo, normalmente permitiremos al usuario reintentar la
operación además de cancelarla. Pero recordemos que el evento OnException se produce
en el objeto Application, después de lo cual la ejecución queda a la espera de nuevos
eventos. Debemos encontrar una forma de detectar y corregir el error sin abandonar el
procedimiento en curso.
Ejemplo de manejo de excepción en Java:

import java.io.IOException;

public static void main(String[] args) {


try {
// Se ejecuta algo que puede producir una
excepción
} catch (IOException e) {
// manejo de una excepción de entrada/salida
} catch (Exception e) {
// manejo de una excepción cualquiera
} finally {
// código a ejecutar haya o no excepción
}
}

Manejo de Excepciones en Java


Vamos a hablar un poco sobre las Excepciones en Java, que son?, de donde vienen? y como
evitarlas entre, otras....... debemos tener presente que siempre estamos propensos a
encontrarnos con errores o Excepciones cuando estamos desarrollando, por eso de la
importancia de conocerlas y saber como tratarlas....
 
Básicamente una excepción es un Objeto descendiente de la clase java.lang.Object,
podemos pensar en ellas como una condición excepcional en nuestro sistema el cual altera
la correcta ejecución del mismo, las excepciones nos indican que hay algo anómalo, 
inconsistente o simplemente un Error, lo cual impide que el sistema se ejecute como debería
de ser...

Tal vez se preguntaran si ¿pero Anómalo, inconsistente o Error no es básicamente lo


mismo?...... podría ser, pero en este enfoque no necesariamene lo es, ya que lo que vamos a
conocer como una excepcion no siempre es un error (hablando como excepción en general,
ya que en java una Exception  es muy diferente a un Error), muchas veces necesitaremos
trabajar con excepciones controladas para indicar alguna inconsistencia en nuestro sistema
que podría provocar errores.......

A modo de ejemplo, podemos encontrarnos con el famoso NullPointerException  el cual


nos indica que un objeto se encuentra vació, pero esto no es un error ya que nosotros
podemos trabajar con objetos null , entonces veamoslo como si la excepción nos dijera "Es
un objeto nulo y no se puede efectuar el proceso", mientras que hay errores
como NoClassDefFoundError  el cual nos indica que la maquina virtual de Java (JVM) no
puede encontrar una clase que necesita, debido a por ejemplo que no encuentra un .class,
esto si se maneja como un error en java.

Para hacer mas claridad sobre el tema veamos la Jerarquía de Excepciones de Java (puede
que no se encuentren algunas excepciones, pero se contemplan las mas comunes)
 
Jerarquía de excepciones
Vemos que se tiene un claro árbol de herencia mediante el cual se pueden definir las
categorías de Excepciones o de Error que se puede dar en el sistema.
 
La importancia de Prever!!!
 
Cuando se esta programando debemos tener claro que nuestro código no es perfecto, así
tengamos mucha experiencia en desarrollo siempre esta la posibilidad de que algo falle, sea
por nuestro código o por otros factores, por eso de la importancia de contemplar todo desde
antes, posibles fallos o lo que pueda afectar el sistema.
 
Veamos un ejemplo Simple:
1private void metodoDividir(int dividendo, int divisor){
2   
3   String resultado+=dividendo/divisor;
4   System.out.println(resultado);
5   
6}
El metodoDividir(int, int)  teóricamente esta bien, claro, a simple vista si
tenemos :  dividendo = 4  y  divisor = 2  pues el resultado es  2  ......básico....... pero y si
el  divisor es 0 ? pues con ese caso puntual el resultado seria el siguiente.
Vemos que nos indican que se produjo una ArithmeticException debido a una división por
cero, además se muestra cual fue la traza del error pasando por el método main hasta
el metodoDividir().

La anterior es una Excepcion simple, algo que se supone no debería pasar, es obvio, no se
puede dividir por cero, o ¿no?.........pues no, en programación no podemos asumir ni
pensar así, ya que muchas veces nos olvidamos de las cosas obvias y las pasamos por alto,
el problema es que eso tan obvio puede detener toda la ejecución del programa.

Trabajando con try - catch - finally


 
Con los bloques Try - Catch podemos capturar y procesar una posible excepción, evitando
que el sistema se detenga sin necesidad cuando el motivo de esto puede ser corregido
fácilmente, la estructura básica es la siguiente.

1try {
2  //Bloque de código que vamos a procesar
3} catch(excepcion) {
  //Tratamiento que se le da a la posible excepción
4
} finally {
5  //Bloque de código que se ejecutará despues del try o del
6catch
7}

Apliquemos esto a nuestro ejemplo anterior...


1
2
3 private void metodoDividir(int dividendo, int divisor){
4 String resultado="";
5  try {
6    resultado+=dividendo/divisor;
7   }catch (Exception e) {
   resultado="Se intentó dividir por cero";
8    JOptionPane.showMessageDialog(null,"Error: No se puede dividir por cero
9 ",
1       "Advertencia",JOptionPane.WARNING_MESSAGE);
0  }
1    finally{
    System.out.println("Termino el proceso : el resultado es =
1 "+resultado);
1    }
2 }
1
3
Como vimos aplicamos la estructura de los bloques y de esta manera nos aseguramos que la
excepción anterior fue controlada evitando que el sistema se detenga, en el catch podemos
hacer el proceso que consideremos conveniente, ya sea solo informar del error o solicitar
nuevos parámetros de entrada.
La salida es la siguiente:

Lección 1.1: La Programación orientada a objetos


Ha obtenido 70 punto(s) sobre 70 hasta ahora.

1.5 Composición

Un sistema orientado a objetos está caracterizado por objetos que interactúan entre sí.
Estas interacciones suponen ciertos tipos de relaciones entre los objetos del sistema.
La semántica que expresa un objeto en el sistema está determinada, en primer lugar,
por las relaciones que éste establece con otros objetos o conjuntos de objetos.
Tomemos como ejemplo un objeto fecha, del que sin establecer ningún tipo de
relación, podría decirse que significa un día del año particular. Pero si relacionamos
ese objeto fecha con un objeto Persona de manera que represente la fecha en que
esa persona nació, en ese contexto dado, el mismo objeto fecha adoptaría un
significado diferente, el de un cumpleaños; aunque sigue siendo una fecha, ahora
tiene otra idea asociada. Las relaciones entre objetos no solo están definidas por los
objetos que participan y la circunstancia que los relaciona, sino también por la cantidad
de objetos (cardinalidad de la relación) y la dirección de la misma. Una relación puede
tener cardinalidad:

         uno a uno, ejemplo: un auto tiene un motor.

         uno a muchos, ejemplo: un auto tiene muchas ruedas.

         muchos a muchos, ejemplo: un auto se puede servir en muchas gasolineras


y una gasolinera puede servir a muchos autos.

y direccionalidad:

         unidireccional, ejemplo: un auto tiene cuatro ruedas.

         bidireccional

Composición
La composición (también conocida como relación asociativa) es un tipo de relación
que se establece entre dos objetos que tienen comunicación persistente. Se utiliza
para expresar que un par de objetos tienen una relación de dependencia para llevar a
cabo su función, de modo que uno de los objetos involucrados está compuesto por el
otro.
De manera práctica, es posible reconocer asociatividad entre dos objetos A y B si la
proposición "A tiene un B" (o viceversa) es verdadera. Por ejemplo: "una computador
tiene un disco duro" es verdadero; por tanto, un objeto computador tiene una relación
de composición con al menos un objeto disco duro.
Los dos conceptos que debes conocer cómo mínimo cuando intentas descifrar la
forma en que tus objetos deben interactuar son Asociación y Composición.
Asociación
La asociación se podría definir como el momento en que dos objetos se unen para
trabajar juntos y así, alcanzar una meta. 

Un punto a tomar muy en cuenta es que ambos objetos son independientes entre sí,
veremos un poco más adelante qué implicación tiene esto. Para validar la asociación,
la frase “Usa un”, debe tener sentido:

         El ingeniero usa una computadora

         El cliente usa tarjeta de crédito.

Composición
En caso contrario, la composición es un tipo de relación dependiente en dónde un
objeto más complejo es conformado por objetos más pequeños. En esta situación, la
frase “Tiene un”, debe tener sentido:

         El auto tiene llantas

         La portátil tiene un teclado.

Y como ésta mini guía no va a mencionar nada de UML. Vamos a ver directamente en
código cómo se verían representadas ambos tipos de relaciones.

El código es Java, pero funciona para cualquier lenguaje de programación orientado a


objetos.

Cómo implementar Asociación
Representaremos la relación: El cliente usa tarjeta de crédito.
Código pascal :
type
 Customer = Class
   private
     id:integer;
     fistName,lastName:String;
     creditCard: CreditCard;
   public
     constructor Customer();
     procedure setCreditCard(creditCards:CreditCard);
 end;
constructor Customer.Customer();
begin
  //Lo que sea que el construtor haga
end;

procedure Customer.setCreditCard(creditCards: CreditCard);
begin
   creditCard = creditCards;
end;

Código java :
public class Customer {

    private int id;


    private String firstName;
    private String lastName;
    private CreditCard creditCard;

    public Customer() {
        //Lo que sea que el construtor haga
    }

    public void setCreditCard(CreditCard creditCard) {


        this.creditCard = creditCard;
    }

    // Más código aquí


}
La explicación viene más adelante para darles oportunidad que hagan sus propias
comparaciones.
Cómo implementar Composición
Representaremos la relación: La portátil tiene un teclado.
Código Pascal:
type
 Laptop = Class
   private
     manufacturer,model,serviceTag:String;
     keyBoard:KeyBoard;
   public
     constructor Laptop();  
 end;

constructor Laptop.Laptop();
begin
  //Lo que sea que el construtor haga

 keyBoard := KeyBoard.create();
end;

Código Java:

public class Laptop {

    private String manufacturer;


    private String model;
    private String serviceTag;
    private KeyBoard keyBoard = new KeyBoard();

    public Laptop() {
        //Lo que sea que el constructor haga
    }

Muy similar, pero hay una gran diferencia: Podemos crear un objeto de tipo
Customer y asignarle un CreditCard más tarde mediante el método
setCreditCard. 

Pero si creamos un objeto Laptop, de entrada sabremos que tendrá un teclado ya


creado, puesto que la variable de referencia keyBoad es declarada e inicializada al
mismo tiempo.

Llamaremos a las clases Customer y Laptop, clases contenedoras.

De ambos casos podemos deducir que:

En la asociación:

1.    Customer es independiente de CreditCard, puesto que el cliente puede


existir sin necesidad de tener asignada una tarjeta de crédito. Démosle
tiempo para que la tramite, ¡Pero no lo dejemos ir!

2.    Se puede asignar o retirar la tarjeta de crédito, sin que la existencia del
Cliente se vea afectada (No debería verse afectada, esto significa
que Customer no debe tronar si no hay un CreditCard presente).

En la composición:
1. Los objetos que componen a la clase contenedora, deben existir desde el principio.
(También pueden ser creados en el constructor, no sólo al momento de declarar las
variables como se muestra en el ejemplo).
2. No hay momento (No debería) en que la clase contenedora pueda existir sin alguno
de sus objetos componentes. Por lo que la existencia de estos objetos no debe ser
abiertamente manipulada desde el exterior de la clase.
1.6 Atributos y Métodos

Atributos y métodos en un objeto


En lugar de utilizar un lenguaje de programación concreto, voy a ponerte una serie
de ejemplos en pseudocódigo para que resulte más sencillo y que luego puedas
extrapolarlo al lenguaje que quieras. Vamos a ver cómo se ve una clase que tiene un
atributo.
class Coche {

public marca;

}
Sencillo, ¿verdad? La clase Coche contiene un atributo (también puede llamarse propiedad,
aunque en programación estructurada, lo llamaríamos variable) llamada marca. Esto está
muy bien, pero tal y como está la clase, no puede hacer nada con ese dato, no le sirve para
nada. Hagamos algo sencillo: que guarde en la propiedad marca el valor que le pasemos.

class Coche {

public marca;

public guardaMarca(parametro) {
this.marca = parametro;
}

}
¿Ves el this? Puede que eso te confunda un poco. Utilizamos this cuando, dentro de un
objeto, nos referimos a un atributo de ese mismo objeto. ¿De todos los objetos de esa
clase? No, sólo de la instancia del objeto que ejecute el método. Ejemplo…

class Coche {

public marca;

public guardaMarca(parametro) {
this.marca = parametro;
}

Coche a3 = new Coche();


a3.guardaMarca("Audi");

Coche m3 = new Coche();


m3.guardaMarca("BMW");
Así, el atributo marca del objeto a3 vale “Audi”, y en el objeto m3 vale “BMW”. Al ejecutar
el método guardaMarca() sobre el objeto m3, sólo afecta a los datos de esa instancia, pero
no de ninguna otra.

Esto es: los objetos son independientes unos de otros. Aunque el código de la clase sea
ese, lo que sucede en un objeto no afecta a otros de la misma clase. Piensa en chalets
adosados… Aunque todos se hacen con el mismo plano, lo que tienen dentro es totalmente
distinto. Las personas, los muebles, las conversaciones… Da igual que el plano sea el
mismo, dentro de cada chalet el día a día es distinto del de todos los demás chalets.

Lo mismo pasa con los objetos. Lo que se hace en uno no afecta a otros. Por lo tanto,
con this hacemos referencia a un atributo (o incluso método) dentro de la instancia que
ejecute el método.

Vamos a ver ahora cómo podríamos imprimir por pantalla la marca del coche:

class Coche {

public marca;

public guardaMarca(parametro) {
this.marca = parametro;
}

public escribeMarca() {
print this.marca;
}

}
Muy sencillo. Vamos a poner un par de métodos más que espero sean autodescriptivos, y te
animo a que trates de entenderlos por tu cuenta.

class Coche {

public marca;

public combustible;

public guardaMarca(parametro) {
this.marca = parametro;
}

public escribeMarca() {
print this.marca;
}

public circular(km) {
this.combustible = this.combustible - 4 * km;
}

public repostar(litros) {
this.combustible = this.combustible + litros;
}

Con esa funcionalidad, la clase coche se acerca mucho más a la realidad, ¿no? Aún
quedarían muchas cosas por comprobar: que no pueda llenarse el depósito hasta
tener un millón de litros, no poder circular si no tenemos combustible… Pero esas
cosas voy a dejarlas como prácticas para ti, si quieres hacerlas.
Puedes escribir en los comentarios (utiliza la etiqueta <pre> y pega código entre ellas,
que así te respeta las tabulaciones) si quieres, y le echo un ojo. Hay algo que quiero
ver en este artículo sí o sí: los métodos especiales.
Métodos especiales: el constructor y el destructor
A veces nos interesará crear un objeto que tenga ya datos dentro, o que esos datos
dependan de algunos cálculos, o simplemente que no se pueda crear un objeto vacío.
Vamos a cambiar de ejemplo y utilizaremos una clase nueva: la clase Persona. Una
persona no puede existir, en el mundo real, sin un nombre y una fecha de nacimiento.
Por lo tanto, tendrá nombre y edad en todo caso, y no debería haber personas
registradas sin nombre y sin fecha de nacimiento.
Vamos a pensar que todo esto es real y creemos una clase que siempre tendrá datos
de inicio, mediante el método constructor.
class Persona {

public nombre;

public edad;

public Persona(nombre, edad) {


this.nombre = nombre;
this.edad = edad;
}

}
El método constructor tiene diversas restricciones que dependerán del lenguaje en el que
trabajemos. Por ejemplo, en C# y Java el constructor se llama igual que la clase, sin
excepción. En Pascal se llama Create() y no es necesario que haya parámetros.

El ejemplo que he puesto se parece más a Java o C# ya que, como ves, hay un método que
se llama igual que la clase y que es, efectivamente, el constructor.

El constructor es un método especial que se ejecuta cuando instanciamos un objeto con el


operador new. En este caso, nunca podrá crearse un objeto de clase Persona sin un valor en
nombre y edad, porque el constructor te obliga a pasarle dos valores (vamos a dejar para

otra ocasión lo de validar los datos   porque no sería correcto pero sí posible -en este
caso y con este código- que la persona se llamara ” ” y tuviera -6 años).

También quiero hablar del destructor. El destructor es otro método especial que se ejecuta


cuando se eliminan las referencias al objeto.

Normalmente se utiliza para liberar recursos de sistema que utiliza el objeto (sockets,


memoria, conexiones a una base de datos), para escribir cosas en un log o registro o
simplemente para hacer limpieza. El destructor se llama en C++ como la clase, pero con
una virgulilla (el rabito de la eñe) delante, como por ejemplo ~Persona() y no es muy
utilizado.

 
Ámbito de métodos y atributos en un objeto: this

Fíjate en que this.nombre y nombre no son lo mismo. En el primer caso, estamos llamando


al atributo del objeto, ya que this indica que se accede al espacio de nombres de ese objeto.
Si indicamos sólamente nombre, podremos causar un comportamiento inesperado ya que en
el caso anterior existe tanto un atributo local como un parámetro que se llaman del mismo
modo. Es por tanto obligatorio utilizar this para hacer referencia al atributo y no utilizarlo
para hacer referencia al parámetro.

¿Sería posible omitir this? Sí, lo es, pero cuando no hay conflictos en los nombres de los
atributos, métodos y parámetros. Un ejemplo:

class Persona {

public nombre;

public edad;

public Persona(parametro_nombre, parametro_edad) {


nombre = parametro_nombre;
edad = parametro_edad;
}

Sobrecarga de métodos (Pascal)


 La sobrecarga de métodos se usa con métodos y constructores, los destructores no se
pueden sobrecargar ya que sólo debe haber uno por clase. Para sobrecargar métodos y
constructores, estos sólo se deben diferenciar en la cantidad de parámetros o en el uso de
parámetros con tipos de datos diferentes y usar el mismo nombre. También se puede usar la
palabra reservada overload, pero esto no es necesario y sólo se mantiene por
compatibilidad con delphi. Gracias a la sobrecarga de métodos, una clase puede tener
distinto comportamiento dependiendo de cual método sobrecargado se use, a esta
característica se le conoce como Polimorfismo por sobrecarga. La palabra Polimorfismo
viene de la capacidad que tiene una clase de cambiar su comportamiento. En el caso del
polimorfismo por sobrecarga, el cambio del comportamiento de una clase se define
sobrecargando los métodos necesarios para lograr el polimorfismo.

El siguiente ejemplo es la unidad Complejos, al cual se le ha añadido un


nuevo constructor Crear que sobrecarga el ya existente, este nuevo constructor permite crear
un número imaginario puro, Ejemplo:

  Unit Complejos;
Interface

Type
TComplejo = class
ParteReal,ParteImag:double;
Constructor Crear(i:double); //sobrecargado, crea un imaginario puro
Constructor Crear(r,i:double); //sobrecargado
Function ObtReal:double;
Function ObtImag:double;
Procedure PonReal(n:double);
Procedure PonImag(n:double);
Function ObtCad(dec:integer):string;
Function PonCad(n:string):integer;
Procedure Adicion(n:TComplejo);
Procedure Multiplicacion(n:TComplejo);
End;
Implementation

Constructor TComplejo.Crear(i:double);
Begin
ParteReal:=0;
ParteImag:=i
End;

Constructor TComplejo.Crear(r,i:double);
Begin
ParteReal:=r;
ParteImag:=i;
End;

Function TComplejo.ObtReal:double;
Begin
ObtReal:=ParteReal
End;

Function TComplejo.ObtImag:double;
Begin
ObtImag:=ParteImag
End;

Procedure TComplejo.PonReal(n:double);
Begin
ParteReal:=n;
End;

Procedure TComplejo.PonImag(n:double);
Begin
ParteImag:=n
End;

Function TComplejo.ObtCad(dec:integer):string;
var aux1,aux2,p:string;
Begin
p:='';
Str(ParteReal:0:dec,aux1);
Str(ParteImag:0:dec,aux2);
if ParteImag>=0 then p:='+';
ObtCad := aux1 + p + aux2 + 'i';
End;

Function TComplejo.PonCad(n:string):integer;
Var aux:string;
p,i,error:integer;
PR,PI:string;
encontro:boolean;
Begin
aux:=n;
ParteReal:=0;
ParteImag:=0;
error:=0;
if (aux[length(aux)]='i') and (Not(aux[length(aux)-1]in['0'..'9']))
then aux:=Copy(aux,1,length(aux)-1)+'1i';
if aux[length(aux)]='i' then
Begin
delete(aux,length(aux),1);
if aux[length(aux)] in ['0'..'9']
then Begin
i:=length(aux);
encontro:=false;
p:=0;
while (not encontro) and (i>1) do
Begin
if (aux[i]='+') or (aux[i]='-')
then Begin
encontro:=true;
p:=i
end;
i:=i-1
End;
PR:=Copy(aux,1,p-1);
delete(aux,1,p-1);
PI:=aux;
Val(PR,ParteReal,error);
Val(PI,ParteImag,error);
if error<>0 then
Begin
ParteReal:=0;
ParteImag:=0
End
End;
End
else
Begin
Val(aux,ParteReal,error);
End;
PonCad:=error
End;

Procedure TComplejo.Adicion(n:TComplejo);
Begin
PonReal(ObtReal+n.ObtReal);
PonImag(ObtImag+n.ObtImag)
End;

Procedure TComplejo.Multiplicacion(n:TComplejo);
var PR,PI:double;
Begin
PR:=(ObtReal*n.ObtReal)-(ObtImag*n.ObtImag);
PI:=(ObtReal*n.ObtImag)+(ObtImag*n.ObtReal);
PonReal(PR);
PonImag(PI)
End;

End.

  Unidad Complejos con el cosntructor Crear sobrecargado.


 

El siguiente programa hace uso de la unidad Complejos con los constructores


sobrecargados:
 

Uses Complejos;
Var A,B:TComplejo;

Begin
A:=TComplejo.Crear(10,4);
  B:=TComplejo.Crear(1);
Writeln('A=',A.ObtCad(0));
Writeln('B=',B.ObtCad(0))
End.

  Uso de la unidad complejos con los constructores sobrecargados.


 

Sobrecarga de métodos y de constructores (JAVA)

La firma de un método es la combinación del tipo de dato que regresa, su nombre


y su lista de argumentos.

La sobrecarga de métodos es la creación de varios métodos con el mismo nombre


pero con diferentes firmas y definiciones. Java utiliza el número y tipo de
argumentos para seleccionar cuál definición de método ejecutar.

Java diferencia los métodos sobrecargados con base en el número y tipo de


argumentos que tiene el método y no por el tipo que devuelve.

Tambien existe la sobrecarga de constructores: Cuando en una clase existen


constructores múltiples, se dice que hay sobrecarga de constructores.

Ejemplo

/* Métodos sobrecargados */
int calculaSuma(int x, int y, int z){
...
}
int calculaSuma(double x, double y, double z){
...
}

/* Error: estos métodos no están sobrecargados */


int calculaSuma(int x, int y, int z){
...
}
double calculaSuma(int x, int y, int z){
...
}

Ejemplo

/* Usuario4.java */

class Usuario4
{
String nombre;
int edad;
String direccion;

/* El constructor de la clase Usuario4 esta sobrecargado */


Usuario4( )
{
nombre = null;
edad = 0;
direccion = null;
}

Usuario4(String nombre, int edad, String direccion)


{
this.nombre = nombre;
this.edad = edad;
this.direccion = direccion;
}

Usuario4(Usuario4 usr)
{
nombre = usr.getNombre();
edad = usr.getEdad();
direccion = usr.getDireccion();
}

void setNombre(String n)
{
nombre = n;
}

String getNombre()
{
return nombre;
}

/* El metodo setEdad() está sobrecargado */


void setEdad(int e)
{
edad = e;
}

void setEdad(float e)
{
edad = (int)e;
}

int getEdad()
{
return edad;
}

void setDireccion(String d)
{
direccion = d;
}

String getDireccion()
{
return direccion;
}
}

Ejemplo

/* ProgUsuario4.java */

class ProgUsuario4
{
void imprimeUsuario(Usuario4 usr)
{
// usr.nombre equivale en este caso a usr.getNombre()
System.out.println("\nNombre: " + usr.nombre );
System.out.println("Edad: " + usr.getEdad() );
System.out.println("Direccion: " + usr.getDireccion() +"\n");
}

public static void main(String args[])


{
ProgUsuario4 prog = new ProgUsuario4( );
/* Se declaran dos objetos de la clase Usuario4 */
Usuario4 usr1,usr2;

/* Se utiliza el constructor por omisión */


usr1 = new Usuario4( );
prog.imprimeUsuario(usr1);

/* Se utiliza el segundo constructor de Usuario4 */


usr2 = new Usuario4("Eduardo",24,"Mi direccion");
prog.imprimeUsuario(usr2);

/* Se utiliza el tercer constructor de Usuario4 */


usr1 = new Usuario4(usr2);

usr1.setEdad(50);
usr2.setEdad(30.45f);

prog.imprimeUsuario(usr1);
prog.imprimeUsuario(usr2);
}
}

Tipos de Clases
Clases abstractas

 Representan conceptos muy generales,


 Describen la interfaz comun para el resto de subclaes
 No pueden ser instanciadas
 Pueden tener operaciones abstractas

Clases concretas o comunes

 Representan conceptos especificos


 Pueden ser instanciadas
 Suelen implementar operaciones abstractas heredadas

Clases finales
 Clases especiales que no pueden tener descendencia.
 Permiten instanciar objetos.

Ejemplos:

Clase abstracta.
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.
 
Una clase abstracta…
Es una clase que no se puede instanciar.
Se usa únicamente para definir subclases. 
¿Cuándo es una clase abstracta?
Cuando deseamos definir una abstracción que englobe objetos de distintos tipos  y
queremos hacer uso del polimorfismo.
Polimorfismo: se refiere a la posibilidad de definir clases diferentes que tienen métodos o
atributos denominados de forma idéntica, pero que se comportan de manera distinta.
 
abstract class FiguraGeometrica {
    . . .
    abstract void dibujar();
    . . .
}

class Circulo extends FiguraGeometrica {
    . . .
    void dibujar() {
        // codigo para dibujar Circulo
        . . .
    }

Se pueden crear referencias a clases abstractas como cualquier otra. No hay ningún
problema en poner:
  FiguraGeometrica figura;
Sin embargo una clase abstracta no se puede instanciar, es decir, no se pueden crear objetos
de una clase abstracta. El compilador producirá un error si se intenta:
  FiguraGeometrica figura = new FiguraGeometrica();
Esto es coherente dado que una clase abstracta no tiene completa su implementación y
encaja bien con la idea de que algo abstracto no puede materializarse. Sin embargo
utilizando el up-casting visto dedicado a la Herencia si se puede escribir:
FiguraGeometrica figura = new Circulo(. . .);
figura.dibujar();

Figura  es un clase abstracta que no tiene sentido calcular su área,  pero sí la de un


cuadrado o un círculo. Si una subclase  de Figura no define area() debe declararse también
como clase abstracta.

Polimorfismos y clases abstractas


Polimorfismo

Este es uno de los conceptos esenciales de una programación orientada a objetos. Así
como la herencia está relacionada con las clases y su jerarquía, el polimorfismo se
relaciona con los métodos.En un lenguaje de programación que cuenta con un
sistemas de tipo dinámico (en los que las variables pueden contener datos de
cualquier tipo u objetos de cualquier clase) .
Tipos de polimorfismo:

         Polimorfismo de sobrecarga

         Polimorfismo paramétrico

         Polimorfismo de inclusión

El polimorfismo de sobrecarga 

Ocurre cuando las funciones del mismo nombre existen, con funcionalidad similar, en
clases que son completamente independientes una de otra (éstas no tienen que ser
clases secundarias de la clase objeto). Por ejemplo, la clase complex, la clase imagen
y la clase link pueden todas tener la función "display". Esto significa que no
necesitamos preocuparnos sobre el tipo de objeto con el que estamos trabajando si
todo lo que deseamos es verlo en la pantalla.

Polimorfismo paramétrico

El polimorfismo paramétrico es la capacidad para definir varias funciones utilizando el


mismo nombre, pero usando parámetros diferentes (nombre y/o tipo). El polimorfismo
paramétrico selecciona automáticamente el método correcto a aplicar en función del
tipo de datos pasados en el parámetro.

Polimorfismo paramétrico

El polimorfismo paramétrico es la capacidad para definir varias funciones utilizando el


mismo nombre, pero usando parámetros diferentes (nombre y/o tipo). El polimorfismo
paramétrico selecciona automáticamente el método correcto a aplicar en función del
tipo de datos pasados en el parámetro.
Este seria una estructura de Polimorfismos:

Ejercicio:

class Mamifero {
public void mover() {
System.out.println("Ahora es un mamifero el que se mueve");
}
}
class Perro extends Mamifero {
public void mover() {
System.out.println("Ahora es un perro el que se mueve");
}
}
class Gato extends Mamifero {
public void mover() {
System.out.println("Ahora es un gato el que se mueve");
}
}
public class Polimorfismo {
public static void muevete(Mamifero m) {
m.mover();
 }
public static void main(String[] args) {
Gatobisho = new Gato();
Perrofeo = new Perro();
muevete(bisho);
muevete(feo);
 }
}
Clases abstractas

Hay ocasiones, cuando se desarrolla una jerarquía de clases en que algún


comportamiento está presente en todas ellas pero se materializa de forma distinta para
cada una. Por ejemplo, pensemos en una estructura de clases para manipular figuras
geométricas. Podríamos pensar en tener una clase genérica, que podría llamarse
FiguraGeometrica y una serie de clases que extienden a la anterior que podrían ser
Circulo, Polígono, etc. Podría haber un método dibujar dado que sobre todas las figuras
puede llevarse a cabo esta acción, pero las operaciones concretas para llevarla a cabo
dependen del tipo de figura en concreto (de su clase). Por otra parte la acción dibujar no
tiene sentido para la clase genérica FiguraGeometrica, porque esta clase representa una
abstracción del conjunto de figuras posibles.

Estructura de una clase abstracta:

abstract class FiguraGeometrica {


...
abstract void dibujar();
...
}

class Circulo extends FiguraGeometrica {


...
void dibujar() {
// codigo para dibujar Circulo
...
}
}

Ejercicio:

public abstract class Figura {


protected double x;
protected double y;
publicFigura (double x, double y) {
this.x = x;
this.y = y;
 }
public abstract double area ();
}
public class Circulo extends Figura {
private double radio;
publicCirculo (double x, double y, double radio) {
super(x,y);
this.radio = radio;
 }
public double area () {
returnMath.PI*radio*radio;
 }
}
public class Cuadrado extends Figura {
private double lado;
publicCuadrado (double x, double y, double lado) {
super(x,y);
this.lado = lado;
 }
public double area () {
returnlado*lado;
}

También podría gustarte