INACAP Universidad Tecnológica de Chile Sede Santiago Centro Taller de Programación I Curso Java J2SE Tema 06: Orientación al Objeto en Java (parte 2) Ing. Manuel López Ramos (versión 1) Parte I Trabajo con objetos de Java 1) Clases y objetos ● Las clases de Java permiten ENCAPSULAR datos dentro de un mismo nombre de variable. ● Las clases también permiten DEFINIR métodos (o acciones) para todos los objetos de dicha clase. ● Luego de definir una clase, se puede crear una INSTANCIA de dicha clase con la palabra clave new. 1) Clases y objetos ● ● Conceptos nuevos: ENCAPSULAMIENTO DEFINICIÓN DE CLASE INSTANCIA DE CLASE Ejemplo: clase Perro y creación de 2 instancias de clase (objetos) class Perro { String nombre; String raza; float peso; char sexo; } ● ... ... Perro p1 = new Perro(); Perro m1 = new Perro(); Al crear una instancia de clase, se puede imaginar como se muestra a continuación. a) Descripción del objeto en memoria RAM (datos de muestra) b) Descripción en diagrama de objetos de UML (datos de muestra) 1) Clases y objetos ● Otro ejemplo: clase Archivo y creación de 2 instancias de clase (objetos) class Archivo { String ruta; String tipo; String fechaCreación; int idDueño; Archivo(){} } Archivo(String r) { this.ruta = r; } ... ... Archivo f1 = new Archivo(); Archivo f2 = new Archivo("/usr"); 1) Clases y objetos ● Representación de las instancias de clase: ... ... Archivo f1 = new Archivo(); Archivo f2 = new Archivo("/usr"); a) Creación de instancias b) Descripción del objeto en memoria RAM c) Descripción en diagrama de objetos de UML 2) Visibilidad o tipos de acceso a datos La visibilidad es otro concepto importante en las aplicaciones de Java. Indica cómo acceder a los atributos y métodos de una clase en la aplicación. ● ● Existen 4 tipos de visibilidad: 1) de paquete: los atributos y métodos sólo pueden usarse dentro del paquete actual. No usan símbolo en UML y tampoco tienen una palabra clave en Java. 2) pública: los atributos y métodos pueden usarse desde cualquier parte de la aplicación. Se simboliza en UML con un signo de adición (+) y en Java con la palabra clave public. 3) privada: los atributos y métodos sólo pueden usarse desde dentro de la clase donde se definieron. Se simboliza en UML con un signo de resta (-) y en Java con la palabra clave private. 4) protegida: los atributos y métodos sólo pueden usarse dentro de la clase donde están definidos y desde clases hijas en herencia. Se simboliza en UML con un signo de gato (#) y en Java con la palabra clave protected. 2) Visibilidad ● Tipos de visibilidad: Visibilidad Palabra clave Símbolo de UML por defecto (de paquete) (no tiene) privada (no usa palabra clave) private protegida protected # pública public + - 2) Visibilidad Como regla general, los atributos deberían declararse como privados (para evitar el acceso directo desde la aplicación) y los métodos deberían declararse como públicos (para utilizarlos en los programas con libertad) -> ENCAPSULAMIENTO de datos ● ● Las clases deberían ser públicas (hay excepciones). ● Se debería definir una clase por archivo (por orden, pero hay excepciones). public class Perro { private String nombre; private String raza; private float peso; private char sexo; } public class Archivo { private String ruta; private String tipo; private String fechaCreación; private int idDueño; public Archivo(){} } a) Archivo Perro.java public Archivo(String r) { this.ruta = r; } b) Archivo Archivo.java 2) Visibilidad Para acceder a atributos no privados, se puede utilizar directamente la notación de punto. Por ejemplo, si se quiere obtener el nombre de un objeto Perro p: ● ... ... Perro p = new Perro("Rex", "Boxer", 45, 8.9F); String nombrePerro = p.nombre; // Acceso directo a String razaPerro = p.raza; // los atributos ... // del objeto. ... NOTA: Esto funciona sólo si los atributos nombre y raza de la clase Perro se declaran como public. Para acceder a atributos privados, se hace necesario utilizar métodos selectores (getters) y mutadores (setters). ● Parte II Tipos de métodos 3) Estructura de una clase ● Las clases de Java internamente comprenden los siguientes elementos: ● lista de atributos ● métodos constructores ● métodos selectores (getters) ● métodos mutadores (setters) ● métodos de clase generales (o métodos comunes) ● métodos estáticos ● método main (opcional) 3) Estructura de una clase Cada uno de los elementos indicados permite identificar diferentes aspectos dentro del programa y de la aplicación. ● ● ● ● ● ● ● Lista de atributos: define los campos donde se almacenarán los datos de los objetos. Método constructor: crea instancias de clase e inicializa sus atributos. Métodos selectores: recuperan los datos almacenados en los atributos de una instancia. Métodos mutadores: modificar los datos de los atributos de una instancia de clase. Métodos estáticos: aquellos que no necesitan ejecutarse sobre una instancia de clase. Métodos generales (o comunes): aquellos que representan las operaciones propias y reales del concepto a modelar mediante la clase. 4) Métodos constructores Los métodos constructores permiten crear instancias de clase e inicializar sus atributos, si se desea. ● ● Un método constructor se define utilizando la sintaxis: public <nombre_de_la_clase>(parámetros_iniciales) { // inicialización de atributos. } por lo que un método constructor no tiene tipo de retorno explícito (es decir, escrito en el código). Si no se crea un método constructor explícito (a mano), Java entrega uno, denominado como el constructor por defecto de la clase. Este constructor no tiene parámetros de entrada y, por lo general, sólo crea al objeto, pero no lo inicializa más que con valores por omisión. ● 4) Métodos constructores Pueden existir varios métodos constructores, pero deben diferenciarse en una de las siguientes características: ● a) cantidad de parámetros de entrada. b) tipos y orden de los parámetros de entrada. Por ejemplo: public NumeroComplejo() { ... } public NumeroComplejo(double parteReal) { ... } public NumeroComplejo(float parteImaginaria) { ... } public NumeroComplejo(double parteReal, double parteImaginaria) { ... } 4) Métodos constructores Los métodos constructores tienen por misión inicializar los objetos antes de su uso. Por lo tanto, sólo deberían incluir código de inicialización de atributos en base a valores por omisión o a parámetros de entrada. Por ejemplo: ● public class ListaUsuarios { private int maxUsuarios; private int diaCreacion, mesCreacion; private String nombre, creadorLista; public ListaUsuarios(int numMaxUsuarios, String nombre) { this.maxUsuarios = numMaxUsuarios; // valor parametrizado this.nombre = nombre; this.creadorLista= ""; // valor por omisión this.diaCreacion = 1; this.mesCreacion = 1; } } ... ... 4) Métodos constructores (uso de this) Nótese que para referenciar a un atributo (ya sea para lectura o escritura de un valor) se usa normalmente la palabra reservada this. ● La palabra reservada this indica que el atributo a utilizar es el del objeto a crear y no el atributo de algún otro objeto del programa. ● ... } public ListaUsuarios(int numMaxUsuarios, String nombre) { this.maxUsuarios = numMaxUsuarios; // valor parametrizado this.nombre = nombre; this.creadorLista= ""; // valor por omisión this.diaCreacion = 1; this.mesCreacion = 1; } ... ... La palabra this puede utilizarse en cualquier método dentro de la clase (ya sea un constructor, selector, mutador o método general). Sólo se prohíbe su uso dentro de métodos estáticos (a estudiar más adelante). ● 5) Métodos selectores Los métodos selectores (o métodos getters) permiten recuperar los datos almacenados en los atributos de una instancia. ● Su función consiste en dar acceso a los atributos privados de un objeto para leer los datos en ellos. ● ● Su forma de creación es estándar y se mustra a continuación: public <tipo_atributo> get<nombre_atributo>() { return this.<nombre_atributo>; } a) Sintaxis básica de escritura de un selector en Java. b) Ejemplo de selectores en la clase Perro class Perro { String nombre; String raza; float peso; char sexo; ... ... public String getNombre() { return this.nombre; } } public float getPeso() { return this.peso; } ... // etc. 6) Métodos mutadores Los métodos mutadores (o métodos setters) permiten recuperar los datos almacenados en los atributos de una instancia. ● Su función consiste en dar acceso a los atributos privados de un objeto para escribir nuevos datos en ellos. ● ● Su forma de creación también es estándar y se mustra a continuación: public void set<nombre_atributo>(<tipo> <param>) { this.<nombre_atributo> = <param>; } a) Sintaxis básica de escritura de un mutador en Java. b) Ejemplo de mutadores en la clase Perro class Perro { String nombre; String raza; float peso; char sexo; ... ... public void setNombre(String v) { this.nombre = v; } } public void setPeso(float v) { this.peso = v; } ... // etc. 7) Métodos generales de clases Los métodos generales de clases representan las operaciones propias del concepto que se está representando. Por ejemplo: ● ● Clase Perro: métodos ladrar(), comer(), dormir(), nacer(), etc. ● Clase Edificio: métodos construir(), demoler(), pintar(), etc. ● Clase Avion: métodos volar(), aterrizar(), planear(), etc. En estos tipos de métodos, el tipo de retorno y los parámetros de entrada se definen de acuerdo al problema; no hay una directriz obligatoria al respecto. ● 8) Atributos y métodos estáticos Los métodos anteriormente vistos (a excepción del constructor) deben ser aplicados sobre una instancia de clase (creada con la palabra clave new). Por ejemplo: ● ... ... Perro p1 = new Perro(); p1.setNombre("Lassie"); p1.setRaza("Beagle"); p1.setPeso(8.9F); p1.setSexo('M'); float pesoActual = p1.getPeso(); p1.ladrar(); p1.comer(); p1.dormir(); ... ... // etc. 8) Atributos y métodos estáticos Sin embargo, hay oportunidades en las cuales conviene crear métodos que se puedan aplicar sin objetos, es decir, "en el aire" o "de forma individual y directa". O dicho de otra forma: métodos que se puedan aplicar a la clase y no a un objeto individual. ● Por ejemplo: supóngase que se necesita un método de la clase Perro que cuente el número total de perros creados en la aplicación. Se desprende de esto que un perro individual no tiene que llevar la contabilización de los demás, sino que más bien la clase debe registrar dicho valor. ● Para esto, se utilizará un atributo estático; es decir, que pertenezca a la clase completa y no sólo a un objeto en particular. ● 9) Atributos estáticos public class Perro { private String nombre, raza; ... ... private static int numPerrosCreados = 0; public Perro() { this.nombre = ""; this.raza = ""; numPerrosCreados++; } } // Llamada a atributo estático 9) Atributos estáticos public class Perro { private String nombre, raza; ... ... private static int numPerrosCreados = 0; public Perro() { this.nombre = ""; this.raza = ""; numPerrosCreados++; } // Llamada a atributo estático } Nótese que para llamar al atributo estático, no puede utilizarse la palabra clave this. ● El atributo estático numPerrosCreados no pertenece a un objeto en particular, sino que a la clase completa. ● 10) Métodos estáticos ● Para acceder a atributos estáticos también suele utilizarse métodos estáticos. Por ejemplo: supóngase que se necesita también un método que lleve a cero al atributo del ejemplo anterior. Entonces se creará el método estático reiniciarContadorPerros (usando la palabra clave static) para realizar tal acción. ● public class Perro { private String nombre, raza; ... ... private static int numPerrosCreados = 0; public Perro() { this.nombre = ""; this.raza = ""; numPerrosCreados++; } } // Llamada a atributo estático public static void reiniciarContadorPerros() { numPerrosCreados = 0; } 10) Métodos estáticos Finalmente, para utilizar un método estático desde el programa principal, se indicará el nombre de la clase y el nombre del método, como se muestra a continuación: ● public class TestPerro { public static void main(String[] { Perro p1 = new Perro(); Perro p2 = new Perro(); Perro p3 = new Perro(); // // // } } args) Hasta acá se habrán contabilizado ya 3 perros nuevos. Perro.reiniciarContadorPerros(); // Vuelve el conteo a cero. Parte III Referencias a objetos Implementación de relaciones entre clases Cuando se necesita implementar relaciones entre clases en código, es importante fijarse en dos aspectos: ● 1) la dirección de la relación 2) la cardinalidad de la relación Supóngase que se necesita implementar las siguientes relaciones entre clases en una aplicación en Java: ● Implementación de relaciones entre clases ● Nótese que hay 2 relaciones: * relación AppMascota03 con Amo ("1 AppMascota03 tiene 2 Amos") * relación Amo con Perro ("1 Amo tiene 1 Perro") ● En Java, la implementación de relaciones se traduce en: "crear un objeto de una de las clases dentro de la clase opuesta" Implementación de relaciones entre clases Pero, ¿qué clase entrega un objeto y cuál lo recibe? En general, se puede seguir la siguiente regla (para relaciones unidireccionales): ● "se deben crear objetos de la clase con mayor cardinalidad dentro de la clase con menor cardinalidad" ● De esta forma, la clase AppMascota03 recibe 2 instancias (objetos) de la clase Amo. Implementación de relaciones entre clases Amo a1 = new Amo(...); Amo a2 = new Amo(...); Pero, ¿y si la cardinalidad es 1-a-1? En general, se puede seguir otra regla (también para relaciones unidireccionales): ● "se deben crear objetos de la clase que tenga la 'punta de flecha' (de la relación) dentro de la clase opuesta a la 'punta de flecha' " ● De esta otra forma, la clase Amo recibe 1 instancia (objeto) de la clase Perro. Implementación de relaciones entre clases Perro p1 = new Perro(...); Pero, ¿y si la cardinalidad es 1-a-1? En general, se puede seguir otra regla (también para relaciones unidireccionales): ● "se deben crear objetos de la clase que tenga la 'punta de flecha' (de la relación) dentro de la clase opuesta a la 'punta de flecha' " ● De esta otra forma, la clase Amo recibe 1 instancia (objeto) de la clase Perro. Implementación de relaciones entre clases ● Finalmente, las clases quedarían escritas de la siguiente forma: public class Amo { private Perro p1; } public class Perro { ... ... } public class AppMascota03 { private static Amo a1; private static Amo a2; } public static void main(String[] args) { a1 = new Amo("", 'M'); a2 = new Amo("", 'F'); ... ... } public Amo(String nom, char sex) { this.p1 = new Perro(); ... } ... Nótese que los objetos deben crearse, usando new. Si no se hace, la cardinalidad implementada sería de cero, lo cual no cumple con lo solicitado. ● También nótese que los atributos generados en la clase principal se declaran como estáticos para poder utilizarlos desde el método main. ● Conclusión Finalmente, para diagramas de UML más grandes (con más clases, con más relaciones, etc.): ● a) identificar todas las relaciones entre clases. Cada una de ellas generará un atributo en otra clase. b) leer correctamente la cardinalidad de la relación. Esta indica cuántos objetos crear dentro de la clase correspondiente. c) Para relaciones bidireccionales se sigue otro procedimiento (se indicará más adelante). d) Para cardinalidades mayores a 3, se necesitará implementar un arreglo de objetos, o bien, una lista de objetos (ArrayList o LinkedList). Parte III Uso de métodos con referencias a objetos Referencias a objetos en relación Cuando ya se comprende cómo crear las relaciones entre clases, se hace necesario entender ahora cómo utilizar los objetos generados por dichas relaciones. ● Aquí es donde se puede apreciar la importancia de contar con métodos selectores (getters) y mutadores (setters), ya que éstos son los que permiten acceder a atributos con visibilidad privada (private) dentro de un objeto. ● El siguiente análisis se realizará en base al diagrama de clases de UML de la sección anterior: ● Referencias a objetos en relación Diagrama de clases de la sección anterior: AppMascota-Amo-Perro Parte IV Trabajo avanzado con objetos