Introducción a la Programación Orientada a Objetos El paradigma imperativo. En un programa se tienen una serie de variables con las cuales operamos y modificamos mediante sentencias y funciones para producir un resultado. Es lo que venimos explicando hasta ahora. Es posiblemente la más utilizada y es la adoptada por lenguajes como C y Pascal entre otros. La programación orientada a objetos se diferencia en que, en lugar de indicar cómo una variable cambia mediante una serie de instrucciones, simplemente indicamos a los objetos que tienen que realizar una acción y son ellos los que ejecutan una serie de instrucciones internas para realizar la acción indicada. La programación orientada a objetos engloba a la programación imperativa que conocemos hasta ahora añadiendo algunos conceptos extra (como en el caso de C++, un lenguaje orientado a objetos, que puede entenderse como una extensión de C, un lenguaje imperativo). La principal ventaja de la POO es la organización de los programas. Es probablemente la forma más natural de programar ya que tiene una correspondencia directa con la realidad en la mayoría de los casos. Clases y objetos. Estos conceptos no son específicos de Java, sino que son válidos para casi todos los lenguajes orientados a objetos. Incluso la forma de escribir el código Java que definen las clases es similar en otros lenguajes (como por ejemplo C++). El concepto clave de la POO es la clase. Una clase no es más que un concepto (como mesa o perro), que a la hora de implementarlo se compone de conjunto de variables (que conforman su estado) junto con un conjunto de funciones que se aplican sobre ellas (que conforman su comportamiento). Un ejemplo de clase puede ser la clase Persona cuyos datos internos son por ejemplo: nombre, peso, altura, sexo, edad, y cuyos métodos son: anda (); come (); habla (); crece (); La clase es el concepto. Para utilizarla tenemos que instanciarla (crear un objeto). Una clase es el correspondiente a un tipo de dato (como un entero) y un objeto el correspondiente a una variable (int a;). Por ejemplo una clase puede ser Persona y los objetos de la clase serían Jose, Juan, Alejandra y Graciela, que son todos Personas. En Java, la clase anterior se representaría de la siguiente forma: public class Persona { private String nombre;
private double peso; private double altura; private int edad; private int sexo; // Dos constantes para el sexo: public static final Hombre = 0; public static final Mujer = 1; public Persona (){ nombre = "Desconocido"; peso = 0; altura = 0; edad = 0; sexo = hombre; public Persona (String n, double p, double a, int s, int e){ nombre = n; peso = p; altura = a; sexo = s; edad = e; public void anda (){ // Al andar perdemos 100 g de peso peso -= 0.1; public void come (){ // Comemos 100 g, por lo que aumentamos el peso peso += 0.1; public void habla (){ System.out.print ("Hola, mi nombre es " + nombre + ", tengo " + edad + " años, mido " + altura + " metros, peso " + peso + " kg y soy "); if (sexo == 0){ System.out.println ("un hombre."); else{ System.out.println ("una mujer."); public void crece (){ if (edad < 18){ altura += 0.1; edad ++; Vamos por partes: La palabra reservada class indica que lo que sigue a continuación es la definición de una clase.
o Public indica que la clase será visible por todas las demás clases del paquete. Por ahora basta con que sepan que un paquete es un conjunto de clases Persona es el nombre que le hemos dado a la clase. Cada clase que creemos deberá estar en el archivo NombreDeLaClase.java, en nuesto caso, la clase Persona estará en Persona.java. Dentro de la clase vamos declarando las variables y métodos de la case, indicando su tipo: private: Las variables y métodos private no podrán ser accedidas por ningúna otra clase. Lo más comun es que todas las variablessean privadas. De hecho, si no indicamos nada, las variables serán privadas por defecto, por lo que en el ejemplo anterior la palabra private es innecesaria realmente. Por ejemplo si intentamos acceder a la edad int edad = Juan.edad; daría un error. Para poder acceder a los datos o modificarlos tendríamos que crear métodos públicos dentro de la clase: public int getedad (){ return (edad); public void setedad (int e){ edad=e; Esto se hace por seguridad, para mantener en todo momento la consistencia interna de los datos. También nos puede interesar definir métodos privados para ser usados por la clase, pero que no nos interesa que puedan ser usados fuera de ésta. o o public: Las variables y métodos publicos pueden ser accedidas por otras clases. Es lo normal en la mayoría de los métodos, aunque ráramente una variable es pública (en general es de un mal estilo de programación). De hecho, para Java, toda función por defecto es pública si no se indica lo contrario. protected: Es equivalente a private, salvo por que las clases que hereden de ésta sí podrán acceder al método o la variable. Una función especial es la que lleva el nombre de la clase y no devuelve nada. Este método es el constructor y es llamado solo una vez, al crearse el objeto. En nuestro caso es la función Persona que inicializa los valores internos de la clase de forma que cuando llamemos a Persona juan = new Persona ("Juan", 72.5, 1.80, 0, 40); El objeto se creará con el nombre Juan, el peso 89.1 kg, etc. El constructor no tiene porqué recibir parámetros. Al constructor que no tiene parámetros se le suele llamar constructor por defecto. Opcionalmente se puede añadir la palabra reservada final. Dependiendo a qué se aplique puede significar una cosa u otra:
Si se aplica sobre una variable, indica que la variable es constante y no podrá ser modificada. Por ejemplo las variables PI=3.1415926535 o IVA=0.18 podrían ser variables finales: class Ejemplo{ private final float PI; private final float IVA; public Ejemplo (){ PI=3.14159265358979; IVA=0.18; Si se aplica sobre un método, indica que dicho método no podrá ser reescrito por una subclase (Ver herencia). Equivalentemente, si se aplica sobre una clase, indica que no se podrán crear clases que hereden de ésta. Otro de los modificadores es la palabra reservada static: Cuando se aplica sobre una variable, indica que la variable pertenece a la clase, no al objeto, es decir, que en lugar de haber una variable por cada objeto que creemos, la variable será la misma para todos los objetos de la clase, por lo que podrán compartir un valor. Sin embargo, el acceso se hace como con cualquier otra variable. Por ejemplo, podemos usar la variable como contador del número de objetos de dicha clase: class Ejemplo{ private static int contadorobjetos; Ejemplo (){ contadorobjetos++; int getnumobjetos (){ return (contadorobjetos); También puede ser útil para definir constantes de la clase si añadimos la palabra final. Por ejemplo, la clase Math de Java tiene las constantes PI y E que pueden ser accedidas sin crear un objeto de la clase Math, ya que son estáticas: Math.PI; En nuestro ejemplo, hemos añadido las constantes Hombre y Mujer con valores 0 y 1 para indicar explícitamente cual es cual. Cuando se aplica sobre un método, igualmente el método no pertenece al objeto sino a la clase. Class Ejemplo{ static int num (){ return (3);
Si creamos un objeto obj de la clase Ejemplo, no podremos llamar a la funcion obj.num (); sino que tendremos que llamarla de la forma Ejemplo.num (); esto es muy útil para implementar funciones que no necesiten datos internos. De hecho, una función estática no puede acceder a datos no estáticos de la clase, ya que el método estático no pertenece a ningún objeto. Uno de los usos más comunes de métodos estáticos es la función principal main. La función main es la primera en ejecutarse en un programa, la cual llamará a otras funciones. Herencia. El clásico ejemplo de herencia es el siguiente: supongamos que tenemos una clase Animal. Esta sería una clase muy genérica. Podríamos generar otras clases más concretas como Mamífero, Ave, Pez y Reptil, todas subclases de la superclase Animal. A la clase Animal se le llama clase padre o superclase y el resto serían las clases hijas o subclases. También podemos seguir creando clases como Gaviota, Paloma que heredan de Ave y Humano, Elefante y Perro que heredan de mamífero (podríamos continuar creando las clases Caniche, Labrador, Husky, etc como subclases de perro). Imaginemos que nos interesa el número de patas de los animales. En la clase Animal la variable estaría inicializada a 4 en el constructor, por ejemplo, de forma que cualquier subclase heredará dicha variable con el valor 4, siempre que no se indique lo contrario. La clase Pez sobreescribirá el valor de la variable patas para inidicar que no tiene ninguna y la clase humano y ave también para indicar que tienen 2. Sin embargo, la clase Perro no tendrá que modificarla y heredará el valor por defecto.
Igualmente pasa con los métodos: Si la clase Perro tiene el método void ladra (){ System.out.println ("Guau"); Las subclases de Perro no necesitarán volver a implementar el método, ya que lo heredan de la clase padre: class Animal{ int numpatas; Animal (){ numpatas=4; int getnumpatas (){ return (numpatas); class Mamifero extends Animal{ Mamifero (){ numpatas=4; class Humano extends Mamifero{ Humano (){ numpatas=2;
class Perro extends Mamifero{ Perro (){ void ladra (){ System.out.println ("Guau"); class Chiwawa extends Perro{ Chiwawa (){ Chiwawa miperro = new Chiwawa (); miperro.ladra(); // Devuelve Guau int numpatas = miperro.getnumpatas(); // Devuelve 4 Humano yo = new Humano (); yo.getnumpatas (); // Devuelve 2 La herencia se indica mediante la palabra reservada extends. Class B extends A indica que la clase B hereda de A. Aunque las clases hijas heredan todas las propiedades de las clases padre por defecto, también pueden sobreescribirlas. Polimorfismo. Se refiere a la capacidad para que varias clases derivadas de una antecesora utilicen un mismo método de forma diferente. Esto quiere decir simplemente que si tenemos dos clases A y B: class A{ void m (){ /*implementacion*/ class B extends A{ void m (){ /*otra implementacion*/ Si hacemos: A objeto = new B (); objeto.m (); La clase de objeto será aparentemente A pero su clase real será B. La llamada al método m ejecutará el método de la clase real, es decir, el método m de B.
Abstracción. Los métodos abstractos no son más que métodos no implementados. Las clases abstractas no pueden ser instanciadas (es decir, que no se pueden crear objetos de clases abstractas), por lo que obligamos a que los métodos deban ser implementados en las subclases. Toda clase que contenga un método abstracto, es una clase abstracta. Una clase/método abstracto se declara con la palabra reservada abstract: abstract class Motor{ abstract void Arrancar (); Si la clase no tuviese el modificador abstract, daría un error de compilación. Como ya hemos dicho, no se pueden crear objetos de una clase abstracta, aunque se pueden crear clases heredadas de una clase abstracta, que a su vez pueden ser abstractas, o implementar los métodos de la clase padre. Interfaces. Las interfaces son algo muy similar a las clases abstractas. Una interfaz indica un conjunto de funciones que deben ser implementadas por la clase. Un ejemplo de interfaz es: public interface Dibujable{ public abstract void draw (); Para indicar que una clase implementa una interfaz se usa la palabra reservada implements: public class Rectángulo implements Dibujable{ public void draw (){ /*dibujar*/ La diferencia esencial entre una interfaz y una clase abstracta es que la primera solo incluye métodos sin implementar (y opcionalmente constantes) mientras que la segunda puede incluir variables, métodos implementados, etc. Las interfaces son útiles cuando queremos indicar los métodos que tienen que implementar una serie de clases, pero no como se implementan. En el ejemplo anterior, indicamos que todo objeto que se quiera dibujar, debe implementar el método draw. Así, si tuviésemos una clase ventana, en lugar de contener una serie de objetos de una clase determinada, por ejemplo Rectangulo objetos []; podría contener una serie de objetos que implementen una interfaz:
Dibujable objetos []; Donde el tipo real de "Objetos" será Rectangulo o cualquier otra clase que implemente la interfaz. A la hora de dibujar los elementos en pantalla, la clase ventana llamaría al método draw de los objetos sin importar de qué clase sean. Un ejemplo real donde se usan las interfaces es en la creación de hebras. Si queremos que un objeto se ejecute en un hilo separado, la clase debe implementar la interfaz Runnable, en concreto el método run ();. Además, Java nos da la posibilidad de heredar de la clase Thread en lugar de implementar esta interfaz. Paquetes. Los paquetes no son más que conjuntos de clases que están relacionadas, bien porque forman parte de un mismo proyecto, o bien porque dicho paquete es una biblioteca. Toda clase debería estar contenida en un paquete. Si una clase pertenece a un paquete P lo indicaremos con package P; que generalmente se coloca en la primera linea del archivo donde está definida la clase. Con esto, todas las clases que indiquen package P conformarán un paquete. Los paquetes pueden contener clases, interfaces, tipos enumerados e incluso otros paquetes. Si un paquete está dentro de otro lo denotaremos con el '.'. Así, por ejemplo, tenemos el paquete "java", el paquete "util" que está dentro de "java" (java.util) y la clase ArrayList que está dentro de "util" (java.util.arraylist). Si queremos utilizar las clases de un paquete, podemos escribir el nombre completo del paquete +. + nombre de la clase (como java.util.arraylist) o importar un paquete al comienzo del archivo con import nombredelpaquete; y luego utilizar la clase como si perteneciese a nuestro paquete. Para poder acceder a las clases de otros paquetes externos, éstas deben ser públicas (si tubiesen el modificador package, por ejemplo, no podríamos acceder a ella).