PROGRAMACIÓN ORIENTADA A OBJETOS Dra. Maricela Bravo mari_clau_18@hotmail.com Herencia en el mundo real Cosa Medio de telecomunicación Medio de transporte Objeto de oficina Coche Vehiculo aéreo 2 Contenido 1. 2. 3. 4. 5. 6. 7. 8. Modificadores de acceso Encapsulación Sobrecarga de métodos Constructores Herencia Sobrescritura de métodos Clases abstractas e interfaces Polimorfismo Elaborado por Dra. Maricela Bravo 3 1. Modificadores de acceso Se utilizan para definir la visibilidad de los miembros de una clase (atributos y métodos) y de la propia clase. En Java existen cuatro modificadores de acceso que ordenados de menor a mayor visibilidad, son: ◦ private ◦ ninguno ◦ protected ◦ public Elaborado por Dra. Maricela Bravo 4 1. Modificadores de acceso private. Cuando un atributo o método es definido como private, su uso está restringido al interior de la clase, solamente puede ser utilizado en el interior de su misma clase. Este modificador puede ser aplicado a métodos y atributos, pero no a la clase. ninguno. A falta de la definición de modificador se utiliza el acceso por defecto. Si un elemento (clase, método o atributo) tiene acceso por defecto, únicamente las clases de su mismo paquete tendrán acceso al mismo. Elaborado por Dra. Maricela Bravo 5 1. Modificadores de acceso protected. Un método o atributo definido como protected en una clase puede ser utilizado por cualquier otra clase de su mismo paquete y además por cualquier subclase de ella. Una clase no puede ser protected, solo sus miembros. public. El modificador public ofrece el máximo nivel de visibilidad. Un elemento (clase, método o atributo) public será visible desde cualquier clase, independientemente del paquete en que se encuentren. Elaborado por Dra. Maricela Bravo 6 1. Modificadores de acceso private default protected public Clase No Si No Si Método Si Si Si Si Atributo Si Si Si Si Variable local No No No No Elaborado por Dra. Maricela Bravo 7 2. Encapsulación Los métodos que se quieren exponer al exterior llevan el modificador de acceso public, mientras que los atributos suelen tener acceso privado, de modo que solo pueden ser accesibles desde el interior de la clase. Ésta es precisamente la idea de la encapsulación: mantener los atributos de los objetos como privados y proporcionar acceso a los mismos a través de métodos públicos. Los beneficios: ◦ Protección de datos sensibles ◦ Facilidad y flexibilidad en el mantenimiento de las aplicaciones. Elaborado por Dra. Maricela Bravo 8 2. Encapsulación Protección de datos. Suponiendo que se necesita una clase que representa a una figura geométrica, por ejemplo un rectángulo. Dicha clase podría proporcionar diversos métodos para realizar cálculos sobre la figura, además de atributos de alto y ancho. Elaborado por Dra. Maricela Bravo 9 2. Encapsulación public class Rectangulo { public int alto, ancho; //métodos de la clase } Al utilizar esta clase desde cualquier programa, el programador podría hacer lo siguiente: Rectangulo r = new Rectangulo(); r.alto = -5; Elaborado por Dra. Maricela Bravo 10 2. Encapsulación Lógicamente, dimensiones con valores negativos no tienen sentido en una figura geométrica, además de provocar resultados incoherentes en la ejecución de los métodos de la clase. A este hecho se le conoce como corrupción de datos, y una forma de evitarlo sería protegiendo los atributos del acceso directo mediante la encapsulación, forzando a que el acceso se realice de forma controlada a través de métodos de acceso. Elaborado por Dra. Maricela Bravo 11 2. Encapsulación Facilidad en el mantenimiento de la clase. Si una vez que ha sido creada la clase deseamos cambiar el criterio sobre los posibles valores que pueden tomar los atributos, podríamos modificar el código de los métodos de acceso y distribuir la nueva versión de la clase, sin que los programadores que la utilizan tengan que cambiar una sola línea de código de sus programas. Elaborado por Dra. Maricela Bravo 12 3. Sobrecarga de métodos Cuando en una misma clase se definen varios métodos con el mismo nombre, se llama sobrecarga de métodos. La ventaja de la sobrecarga de métodos es que si tenemos varios métodos que van a realizar la misma operación, no necesitamos asignarle un nombre diferente a cada uno. Para que un método pueda sobrecargarse es imprescindible que se cumpla la siguiente condición: cada versión del método debe distinguirse de las otras en el número o tipo de parámetros. El tipo de devolución puede o no ser el mismo. Elaborado por Dra. Maricela Bravo 13 4. Constructores Un constructor es un método especial que es ejecutado en el momento en que se crea un objeto de la clase (cuando se llama al operador new). Podemos utilizar los constructores para añadir aquellas tareas que deban realizarse en el momento en que se crea un objeto de la clase, por ejemplo, la inicialización de los atributos. Elaborado por Dra. Maricela Bravo 14 4. Constructores Al crear un constructor hay que tener en cuenta las siguientes reglas: ◦ El nombre del constructor debe ser el mismo que el de la clase. ◦ El constructor no puede tener tipo de devolución. ◦ Los constructores se pueden sobrecargar. ◦ Toda clase debe tener al menos un constructor. Elaborado por Dra. Maricela Bravo 15 5. Herencia Es la capacidad de crear clases que adquieran de manera automática los miembros (atributos y métodos) de otras clases que ya existen, pudiendo al mismo tiempo añadir atributos y métodos propios. Ventajas de la herencia: ◦ Reutilización de código ◦ Mantenimiento de aplicaciones existentes Elaborado por Dra. Maricela Bravo 16 5. Herencia Superclase/ Clase base Subclase/ Clase derivada En POO, la clase de la que se va a heredar se llama superclase o clase base. Mientras que la que hereda se le conoce como subclase o clase derivada. Elaborado por Dra. Maricela Bravo 17 5. Herencia Reglas básicas para la herencia en Java ◦ No está permitida la herencia múltiple ◦ Si es posible la herencia multinivel, A puede ser heredada por B y C puede heredar de B. ◦ Una clase puede ser heredada por varias clases. Elaborado por Dra. Maricela Bravo 18 5. Herencia Clase A Clase C Clase B Clase A Clase B Clase A Clase C Clase B Clase C Elaborado por Dra. Maricela Bravo 19 5. Herencia public class subclase extends superclase { //código de la subclase } public class PuntoColor extends Punto { //código de la subclase } public class nombreClase extends Object { //código de la subclase } Elaborado por Dra. Maricela Bravo 20 5. Herencia Aunque una subclase hereda todos los miembros de la superclase, incluidos los privados, no tiene acceso directo a éstos, puesto que private significa privado a la clase o lo que es lo mismo, solamente accesibles desde el interior de ésta. Cada vez que Java crea un objeto de una clase, antes de ejecutarse el constructor de dicha clase se ejecutará primero el de su superclase. Ver ejemplo. Elaborado por Dra. Maricela Bravo 21 5. Herencia Métodos y atributos protegidos El modificador protected, es un modificador de acceso aplicable a atributos y métodos de una clase, pensado para ser utilizado con la herencia. Ver ejemplo. Es importante subrayar que las subclases acceden a los miembro protegidos a través de la herencia, no se puede utilizar una referencia a un objeto de la superclase para acceder al miembro protegido. Elaborado por Dra. Maricela Bravo 22 5. Herencia Clases finales Si queremos evitar que una clase sea heredada por otra, deberá ser declarada con el modificador final. public final class ClaseA { } Elaborado por Dra. Maricela Bravo 23 Tarea Investigar para qué se utilizan las siguientes interfaces en J2SE 1. 2. 3. 4. 5. java.lang.Runnable java.util.Enumeration java.awt.event.WindowListener java.sql.Connection java.io.Serializable Elaborado por Dra. Maricela Bravo 24 Herencia Aunque una subclase hereda todos los miembros de la superclase, incluidos los privados, no tiene acceso directo a éstos, puesto que private significa privado a la clase o lo que es lo mismo, solamente accesibles desde el interior de ésta. Cada vez que Java crea un objeto de una clase, antes de ejecutarse el constructor de dicha clase se ejecutará primero el de su superclase. Ver ejemplo. Elaborado por Dra. Maricela Bravo 25 Herencia Métodos y atributos protegidos El modificador protected, es un modificador de acceso aplicable a atributos y métodos de una clase, pensado para ser utilizado con la herencia. Ver ejemplo. Es importante subrayar que las subclases acceden a los miembro protegidos a través de la herencia, no se puede utilizar una referencia a un objeto de la superclase para acceder al miembro protegido. Elaborado por Dra. Maricela Bravo 26 5. Herencia Clases finales Si queremos evitar que una clase sea heredada por otra, deberá ser declarada con el modificador final. public final class ClaseA { } Elaborado por Dra. Maricela Bravo 27 Herencia en Java Java permite definir una clase como subclase de una clase padre. class clase_hija extends clase_padre { .......... } Clase Padre Clase Hija 28 Ejemplo de Herencia Polygon class Polygon #width #height +set_values() { protected int width, height; public void set_values (int a, int b) { Rectangle Triangle width=a; height=b; +area() } } +area() class Rectangle extends Polygon { public int area() { return (width * height); } } 29 class Triangle extends Polygon { public int area() { return (width * height / 2); } public static void main(String[] args) { Rectangle rect; Triangle trgl; rect = new Rectangle(); trgl = new Triangle(); rect.set_values (4,5); trgl.set_values (4,5); System.out.print("area" + rect.area() + '\n' + trgl.area() + '\n'); } } 30 Constructores y Herencia Cuando se declara un objeto de una clase derivada, se ejecutan los constructores siguiendo el orden de derivación, es decir, primero el de la clase base, y después los constructores de las clases derivadas de arriba a abajo. Para pasar parámetros al constructor de la clase padre: super (para1, para2, ..., paraN) 31 Ejemplo de super class Persona { private String nombre; private int edad; public Persona() {} public Persona (String n, int e) { nombre = n; edad = e; } } class Alumno extends Persona { private int curso; private String nivelAcademico; public Alumno (String n, int e, int c, String nivel) { super(n, e); curso = c; nivel_academico = nivel; } public static void main(String[] args) { Alumno a = new Alumno("Pepe", 1, 2, "bueno"); } } 32 Redefinir f. miembros de la clase padre class Persona { private String nombre; private int edad; ................... public String toString() { return nombre + edad; } public void setEdad(int e) { edad = e; } } class Alumno extends Persona { private int curso; private String nivelAcademico; ................... public String toString() { return super.toString() + curso + nivelAcademico; } public void setCurso(int c) { curso = c; } } 33 Referencias a objetos de clases hijas Si tenemos ClaseHijo hijo = new ClaseHijo(...); entonces es posible padre=hijo donde padre es una variable de tipo ClasePadre. ◦ pero no es posible!! hijo=padre (sí que es posible con casting hijo= (ClaseHijo) padre) Ahora bien: ◦ Con padre sólo podemos acceder a atributos y métodos def. en la clase padre. 34 Referencias a objetos de clases hijas public static void main(String[] args) { Persona p; Alumno a = new Alumno(“pepe”,23,1,”universitario”); p=a; //ref padre señala al objeto hijo // acceso al objeto hijo mediante la ref padre p.setEdad(24); /* no es posible acceder a un miembro de la clase hija usando una ref a la clase padre*/ p.setCurso(88); // ERROR } 35 Ejemplo class Persona { ................... } class Alumno extends Persona { ...................... public String toString() { return super.toString() + curso + nivelAcademico; } } class Profesor extends Persona { private String asignatura; public Profesor (String n, int e, String asign) { super(n, e); asignatura = asign; } public String toString() { return super.toString() + asignatura; } } 36 POLIMORFISMO 37 Polimorfismo Una misma llamada ejecuta diferentes sentencias dependiendo de la clase a la que pertenezca el objeto al que se aplica el método. Supongamos que declaramos: Persona p; Podría suceder que durante la ej. del programa, p referencie a un profesor o a un alumno en distintos momentos, y Entonces: ◦ Si p referencia a un alumno, con p.toString(), se ejecuta el toString de la clase Alumno. ◦ Si p referencia a un profesor, con p.toString(), se ejecuta el toString de la clase Profesor. Enlace dinámico: Se decide en tiempo de ejecución qué método se ejecuta. OJO!: Sobrecarga de fs => enlace estático (t. de compilación). 38 Ejemplo de Polimorfismo public static void main(String[] args) { Persona v[]=new Persona[10]; // Se introducen alumnos, profesores y personas en v for (int i=0 ; i<10; i++) /* Se piden datos al usuario de profesor, alumno o persona */ switch (tipo) { case /* profesor */: v[i] = new Profesor (….); break; case /* alumno */: v[i] = new Alumno(…); break; case /* persona */: v[i] = new Persona(…); break; default: /* ERROR */ } } for (int i=0 ; i<10; i++) System.out.println(v[i]); // enlace dinámico con toString() } 39 CLASES ABSTRACTAS 40 Clases Abstractas Una clase abstracta es una clase de la que no se pueden crear objetos, pero puede ser utilizada como clase padre para otras clases. Declaración: abstract class NombreClase { .............. } 41 Métodos abstractos Tenemos un método f() aplicable a todos los objetos de la clase A. ◦ Área de un polígono. La implementación del método es completamente diferente en cada subclase de A. ◦ Área de un triángulo. ◦ Área de un rectángulo. ..................................... Para declarar un método como abstracto, se pone delante la palabra reservada abstract y no define un cuerpo: abstract tipo nombreMétodo(....); Luego en cada subclase se define un método con la misma cabecera y distinto cuerpo. 42 Ejemplo 1 43 Ejemplo 1 abstract class GraphicObject { int x, y; ... void moveTo(int newX, int newY) { ... } abstract void draw(); abstract void resize(); } class Circle extends GraphicObject { void draw() { ... } void resize() { ... } } class Rectangle extends GraphicObject { void draw() { ... } void resize() { ... } } 44 Ejemplo de clase abstracta persona -nombre -edad +toString() : String +cambiarEdad() alumno -curso -nivelAcademico +cambiarCurso() +toString() : String +pagoMensual() : double +mostrarAsignaturas() profesor -asignatura +toString() : String libre presencial -listaAsignaturas -precioPorHora -noHorasDiarias -pedirAsignaturas() +pagoMensual() : double +mostrarAsignaturas() -matriculaCurso -noConvocatoria -plusPorConvocatoria +pagoMensual() : double +mostrarAsignaturas() 45 Ejemplo de clase abstracta abstract class Alumno extends Persona { protected int curso; private String nivelAcademico; public Alumno (String n, int e, int c, String nivel) { super(n, e); curso = c; nivelAcademico = nivel; } public String toString() { return super.toString() + curso + nivelAcademico; } abstract double pagoMensual(); abstract String getAsignaturas(); } 46 Ejemplo de clase abstracta class Libre extends Alumno { private String []listaDeAsignaturas; private static float precioPorHora=10; private int noHorasDiarias; private void pedirAsignaturas() {}// se inicializa listaDeAsignaturas public double pagoMensual() { return precioPorHora*noHorasDiarias*30; } public String getAsignaturas() { String asignaturas=""; for (int i=0; i<listaDeAsignaturas.length; i++) asignaturas += listaDeAsignaturas[i] + ' '; return asignaturas; } public Libre(String n, int e, int c, String nivel, int horas) {super(n,e,c,nivel); noHorasDiarias = horas; pedirAsignaturas(); } } 47 Ejemplo de clase abstracta class Presencial extends Alumno { private double matriculaCurso; private double plusPorConvocatoria; private int noConvocatoria; public double pagoMensual() { return (matriculaCurso+plusPorConvocatoria*noConvocatoria)/12; } public String getAsignaturas(){ return “todas las del curso “ + curso; } public Presencial(String n, int e, int c, String nivel, double mc, double pc, int nc) { super(n,e,c,nivel); matriculaCurso=mc; plusPorConvocatoria=pc; noConvocatoria=nc; } } 48 Ejemplo de clase abstracta // FUNCIONES GLOBALES void mostrarAsignaturas(Alumno v[]) { for (int i=0; i<v.length; i++) System.out.println(v[i].getAsignaturas()); // enlace dinámico } double totalPagos(Alumno v[]) { double t=0; for (int i=0; i<v.length; i++) t += v[i].pagoMensual(); // enlace dinámico return t; } 49 Interfaces Podría suceder que los objetos de varias clases compartan la capacidad de ejecutar un cierto conjunto de operaciones. Y dependiendo de la clase de objeto, cada operación se realice de diferente manera. Ejemplo: ◦ Clases: Circulo, Elipse, Triangulo, .... ◦ Todas esas clases incluyen los métodos: área, perimetro, cambiarEscala, etc. Podríamos definir una interfaz común que agrupe todos los métodos comunes (como métodos abstractos). Y luego definir varias clases de modo que implementen una misma interfaz. 50 Ejemplo de Interface public interface Figura { abstract double area(); abstract double perimetro(); } public class Circulo implements Figura { private double radio; private static double PI=3.1416; .............. public double area() { return PI*radio*radio; } public double perimetro() { return 2*PI*radio; } } public class Cuadrado implements Figura { private double lado; .............. public double area() { return lado*lado; } public double perimetro() { return 4*lado; } } 51 Ejemplo de Interface • Una interface puede incluir también definiciones de constantes aparte de métodos abstractos. • Una misma clase puede implementar más de una interface Herencia múltiple de interfaces • Se pueden crear jerarquías de interfaces (con extends!!). • Se pueden declarar referencias a objetos que implementen una cierta interface. double totalArea(Figura v[]) { double t=0; for (int i=0; i<v.length; i++) t += v[i].area(); // enlace dinámico return t; } 52 Clases Abstractas VS Interfaces Las clases abstractas son similares a las interfaces en lo siguiente: ◦ No se pueden instanciar objetos de ellas ◦ Pueden contener una mezcla de métodos abstractos y métodos con implementación. 53 Clases Abstractas VS Interfaces Con las clases abstractas ◦ Es posible declarar atributos que no son static y final ◦ Es posible declarar métodos public, protected, y private. • Con las Interfaces Todos los atributos son automáticamente public, static y final. Todos los métodos son public. Solamente se puede extender una clase, sea o no abstracta. Es posible implementar cualquier número de interfaces. 54 Cuando utilizar clases Abstractas o Interfaces Considera utilizar clases abstractas cuando: ◦ Se desea compartir código entre varias clases estrechamente relacionadas. ◦ Se espera que las clases que extienden de la clase abstracta posean muchos métodos comunes, o requieran del uso de modificadores de acceso diferentes a public (protected y private). ◦ Se necesita declarar atributos non-static y non-final. Esto permite definir métodos que pueden accesar y modificar el estado del objeto al cual pertenecen. 55 Cuando utilizar clases Abstractas o Interfaces Considerar el uso de Interfaces cuando: ◦ Se espera que clases no relacionadas implementen la Interface. ◦ Se necesita especificar el comportamiento de un tipo de dato particular, independientemente de quien implementa su comportamiento. 56 HERENCIA MULTIPLE 57 Herencia Múltiple Reglas básicas para la herencia en Java ◦ No está permitida la herencia múltiple ◦ Si es posible la herencia multinivel, A puede ser heredada por B y C puede heredar de B. ◦ Una clase puede ser heredada por varias clases. Elaborado por Dra. Maricela Bravo 58 Herencia Multiple Clase A Clase C Clase B Clase A Clase B Clase A Clase C Clase B Clase C Elaborado por Dra. Maricela Bravo 59 Herencia Múltiple Cuando se trabaja con clases en Java se está limitado a heredar solamente de una clase base. Las interfaces relajan esta restricción al permitir realizar herencia múltiple mediante la combinación de varias interfaces en una misma clase. Para hacer esto, solo se colocan los nombres de las interfaces después de la palabra reservada implements separadas por comas. 60 Ejemplo 1: extends - implements interface Hello1 // 1st interface { public abstract void display1(); } interface Hello2 // 2nd interface { public abstract void display2(); } class Test1 // concrete (non-abstract) class { public void calculate() { System.out.println("Concrete class Test1 method calculate() executed"); } } 61 Ejemplo 1: extends - implements abstract class Test2 extends Test1 // abstract class extending Test1 { public abstract void show(); } public class Demo extends Test2 implements Hello1, Hello2 { public void display1() { System.out.println("Interface Hello1 method display1() executed"); } public void display2() { System.out.println("Interface Hello2 method display2() executed"); } public void show() { System.out.println("Abstract class Test2 method show() executed"); } public static void main(String args[]) { Demo d1 = new Demo(); d1.display1(); d1.display2(); d1.calculate(); d1.show(); } } 62 Ejemplo 2: extends - implements interface Forward { void drive(); } interface Stop { void park(); } interface Speed { void turbo(); } class GearBox { public void move() { } } class Automatic extends GearBox implements Forward, Stop, Speed { public void drive() { System.out.println("drive()"); } public void park() { System.out.println("park()");} public void turbo() { System.out.println("turbo()"); } public void move() { System.out.println("move()"); } } 63 Ejemplo 2: extends - implements public class Car { public static void cruise(Forward x) { x.drive(); } public static void park(Stop x) { x.park(); } public static void race(Speed x) { x.turbo(); } public static void move(GearBox x) { x.move(); } public static void main(String[] args) { Automatic auto = new Automatic(); cruise(auto); // Interface Forward park(auto); // Interface Stop race(auto); // Interface Speed move(auto); // class GearBox } } 64 Ejemplo 3: Super Heores Clases abstractas Interfaces 65 Ejemeplo 3: extends - implements interface SuperHero { void powers(); } interface Alien { void planet(); } interface SuperMan extends Alien, SuperHero { void xRayVision(); } interface Human { void normal(); } interface Monster extends Alien { void killHuman(); } 66 Ejemplo 3: extends - implements class Skrull implements Monster { void shapeShift() {} public void killHuman() {} public void planet() {} } class ClarkKent implements SuperMan, Human { public void normal() {} public void planet() {} public void xRayVision() {} public void powers() {} } class Hulk implements Human, SuperHero { void thunderClap() {}; public void normal() {} public void powers() {} } 67 Ejemplo 3: extends - implements public class Earth { static Alien invasion(Monster mt) { return mt; } static SuperMan change(ClarkKent ck) { return ck; } static void battle(SuperHero sh, Monster m) { sh.powers(); m.planet(); m.killHuman(); } public static void main(String[] args) { Skrull ogre = new Skrull(); invasion(ogre); ClarkKent christopherReeve = new ClarkKent(); SuperMan superMan = change(christopherReeve); battle(superMan, ogre); battle(new Hulk(), ogre); } } 68 Tarea Investigar para qué se utilizan las siguientes interfaces en J2SE 1. 2. 3. 4. 5. java.lang.Runnable java.util.Enumeration java.awt.event.WindowListener java.sql.Connection java.io.Serializable Elaborado por Dra. Maricela Bravo 69 Manejo de una excepción Supongamos que en una función f de Java se detecta una situación anómala, entonces: Se ejecuta: throw new NombreExcepción([msg]); Para que pueda ser capturada: try {// bloque try f(...); // u otra func que llame a f. } 70 Manejo de una excepción El tratamiento de la excepción se especifica mediante una sentencia catch: try { f(...) // fuente de la excepción ........ } catch (tipo_excepcion1 parámetro1) { “tratamiento de la excepción1” } catch (tipo_excepcion2 parámetro2) { ................................ } catch (tipo_excepcionN parámetroN) { “tratamiento de la excepciónN” } [ finally { “bloque que se ejecuta siempre” }] 71 Creación de clases de Excepciones Se debe definir una nueva clase subclase de la clase Exception. • class MiExcepcion extends Exception { public MiException() { } // si se quiere mostrar un cierto mensaje // se debe definir este segundo constructor public MiException(String msg) { super(msg); } } 72 Jerarquía de Excepciones 73 Igualdad y Asignación entre objetos El operador de asignación no sirve para crear una copia de un objeto. ¿Cómo crear una copia a nivel de bits? ◦ Solución: utilizar el método clone(). ◦ Para poder utilizarlo con los objetos de una clase A, la clase A debe implementar la interfaz Cloneable y se debe incluir el siguiente método clone() en la clase A: public Object clone(){ Object obj=null; try{ obj=super.clone(); }catch(CloneNotSupportedException ex){ System.out.println(" no se puede duplicar"); } return obj; } 74 Ejemplo con clone() class Date implements Cloneable { .................... public Object clone(){ Object obj=null; try{ obj=super.clone(); }catch(CloneNotSupportedException ex){ System.out.println(" no se puede duplicar"); } return obj; } public static void main(String[] args) { Date ob1, ob2; ob1 = new Date(12, 4, 96); ob2 = (Date) ob1.clone(); // ob2 es una copia de ob1 // las alias de ob1 y ob2 son diferentes System.out.println(ob1 == ob2); // el contenido de ob1 y ob2 es el mismo System.out.println(ob1.equals(ob2)); } } 75 Problemas con el clone() Si se quiere hacer una copia de un objeto que contiene otros objetos, no sirve el clone() que proporciona Java. ◦ Ejemplo: clase Persona con un atributo fecha de nacimiento. ◦ Solución: redefinir el método clone() para la clase Persona de modo que haga una copia del objeto fecha de nacimiento. 76 Ejemplo con clone() redefinido public class Persona implements Cloneable { private String nombre; private Date fechaNacimiento; public Persona(String nombre, Date fechaNacimiento){ this.nombre = nombre; this.fechaNacimiento = fechaNacimiento; } public Object clone(){ return (new Persona(nombre, (Date) (fechaNacimiento.clone()))); } public static void main(String[] args) { Persona p1, p2; p1 = new Persona("Pepe", new Date(1,1,2006)); p2 = (Persona) p1.clone(); } } 77