Java y La Orientación A Objetos
Java y La Orientación A Objetos
Dos Paradigmas.
Todos los programas están formados por código y datos, al mismo tiempo un
determinado programa puede estar organizado en base a su código o a sus datos.
Así, nos encontramos con dos posibles paradigmas:
Abstracción.
• Encapsulado
• Herencia
• Polimorfismo.
Encapsulado.
El encapsulado es el mecanismo que permite juntar el código y los datos que
manipula éste, para mantenerlos alejados de posibles interferencias o usos
indebidos. Es como un envoltorio protector que evita que otro código que está
fuera pueda acceder arbitrariamente al código o a los datos. De esta forma, el
acceso al código y a los datos se realiza de forma controlada a través de una
interfaz bien definida.
El poder del código encapsulado es que todo el mundo conoce cómo acceder a
él y pueden utilizarlo independientemente de los detalles de implementación; y
esto sin temor a efectos laterales inesperados.
En Java, la base del encapsulado es la clase. Una clase define la estructura y el
comportamiento (datos y código) que serán compartidos por un conjunto de
objetos. Cada objeto de una clase dada contiene la estructura y el
comportamiento definidos por la clase; a los objetos se les llama
también instancias de una clase. Una clase es una construcción lógica, mientras
que un objeto tiene realidad física.
Cuando se crea una clase, hay que especificar el código y los datos que la
constituyen. Estos elementos son los miembros de la clase. Los datos definidos
por la clase son las variables miembro o variables de instancia. El código que
opera sobre esos datos se llama método miembro, o simplemente método. La
palabra equivalente a método en C/C++ sería función. En un programa Java
escrito correctamente, los métodos definen cómo se pueden utilizar las variables
miembro. De esta manera, el comportamiento y la interfaz de una clase se
definen mediante métodos que operan sobre sus datos de instancia.
Como el propósito de una clase es encapsular la complejidad, debería haber
mecanismos para ocultar la complejidad de la implementación dentro de la clase.
Por esto, cada variable o método de una clase puede marcarse como público o
privado:
Si una variable o método de una clase es público, los usuarios externos a la
clase pueden acceder a esa variable o método; sin embargo, si una variable o
método de una clase es privado, entonces sólo puede ser utilizado por código
miembro de la clase.
Herencia.
Sin embargo, utilizando la herencia, un objeto puede heredar sus atributos
generales de otro objeto (su padre), y definir explícitamente sólo aquellas
cualidades que lo hacen único dentro de su clase. Por tanto, la herencia es el
mecanismo que permite a un objeto ser una instancia específica de un caso más
general.
Una clase que hereda de otra, se denomina subclase, y aquella clase de la que
se hereda, se denomina superclase.
Por ejemplo, supongamos que se define una clase para describir a los
animales; se podría definir otra clase para describir a los mamíferos. La clase
"mamíferos" heredaría todos los atributos de la clase "animales", pero además
definiría una serie de atributos específicos de los mamíferos, como el tipo de
dientes, o las glándulas mamarias. En este caso, la clase "mamíferos" es subclase
de "animales", y la clase "animales" es superclase de "mamíferos".
Una subclase hereda todos los atributos de cada uno de sus antecesores en la
jerarquía de clases. Esta característica permite a los programas orientados a
objetos crecer en complejidad de manera lineal en vez de geométrica.
Polimorfismo.
El polimorfismo, es una característica que permite que una interfaz sea
utilizada por una clase general de acciones. La acción específica se determina a
partir de la naturaleza exacta de la situación.
Como ejemplo, supongamos que necesitamos escribir una función que opere
con caracteres, números enteros, y números reales; sin el polimorfismo, el
programador tendría que escribir tres veces la misma función y darle a cada una
un nombre diferente; gracias al polimorfismo, estas funciones podrían recibir el
mismo nombre.
Cualquier concepto que se quiera implementar en un programa Java debe ser
encapsulado en una clase.
Una clase define un nuevo tipo de dato. Una vez definido, este nuevo tipo se
puede utilizar para crear objetos de ese tipo. Así, un objeto es una instancia de
una clase. Las palabras "objeto" e "instancia" se suelen usar indistintamente.
Cuando se define una clase en Java, hay que especificar los datos que contiene
la clase y el código que opera sobre ellos. Aunque puede haber clases sencillas
que sólo contengan código o datos, la mayoría de las clases contiene ambas
cosas.
Normalmente, las variables de instancia de una clase son modificadas por los
métodos definidos en esa clase, por lo que los métodos son los que determinan
cómo se pueden utilizar los datos de una clase.
Cuando se crea un objeto de una determinada clase, ese objeto tiene su propia
copia de las variables de instancia; los datos de un objeto son propios y están
separados de los datos de otro objeto, aunque sean objetos de la misma clase.
Una vez que se define una clase, para declarar objetos de esa clase, hay que
realizar dos pasos:
• Declarar una variable del tipo de la clase; esta variable no es el objeto, sino
una referencia al mismo.
• Obtener una copia física y real del objeto y asignarla a esa variable; esto se
realiza con el operador de Java new. El operador new asigna dinámicamente, es
decir, en tiempo de ejecución, memoria para un objeto y devuelve una referencia
al mismo. Hasta que no se crea el objeto con el operador new, éste no existe
físicamente.
Con los tipos simples, como los caracteres o los enteros, no es necesario
utilizar el operador new, porque los tipos simples se implementan como variables
normales; esto es así por cuestiones de eficiencia en la implementación de Java.
Merece la pena resaltar de nuevo la diferencia entre clase y objeto: la clase es
una construcción lógica, mientras que un objeto tiene realidad física, es decir,
ocupa espacio en memoria.
No hay necesidad, por tanto, en Java, de destruir explícitamente los objetos,
como sucede en C++.
Métodos.
El tipo especifica el tipo de dato devuelto por el método. Puede ser cualquier
tipo válido, incluidos los tipos de clase; si el método no devuelve ningún valor,
entonces el tipo debe ser void. El nombre del método lo indica
nombre_de_método y lista_de_parámetros es la lista de parámetros
que recibe el método; los parámetros son variables que reciben el valor de los
argumentos que se pasan al método. Un método puede no tener parámetros, en
este caso la lista de parámetros está vacía.
Algunas veces un método necesita hacer referencia al objeto que lo invocó.
Para poder hacer esto, Java define la palabra reservada this, que puede ser
utilizada dentro de cualquier método para referirse al objeto actual. this también
se utiliza para resolver colisiones en el espacio de nombres.
Cuando se invoca un método sobrecargado, Java utiliza el tipo y/o el número
de argumentos como guía para determinar la versión del método sobrecargado
que realmente debe llamar. En algunos casos las conversiones de tipo
automáticas de Java pueden jugar un papel importante en la resolución de la
sobrecarga.
Control de acceso.
Una clase bien implementada debe ser como una "caja negra" que puede ser
utilizada pero no deja ver los detalles internos de su implementación.
Cuando no se utiliza ningún especificador de acceso, entonces, por defecto, los
miembros de una clase son públicos dentro de su propio paquete, pero no pueden
ser accedidos desde fuera de su paquete.
Herencia en Java.
La herencia permite las clasificaciones jerárquicas. En Java, para heredar una
clase, se utiliza la palabra reservada extends.
En una jerarquía de clases, cuando un método de una subclase tiene el mismo
nombre y tipo que un método de su superclase, entonces se dice que el método de
la subclase sobrescribe al método de la superclase.
En Java, cuando surge una condición excepcional, se crea un objeto que
representa la excepción y se envía al método que provocó esta excepción. Este
método puede elegir gestionar la excepción él mismo o pasarla. Pero en algún
punto, la excepción es capturada y procesada.
Las excepciones pueden ser generadas por el intérprete de Java o pueden ser
generadas por el propio código. Las excepciones generadas por Java están
relacionadas con errores que violan las reglas del lenguaje Java o las restricciones
del entorno de ejecución de Java. Las excepciones generadas manualmente se
suelen utilizar para informar de algún error al método llamante.
Cuando el intérprete de Java lanza una excepción, ésta debe ser capturada
porque de lo contrario el programa terminará su ejecución bruscamente. Hay
varios tipos de excepciones que son lanzadas automáticamente, como por
ejemplo, las excepciones de división por cero, de índice de matriz fuera del
rango de la misma, conversión de tipo inválida, etc.