advertisement

Oracle certified professional java se 6 programmer 2

54 %
46 %
advertisement
Information about Oracle certified professional java se 6 programmer 2
Education

Published on March 1, 2014

Author: PabloGaleanaBailey

Source: slideshare.net

Description

SCJP , Curso SCJP
advertisement

México Distrito Federal a 28 de Febrero de 2014 Autor: Pablo Galeana Bailey Cualquier duda y/o comentario quedo a sus órdenes para alguna asesoría o recomendación para mejorar el presente material. Email: knoppixpgb@gmail.com 1. Objetivo de Certificación 5.1 Encapsulación Desarrollar código que implemente una fuerte encapsulación, un bajo acoplamiento y una alta cohesión en clases, describiendo posteriormente los beneficios. El desarrollador debe escribir las clases y el código de forma que soporte flexibilidad y mantenibilidad. Como ejemplo, una clase con las variables de instancia públicas, y el resto de programadores están modificando las variables directamente, tal y como se muestra a continuación:

Lo ideal sería crear un método setSize(int a) para controlar los valores y modificar la visibilidad de la variable para hacerla privada. Sin embargo, esto hará que todo el código que hace uso de esta clase genere errores. La capacidad de hacer cambios en nuestro código de implementación sin romper el resto que use el código es un beneficio clave de la encapsulación. Lo ideal es ocultar los detalles de implementación tras una interfaz de programación pública. Por interfaz se entiende el conjunto de métodos accesibles que nuestro código pone a disponibilidad pública - o lo que es lo mismo, la API de nuestro código. Ocultando los detalles de implementación, podemos modificar el código sin necesidad de modificar el código que llama a nuestros métodos. Si queremos mantenibilidad, flexibilidad y extensabilidad nuestro diseño debe incluir encapsulación:      Mantener las variables de instancia protegidas (con un modificador de acceso, normalmente privado). Hacer métodos de acceso público y forzar al código para que use dichos métodos en lugar de que accedan directamente a la variable de instancia. Restringir el acceso a obtener el valor que lleva el atributo Si no ponemos el método Get, solo se podrá acceder desde la propia clase a este atributo. Restringir el acceso a modificar el valor que lleva el atributo Si no ponemos el método set, solo se podrá acceder desde la propia clase a este atributo. Para los métodos, utilizar la convención de nombrado JavaBeans (set<Propiedad> y get<Propiedad>). A estos métodos se les conoce como getters y setters (algunas veces como accesors y mutators). Implementación del ejemplo anterior pero con métodos setters y getters:

El código anterior no realiza ninguna validación o procesamiento por lo que podría pensarse que no es útil. Sin embargo, podríamos cambiar en un futuro dicha implementación sin necesidad de cambiar la signatura del método y, por tanto, sin romper nuestra API. Con esto obtenemos las ventajas de un buen diseño OO. Debemos siempre forzar al código llamante a que acceda a las variables a través de métodos públicos. 2. Objetivo de Certificación 5.5 - Herencia, "Es-Un" (Is-A) y "Tiene-Un" (HasA) Desarrollar código que implemente relaciones "Is-A" o/y "Has-A" La instrucción instanceof devuelve 'true' si la variable que estamos probando es del mismo tipo del que comparamos. El siguiente código: Produce la siguiente salida: they'renotequal t1's anObject Vemos dos aspectos claves:  t1 no es igual a t2, aunque no está el método equals() (¿o sí está?).  t1 es una instancia de la clase Object (¿no era de la clase Test?). Esto se debe a que toda clase en Java es una subclase de Object. Siempre que creamos una clase, dicha clase heredará de Object y por tanto heredará sus métodos (equals, clone, notify, wait y otros disponibles).

Para el examen, necesitaremos saber que podemos crear relaciones de herencia en Java extendiendo una clase. Es también importante comprender que las dos razones más comunes para usar herencia son: 1.- Promocionar la reutilización de código:Un diseño común de aproximación es crear una versión genérica de una clase con la intención de crear subclases más especializadas que hereden de ella. Por ejemplo: Genera la siguiente salida: displayingshape movinggamepiece Aquí, PlayinPiece hereda el método genérico display() de una clase menos especializada (GameShape) y además añade su propio método, movePiece(). La reutilización de código a través de la herencia se utiliza para métodos con funcionalidad genérica que podrían aplicarse a un amplio rango de subclases, por lo que no sería necesario reimplementarlos en cada objeto. 2.-Usar polimorfismo: el uso de herencia es para permitir a nuestras clases ser accedidas de forma polimórfica (capacidad también proporcionada por las interfaces). Por ejemplo, imaginemos que tenemos una clase GameLauncher que quiere recorrer una lista de distintos objetos GameShape e invocar el método

display() en cada uno de ellos. En el momento de escribir la clase no sabemos qué tipos de figuras tendremos y además no querremos tener que cambiar el código si se añade una nueva figura. El polimorfismo permite tratar cualquier subclase de GameShape como un objeto GameShape. Imaginemos que ahora tenemos dos subclases que extienden de la clase GameShape: Ahora imagina una clase de prueba que tiene un método con un argumento de tipo GameShape, lo que significa que puede tomar cualquier tipo de GameShape. El siguiente código: Generaría la siguiente salida:

displayingshape displayingshape El punto clave es que el método doShapes() está declarado con un argumento GameShape por lo que se le puede pasar cualquier subtipo de GameShape. El método puede invocar cualquier método de GameShape, sin preocuparse del tipo de clase en ejecución que ha sido pasado al objeto. Sin embargo, usar una variable de referencia declarada de tipo GameShape (ya sea un parámetro de método, una variable local o una variable de instancia) significa que sólo podremos invocar los métodos de GameShape. Los métodos que podemos llamar en una referencia son totalmente dependientes del tipo de variable declarada, no importa qué tipo de objeto actual esté apuntando la referencia. 2.1. Relaciones Is-A y Has-A IS-A En OO, el concepto de IS-A está basado en la herencia de clase o implementación de interfaz. IS-A es una forma de decir, "A es un tipo de B". Por ejemplo, un Subaru es un tipo de Coche, por lo que en términos de OO podríamos decir, "Subaru IS-A Car". Podemos expresar esta relación en Java mediante las palabras reservadas 'extends' (para herencia de clase) e 'implements' (para implementación de interfaz). Un Coche es un tipo de Vehículo, por lo que el árbol de herencia podría empezar por la clase Vehicle, tal y como se muestra a continuación:

En términos de OO podríamos decir:           Vehicle es la superclase de Car. Car es la subclase deVehicle. Car es la superclase de Subaru. Subaru es la subclase de Vehicle. Car hereda de Vehicle. Subaru hereda de Vehicle y Car. Subaru deriva de Car. Car deriva de Vehicle. Subaru deriva de Vehicle. Subaru es un subtipo de Vehicle y Car. Volviendo a nuestra relación IS-A, las siguientes sentencias son ciertas:  "Car extends Vehicle" significa"Car IS-A Vehicle."  "Subaru extends Car" significa "Subaru IS-A Car." También podemos decir: "Subaru IS-A Vehicle" puesto que se dice que es "un tipo de cualquier clase superior de su árbol de herencia". Si la expresión Fooinstanceof Bar es cierta, entonces Foo IS-A Bar, aún cuando Foo no extienda directamente a Bar sino que extiende una clase que es subclase de Bar. Ejemplo: Modelar por medio de la herencia el siguiente diagrama de clases:

HAS-A Las relaciones HAS-A están basadas en el uso. En otras palabras, la clase A HAS-A B si en código en la clase A tiene una referencias a una instancia de la clase B. Por ejemplo: "Un Horse IS-A Animal. Un Horse HAS-A Halter (rienda)". El código podría ser el siguiente: En el código anterior, la clase Horse tiene una variable de instancia de tipo Halter, por lo que podremos decir "Horse HAS-A Halter". Los usuarios de la clase Horse (los que hacen llamadas a dicha clase) piensan que la clase Horse tiene el comportamiento definido por Halter. La clase Horse podría tener un método tie(LeadRoperope) (atar las riendas). Los usuarios no tiene que saber que cuando invoquen el método tie(), el objeto Horse delegue la llamada a su clase Halter. Veamos esto en código:

En OO, nosotros no queremos que los usuarios se preocupen sobre qué clase o qué objeto está haciendo el trabajo. 3. Objetivo de Certificación 5.2 – Polimorfismo Dado un escenario, desarrollar código que demuestre el uso de polimorfismo. Es más, determinar cualdo será necesario un casting y reconocer errores de compilación vs ejecución relacionados con el casting de referencias de objetos Cualquier objeto Java que pueda pasar más de una prueba IS-A puede ser considerado polimórfico. Todos los objetos Java son polimórficos puesto que pasan la prueba IS-A para su propio tipo y para la clase Object. Recordar unas cuantas cosas sobre las referencias:      Una variable de referencia sólo puede ser de un tipo y, una vez declarada, no puede cambiarse el tipo (aunque cambie el objeto al que referencie). Una referencia es una variable, por lo que puede ser reasignada a otros objetos (a no ser que la referencia se declare como 'final'). Un tipo de la variable de referencia determina los métodos que podrán invocarse en el objeto que está referenciando la variable. Una variable de referencia puede apuntar a cualquier objeto del mismo tipo que la referencia declarada o -esto es lo más importante- puede apuntar a cualquier subtipo del tipo declarado. Una variable de referencia puede ser declarada como un tipo de clase o un tipo de interfaz. Si la variable es declarada como un tipo de interfaz, puede referenciar a cualquier objeto de cualquier clase que implemente la interfaz. Antes creamos una clase GameShape que fue extendida por otras dos clases, PlayerPiece y TilePiece. Ahora imaginemos que queremos animar alguna de las figuras en el tablero de juego. Pero no todas las formas pueden ser animadas, por lo que ¿cómo podríamos hacer esto con herencia de clase?. La solución correcta sería crear una interfaz Animatable y hacer que sólo las subclases de Gameshape que puedan ser animadas implementen dicha interfaz. A continuación se muestra dicha interfaz: Y a continuación se muestra la clase modificada PlayerPiece que implementa dicha interfaz:

Ahora tenemos un PlayerPiece que pasa la prueba IS-A para la clase Gameshape y para la interfaz Animatable. Esto significa que PlayerPiece puede ser tratado de forma polimórfica como uno de los cuatro tipos en cualquier momento, en función del tipo declarado en la variable de referencia:     Un objeto (puesto que cualquier objeto hereda de Object) Un GameShape (puesto que PlayerPiece extiende a GameShape) Un PlayerPiece (es realmente lo que es) Un Animatable (puesto que PlayerPiece implementa Animatable) Las siguientes declaraciones son todas legales. Sólo tenemos un objeto una instancia del tipo PlayerPiece- pero hay cuatro tipos distintos de variables de referencia, todas apuntando a un objeto.Puesto que esta definición depende de una comprensión clara de la sobrescritura (override) y la distinción entre métodos estáticos y métodos de instancia. Ejemplo: Modelar por medio del polimorfismo el siguiente diagrama de clases:

El siguiente diagrama de clases representa la jerarquía de herencia de las clases asociadas a los tipos primitivos en Java 4. Objetivos de Certificación 1.5 y 5.4 - Sobrescritura/Sobrecarga 1.5 - Dado un código de ejemplo, determinar si un método está sobrescribiendo o sobrecargando correctamente otro método e identificar los valores de retorno legales (incluyendo retornos covariantes) para el método. 4.1. Métodos Sobrescritos Cuando tenemos una clase que hereda un método de una superclase, tenemos la oportunidad de sobrescribir el método (a no ser que esté marcado como 'final'). El

principal beneficio de la sobrescritura es la capacidad de definir comportamiento que es específico a un tipo de subclase particular.Ejemplo la clase Horse, sublcase de Animal, sobrescribiendo su método eat(): Si heredamos métodos abstractos no tenemos otra opción,debemos implementar el método en la subclase a no ser que la subclase también sea abstracta. Los métodos abstractos deben ser implementados por la subclase concreta, aunque esto es como decir que la subclase concreta sobrescriba los métodos abstractos de la superclase. Por lo que podemos decir que los métodos abstractos son métodos que estamos forzados a sobrescribir. "No existe forma alguna de usar un método eat() genérico, sino que tenemos que crear nuestra propia implementación del método". Un (no abstracto) ejemplo del uso de polimorfismo podría ser el siguiente: En el código anterior, la clase de prueba usa una referencia Animal para invocar un método del objeto Horse. Recuerda que el compilador sólo permitirá que se invoquen métodos de la clase Animal cuando usamos una referencia de este tipo. El siguiente código no sería legal si tenemos en cuenta el código anterior:

El método que sobrescribe no puede tener un modificador de acceso más restrictivo que el método que está sobrescribiendo. Modifiquemos el ejemplo polimórfico que vimos anteriormente: Si el código compilara (lo cual no es correcto) lo siguiente produciría un fallo en tiempo de ejecución: La variable 'b' es de tipo Animal, el cual tiene un método público eat(). Pero recuerda que en tiempo de ejecución, Java utiliza la invocación virtual de métodos para seleccionar de forma dinámica la versión actual del método que va a ejecutar, basándose en la instancia actual. Una referencia Animal siempre puede apuntar a una instancia Horse, puesto que HorseIS-A Animal. Lo que significa que la superclase apunte a una instancia de una subclase es que se garantiza que la subclase es capaz de hacer todo lo que puede hacer la superclase. Si la instancia Horse sobrescribe los métodos heredades de Animal o simplemente los hereda, cualquiera con una referencia a una instancia Horse es libre de llamar a todos los métodos accesibles de Animal. Por dicha razón, un método que sobrescribe debe cumplir el contrato de la superclase. Las reglas para sobrescribir un método son las siguientes:  La lista de argumentos debe ser exactamente la misma que la del método sobrescrito. Si no encajan, tendremos un método sobrecargado.

          El tipo devuelto debe ser el mismo, o un subtipo, del tipo de retorno declarado en el método original de la superclase. El nivel de acceso no puede ser más restrictivo que el del método sobrescrito. El nivel de acceso puede ser menos restrictivo que el del método sobrescrito. Los métodos de la instancia pueden ser sobrescritos sólo si son heredados de la subclase. Una subclase en el mismo paquete que la superclase de la instancia pueden sobrescribir cualquier método de la superclase que no esté marcado como private o final. Una subclase en un diferente paquete sólo puede sobrescribir los métodos no-finales marcados como public o protected (puesto que los métodos protected son heredados por la subclase). El método que sobrescribe puede lanzar cualquier excepción unchecked, sin importar si el método sobrescrito declara la excepción. El método que sobrescribe no debe lanzar excepciones checked que son nuevas o más amplias que las declaradas por el método sobrescrito. Por ejemplo, un método que declara una FileNotFoundException no puede ser sobrescrito por un método que declare una SQLException, Exception o cualquier otra excepción no-runtime a no ser que sea subclase de FileNotFoundException. El método que sobrescribe puede lanzar excepciones más específicas o cerradas. Esto es porque que un método sobrescrito "tome riesgos" no significa que la excepción de la subclase tome los mismos riesgos. En definitiva: un método que sobrescribe no tiene que declarar ninguna excepción que nunca vaya a lanzar, sin importar lo que el método sobrescrito declare. No podemos sobrescribir un método marcado como final. No podemos sobrescribir un método marcado como static. Más adelante se verán en más detalle los métodos estáticos. Si un método no puede ser heredado, no podemos sobrescribirlo. Recuerda que sobrescribir implica que estamos re-implementando un método que hemos heredado. Por ejemplo, el siguiente código no es legal e incluso si añadimos el método eat() a Horse, no sería una sobrescritura del método eat() de Animal.  Invocando una Versión de Superclase de un Método Sobrescrito Algunas veces, necesitaremos utilizar algún código de un método de la

superclase, manteniendo la sobrescritura de dicho método para proporcionar algún comportamiento adicional y específico. Esto se hace mediante la palabra reservada 'super' tal y como se muestra a continuación: Nota: Usar 'super' para invocar a un método sobrescrito sólo se aplica a los métodos de la instancia. (Los métodos estáticos no pueden sobrescribirse.) Ejemplos de Sobrescritura de Métodos Veamos cómo sobrescribir el método eat() de Animal: Legales e Ilegales La Tabla 2.1 muestra ejemplos de sobrescrituras ilegales del métdoeat() de Animal dada la definición anterior de la clase. Código Ilegal de Problema con el Código Sobrescritura El modificador de acceso es más privatevoideat() { } restrictivo public void eat() throws Declara una excepción checked no IOException { } declarada por la superclase Sobrecarga legal, no sobrescritura (la public void eat(String food) { } lista de argumentos ha cambiado) No es sobrescritura (cambia tipo de publicStringeat() { } retorno) ni sobrecarga (los argumentos no han cambiado)

4.2. Métodos Sobrecargados Los métodos sobrecargados nos permiten reutilizar el mismo nombre de método en una clase, pero con argumentos diferentes (y opcionalmente un tipo de retorno diferente).Las reglas son simples:      Los métodos sobrecargados DEBEN cambiar la lista de argumentos Los métodos sobrecargados PUEDEN cambiar el tipo de retorno Los métodos sobrecargados PUEDEN cambiar el modificador de acceso Los métodos sobrecargados PUEDEN declarar excepciones checked nuevas o más genéricas Un método puede ser sobrecargado en la misma clase o en una subclase. En otras palabras, si la clase A define el método doStuff(int i), la subclase B podría definir un método doStuff(String s) sin sobrescribir la versión de la superclase. Los dos métodos, con el mismo nombre pero en diferentes clases, siguen considerándose sobrecargados si la subclase hereda una versión del método y declara otra versión sobrecargada en su definición de clase. Sobrecargas Legales Veamos el método que queremos sobrecargar: Los siguientes métodos son sobrecargas legales del método: Invocando Métodos SobrecargadosCuando se invoca un método, podría existir más de un método con el mismo nombre para el tipo de objeto que estamos usando para invocar el método. Para decidir cuál de los métodos invocar nos basamos en los argumentos. Si no existe un método que encaje con los argumentos pasados el compilador se quejará porque no puede encontrarlo. Los siguientes son ejemplos de invocaciones de métodos sobrecargados:

En el código de TestAdder anterior, la primera llamada a a.addThem(b,c) pasa dos enteros al método, por lo que se llama a la primera versión (la versión sobrecargada que toma dos argumentos enteros). La segunda llamada a a.addThem(22.5, 9.3) pasa dos 'doubles' al método, por lo que se llama a la segunda versión. Invocar métodos sobrecargados que toman referencias a objetos en lugar de valores primitivos es más interesante. Digamos que tenemos un método sobrecargado con una versión que toma un Animal y otra que toma un Horse(subclase de Animal). Si pasamos un objeto Horse en la invocación del método, invocaremos la versión sobrecargada que toma un Horse. O esto es lo que parece a primera vista:

La salida es lo que esperábamos: in the Animal version in the Horse version Pero, ¿que pasa si usamos una referencia Animal que apunte aun objeto Horse? El código anterior imprimiría:in the Animal versión Aun cuando el objeto actual en ejecución es un Horse y no un Animal, la elección de cuál método sobrecargado se va a llamar NO se decide dinámicamente en ejecución. Simplemente hay que recordar que el tipo de referencia (no el tipo de objeto) es el que decide que método sobrecargado va a invocarse. En resumen, la versión sobrecargada del método que se va a llamar (de qué clase en el árbol de herencia) se decide en tiempo de ejecución basándose en el tipo de objeto, pero qué versión sobrecargada del método se va a llamar se basa en el tipo de referencia del argumento pasado en tiempo de compilación. Si invocamos un método pasándole una referencia de Animal que apunta a un objeto Horse, el compilador sólo sabe del Animal, por lo que elige la versión sobrecargada que toma un Animal. No se preocupa de que en ejecución se esté pasando un Horse.

4.3. Polimorfismo en Métodos Sobrescritos y Sobrecargados El polimorfismo no se preocupa si un método está sobrecargado. Si pasamos una referencia de tipo Animal, el método sobrecargado que toma un Animal es el que se invoca, aunque el objeto actual sea un Horse. Por lo tanto, el polimorfismo no determina qué versión sobrecargada se invoca. El polimorfismo entra en juego cuando la decisión es qué versión sobrescrita de un método se va a llamar. No obstante, a veces, un método está sobrecargado y sobrescrito. Imaginemos que las clases Animal y Horse son las siguientes: Observa que la clase Horse tiene el método eat() sobrecargado y sobrescrito. La siguiente tabla muestra qué versión de los tres métodos eat() se invocará en función de cómo son invocados. MethodInvocationCode Result Animal a = new Animal(); Generic Animal EatingGenerically a.eat(); Horse h = new Horse(); Horseeating hay h.eat(); Horse eating hay. Polymorphism works—the actual Animal ah = new Horse (); object type (Horse), not the reference type (Animal), ah.eat(); is used to determine which eat() is called. Horse he = new Horse(); Horse eating Apples. The overloaded eat(String s) he.eat("Apples") ; method is invoked. Animal a2 = new Animal(); Compier error! Compiler sees that Animal class a2.eat ("treats"); doesn't have an eat() method that takes a String. Compiler error! Compiler still looks only at the reference, and sees that Animal doesn't have an Animal ah2 = new Horse(); eat() method that takes a String. Compiler doesn't ah2.eat("Carrots"); care that the actual object might be a Horse at runtime. Método Sobrecargado Argumento(s) Debe cambiar Tipo Retorno de Puede cambiar Excepciones Puede cambiar Acceso Puede cambiar Método Sobrescrito No debe cambiar No puede cambiar excepto para retornos covariantes Puede ser más específica. No debe lanzar excepciones checked nuevas o más genéricas No puede hacerse más

restrictivo menos). Invocación El tipo de referencia determina qué versión sobrecargada se selecciona (basándose en los tipos de argumento). Ocurre en tiempo de compilación. El método actual que es invocado es una invocación virtual de método que ocurre en tiempo de ejecución, pero el compilador conocerá la signatura del método. (pero sí El tipo de objeto (el tipo de la instancia actual en pila) determina qué método se selecciona. Ocurre en tiempo de ejecución. 5. Objetivo de Certificación 5.2 - Casting a Variables de Referencia Dado un escenario, desarrollar código que demuestra el uso del polimorfismo. Es más, determinar cuándo es necesario hacer un casting y reconocer errores de compilador y de tiempo de ejecución relacionados con el casting a referencias de objetos. Hemos visto cómo es posible y común usar tipos de variables de referencia genéricos para referirnos a tipos de objetos más específicos. Por ejemplo, la siguiente línea: Pero qué ocurre cuanto queremos usar una variable de referencia Animal para invocar un método que sólo tiene la clase Dog. Sabemos que la variable apunta a un Dog y queremos hacer algo específico de Dog. En el siguiente código, tenemos un array de Animal y siempre que encontremos un Dog en el array, querremos hacer algo específico de dicha clase. Por ahora haremos como si todo el código estuviera correcto, excepto en la línea del código que invoca el método playDead.

Cuando intentamos compilar este código, el compilador dice algo parecido a lo siguiente: cannotfind symbol El compilador quiere decir, "Hey, la clase Animal no tiene el método playDead()". Modifiquemos el bloque de código if: El nuevo y mejorado bloque de código contiene un casting, que en este caso a veces es llamado downcast puesto que está haciendo un casting bajando sobre el árbol de herencia a una clase más específica. Ahora el compilador no pone problemas. Le estamos diciendo "Sabemos que estamos apuntando a un objeto Dog, por lo que es correcto hacer una nueva variable de referencia Dog para apuntar a dicho objeto". En este caso estamos seguros puesto que antes de hacer el casting hacemos una prueba con instanceof para estar seguros. Es importante conocer que el compilador está forzado a confiar en nosotros cuando hacemos un downcast, incluso cuando nos equivocamos:

El código anterior compila pero cuando intentamos ejecutarlo obtenemos una excepción como la siguiente: java.lang.ClassCastException Todo lo que puede verificar el compilador es que los dos tipos están en el mismo árbol de herencia. El compilador debe permitir cosas que es posible que no funcionen en tiempo de ejecución. Sin embargo, si el compilador sabe a ciencia cierta que el casting podría no funcionar, la compilación fallará. El siguiente bloque de código NO compilará: En este caso, obtendremos un error parecido al siguiente: inconvertibletypes Al contrario que el downcasting, el upcasting (casting por encima del árbol de herencia hacia un tipo más general) trabaja de forma implícita (no es necesario hacerlo) puesto que cuando hacemos un upcasting estamos restringiendo implícitamente el número de métodos que podemos invocar, al contrario que downdcasting que implica que más tarde podríamos querer invocar un método más específico. Porejemplo: Ambos upcasting compilarán y se ejecutarán sin lanzar excepciones, puesto que Dos IS-A Animal. Todo lo que puede hacer un Animal puede hacerlo un Dog.

El compilador y la JVM lo saben, por lo que el upcasting implícito siempre es legal. Si Dog implementa Pet y Pet define el método beFriendly(), entonces a un Dog le podemos hacer un casting implícito a Pet, pero el único método que podremos invocar es beFriendly(), que Dog se ha visto obligado a definir al implementar la interfaz Pet. Si Dog implementa Pet y Beagle extiende a Dog pero no declara que implementa a Pet, Beagle sigue siendo un Pet. La clase Beagle puede sobrescribir cualquier método que hereda de Dog, incluyendo métodos que Dog ha implementado para cumplir con el contrato de la interfaz Pet. Por último, si Beagle no declara que implementa Pet, al observar la API de Beagle puede verse claramente que Beagle IS-APet, sin tener que buscar entre las superclases de Beagle. De forma resumida, si Beagle IS-A Dog y Dog IS-A Pet, entonces Beagle IS-A Pet. Por tanto, no podemos dejarnos engañar por un código que muestre una clase concreta que declara que implementa a una interfaz pero que no implementa los métodos de la interfaz. Antes de decir si el código es legal, debemos conocer si las superclases de esta clase están declaradas. Si alguna clase en su árbol de herencia proporciona implementaciones concretas (no abstractas) del método y han declarado que ella (la superclase) implementa la interfaz, entonces la subclase no está en la obligación de re-implementar (sobrescribir) dichos métodos. 6. Objetivo de Certificación 1.2 - Implementando una Interfaz Desarrollar código que declare una interfaz Cuando implementamos una interfaz, estamos de acuerdo en cumplir con el contrato definido en la interfaz. Esto significa que vamos a proporcionar implementaciones legales para cada método definido y que cualquiera que conozca los métodos de la interfaz (no como se implementan sino coomo pueden llamarse y qué devuelven) pueden invocar dichos métodos en una instancia de nuestra clase implementadora. Java comprueba esto mediante una comprobación del compilador en todas las clases que implementen una interfaz. Si la clase especifica que implementa una interfaz, el compilador comprobará que tiene una implementación correcta por cada método en la interfaz (con algunas excepciones que se verán a continuación). Asumamos una interfaz, Bounceable , con setBounceFactor(). La siguienteclasecompilará: dos métodos: bounce() y

Evidentemente, esta implementación es de las peores que existen, sin embargo compila y se ejecuta. El contrato de la interfaz garantiza que la clase tendrá el método pero nunca garantiza una buena implementación ni siquiera código de implementación en el cuerpo del método. Las clases implementadoras deben seguir las mismas reglas que la implementación de un método en una clase que extiende a otra clase abstracta. Para que una clase implementadora sea legal, una clase noabstracta debe hacer lo siguiente:     Proporcionar implementaciones concretas (no abstractas) para todos los métodos declarados en la interfaz. Seguir todas las reglas para sobrescrituras legales. Declarar excepciones no checked en los métodos de implementación distintas a las declaradas por el método de la interfaz, o excepciones subclases de las declaradas por el método de la interfaz. Mantener la signatura del método de la interfaz y el mismo tipo de retorno (o un subtipo). Aunque no tiene que declarar las excepciones declaradas en el método de la interfaz. Además, una clase implementadora puede ser abstracta. Por ejemplo, lo siguiente es legal para una clase Ball que implementa Bounceable: Esta clase no proporciona la implementación de los métodos pero es correcta. Si la clase es abstracta, puede simplemente pasar la tarea a su primera subclase concreta. Por ejemplo, si la clase BeachBall extiende a Ball y BeachBall no es abstracta, entonces deberá proporcionar implementaciones para todos los métodos de Bounceable: Puede haber clases que implementen interfaz pero no proporcionen implementaciones correctas. Sin embargo, a no ser que la clase sea abstracta, la clase implementadora debe proporcionar implementaciones para todos los métodos definidos en la interfaz. Dos reglas más que debemos saber antes de aparcar este tema:

1. Una clase puede implementar más de una interfaz. Es legal, por ejemplo, lo siguiente: Nosotros sólo podemos extender una clase, pero implementar muchas interfaces. Recuerda que al ser subclase definimos quién y qué somos, mientras que implementar una interfaz define el papel que podemos jugar, a pesar de lo diferentes que podrían ser otras clases que implementan la misma interfaz (pero de diferente árbol de herencia). Por ejemplo, una clase Person extiende a HumanBeing (aunque para algunos sea discutible). Pero Person podría implementar Programmer, Snowboarder, Employee, Parent o PersonCrazyEnoughToTakeThisExam. 2. Una interfaz puede extender otra interfaz pero nunca implementar ninguna. El siguiente código es perfectamente legal: Esto significa que: la primera clase implementadora concreta (no-abstracta) de Bounceable debe implementar todos los métodos de Bounceable más todos los de Moveable. La subinterfaz, como la llamaremos, simplemente añade más requisitos al contrato de la superinterfaz. Para las clases, lo siguiente es ilegal: public class Programmer extends Employee, Geek { } // Illegal ! Como hemos mencionado antes, una clase no puede extender múltiples clases en Java. Sin embargo, una interfaz es libre de extender múltiples interfaces.

En el siguiente ejemplo, Ball debe implementar Bounceable, mas todos los métodos de la interfaz que extiende Bounceable (incluyendo cualquier interfaz que dicha interfaz extienda y así hasta alcanzar el tope de la pila). Por lo que Ball tendría que tener el siguiente aspecto: Si la clase Ball no implementa alguno de los métodos de Bounceable, Moveable o Spherical, el compilador se quejará hasta que lo haga. A no ser que la clase Ball sea marcada como abstracta. En este caso, Ball podría elegir implementar alguno, todos o ninguno de los métodos de cualquiera de las interfaces y así dejar el resto de las implementaciones a una subclase concreta de Ball, tal y como se muestra a continuación: 7. Objetivo de Certificación 1.5 - Tipos de Retorno Legales Dado un código de ejemplo, determinar si un método está sobrescribiendo o sobrecargando correctamente otorméotodo e identificar valores de retorno legales (incluyendo retornos covariantes ) para el método. 7.1. Declaraciones de Tipos de Retorno Tipos de Retorno en Métodos SobrecargadosRecuerda que la sobrecarga de métodos no es más que reutilizar el nombre. El método sobrecargado es un método diferente de cualquier otro método con el mismo nombre. Por lo que si

heredamos un método y lo sobrecargamos en una subclase, no estamos sujetos a las restricciones de sobrescribir, lo que significa que podemos declarar cualquier tipo de retorno que queramos. Lo que no podemos hacer es cambiar únicamente el tipo de retorno. Recuerda que para sobrecargar un método debemos cambiar la lista de argumentos. El siguiente código muestra un método sobrecargado: La versión Bar usa un tipo de retorno diferente. Esto es correcto puesto que hemos cambiado la lista de argumentos. Lo siguiente NO está permitido: Sobrescritura y Tipos de Retorno y Retornos Covariantes. Cuando una subclase quiere cambiar la implementación de un método heredado (sobrescritura), la subclase debe definir un método que encaje de forma exacta con la versión heredada. O, a partir de Java 5, podemos cambiar el tipo de retorno en el método que sobrescribe por un subtipo del tipo de retorno declarado en el método sobrescrito (el de la superclase).Veamos un retorno covariante en acción:

A partir de Java 5, este código compilará. Si intentamos compilar este código con un compilador 1.4 o con la bandera 'source' como sigue: javac -source 1.4 Beta.java Obtendremos un error de compilador como el siguiente: attempting to use incompatible return type. Para el examen, debemos estar seguros de que los métodos sobrecargados pueden cambiar el tipo de retorno, pero al sobrescribir métodos sólo lo podemos hacer siguiendo los retornos covariantes. Sólo este conocimiento nos ayudará en muchas preguntas de examen. 7.2. Devolver un Valor Sólo tenemos que recordar seis reglas al devolver un valor: 1. Podemos devolver null en un método con una referencia a un objeto como tipo de retorno. 2. Un array es un tipo de retorno perfectamente legal. 3. En un método con un tipo de retorno primitivo, podemos devolver cualquier valor o variable que pueda ser implícitamente convertida al tipo de retorno declarado. 4. En un método con un tipo de retorno primitivo, podemos devolver cualquier valor o variable al que le podamos hacer explícitamente un casting al tipo de retorno declarado: 5. No podemos devolver nada desde un método con un tipo de retorno 'void'.

6. En un método con un tipo de retorno 'referencia a objeto', podemos devolver cualquier tipo de objeto al que se le pueda hacer implícitamente un casting al tipo de retorno declarado. 8. Objetivos de Certificación 1.6 y 5.4 - Constructores e Instanciación 1.6 - Dado un conjunto de clases y superclases, desarrollar constructores para uno o más clases. Dada la declaración de una clase, determinar si un constructor por defecto será creado, y si es así, determinar el comportamiento de dicho constructor. Dada una lista de clases internas y no.internas, escribir código para instanciar la clase. 5.4 - Dado un escenario, desarrollar código que declare y/o invoque métodos sobrescritos y sobrecargados y código que declare y/o invoce constructores de la suplerclase, sobrescritos o sobrecargados. 8.1. Conceptos Básicos de los Constructores Cada clase, incluyendo las clases abstractas, DEBEN tener un constructor. Un constructor tiene el siguiente aspecto: Como se ve, no hay tipo de retorno. Recordar dos puntos claves sobre los constructores:  No tiene tipo de retorno  El nombre debe coincidir exactamente con el nombre de la clase

Los constructores se utilizan para inicializar el estado de las variables de instancia, como se muestra a continuación: En el código anterior, la clase Foo no tiene un constructor sin argumentos. Esto significa que el siguiente código no compilará: Pero este otro código si compilará: Por lo que es muy común (y deseable) que una clase tenga un constructor sin argumentos, independientemente de cuantos otros constructores sobrecargados tiene la clase (sí, los constructores pueden sobrecargarse). No siempre podremos cumplir con esto, ocasionalmente tendremos una clase donde no tiene sentido crear una instancia sin aportar información al constructor. 8.2. Encadenado Constructores Sabemos que los constructores se invocan en tiempo de ejecución cuando usamos el operador 'new': ¿Pero qué es lo que ocurre realmente? (Asumiremos que Horse extiende Animal y Animal extiende Object) 1. El constructor Horse es invocado. Cada constructor invoca el constructor de su superclase con una llamada implícita a 'super()', a no ser que el constructor invoque un constructor sobrecargado de la misma clase (a continuación se ampliará esto). 2. Se invoca el constructor de Animal. 3. Se invoca el contructor de Object (Object es la última suplerclase de todas las clases, por lo que no escribiremos 'extendsObject', esto es implícito). En este punto, estamos elel principio de la pila. 4. Las variables de instancia toman sus valores explícitos. Por valores explícitos entendemos valores que son asignados en el momento que se

5. 6. 7. 8. 9. declaran las variables, como 'int x = 27', donde '27' es el valor explícito (en oposición al valor por defecto) de la variable de instancia. Se completa el constructor de Object. La variables de instancia de Animal toman sus valores explícitos (si tuvieran). Se completa el constructor de Animal. La variables de instancia de Horse toman sus valores explícitos (si tuvieran). Se completa el constructor de Horse. La siguiente tabla muestra cómo trabajan los constructores en la pila de llamadas: 4. Object() 3. Animal() callssuper() 2. Horse() callssuper() 1. main() calls new Horse() 8.3. Reglas para los Constructores La siguiente lista resume las reglas que necesitamos saber para el examen (y para comprender el resto de la sección). DEBEMOS recordarlas y estudiarlas más de una vez:         Los constructores pueden usar cualquier modificador de acceso. Esto significa que sólo el código de dentro de la clase puede instanciar un objeto de dicho tipo, por lo que si queremos permitir el uso de una instancia de la clase, la propia clase debe proporcionar un método o una variable estática para permitir el acceso a una instancia creada en el cuerpo de la clase). El nombre del constructor debe coincidir con el nombre de la clase. Los constructores no tienen tipo de retorno. Es legal (pero estúpido) tener un método con el mismo nombre de la clase, pero esto no lo convierte en un constructor. Si vemos el tipo de retorno, es un método y no un constructor. De hecho, podríamos tener un método y un constructor con el mismo nombre -el nombre de la clase- en la misma clase y no sería un problema para Java. Ten cuidado de no confundir un método con un constructor, debemos mirar el tipo de retorno. Si no escribimos un constructor en nuestro código de clase, se generará un constructor por defecto por el compilador. El constructor por defecto SIEMPRE es un constructor sin argumentos. Si queremos tener un constructor por defecto y hemos escrito otro/s constructor/s en nuestro código de clase, el compilador no proporcionará un constructor sin argumentos (o cualquier otro constructor). Dicho de otra forma, si hemos escrito un constructor con argumentos, no tendremos un constructor sin argumentos a no ser que lo escribamos nosotros. Cada constructor tiene, como primera sentencia, una llamada a un constructor sobrecargado (this()) o una llamada al constructor de la superclase (super()), aunque recuerda que esta llamada puede ser insertada por el compilador.

        Si escribimos un constructor (en lugar de confiar en el constructor por defecto generado por el compilador) y no escribimos una llamada a super() o a this(), el compilador insertará una llamada sin argumentos a super(), como primera llamada en el constructor. Una llamada a super() puede ser una llamada sin argumentos o puede incluir argumentos pasados al super constructor. Un constructor sin argumentos no es necesariamente el constructor por defecto (por ejemplo proporcionado por el compilador), aunque el constructor por defecto siempre es un constructor sin argumentos. El constructor por defecto es el que proporciona el compilador. Nosotros somos libres de crear nuestro propio constructor sin argumentos. No podemos hacer una llamada a un método de instancia, o acceder a una variable de instancia, hasta que se haya ejecutado el super constructor. Sólo podemos acceder a métodos y variables estáticos como parte de la llamada a super() o this(). Las clase abstractas tiene constructores y dichos constructores siempre son llamados cuando se instancia la subclase concreta. Las interfaces no tienen constructores. Las interfaces no son parte del árbol de herencia de un objeto. La única forma de invocar un constructor es desde otro constructor. En otras palabras, no podemos escribir código que llame a un constructor tal y como se muestra a continuación: 8.4. Determinar si será Creado un Constructor por Defecto El siguiente ejemplo muestra una clase Horse con dos constructores: ¿Pondrá un constructor por defecto el compilador en la clase anterior?. No!, ¿y en la siguiente variación?

Ahora el compilador, ¿insertará un constructor por defecto?. No!, ¿y en la siguiente clase? Ahora el compilador generará un constructor por defecto para la clase anterior, puesto que la clase no ha definido constructores. Veamos la siguiente: Podría parecer que el compilador no creará ninguna, puesto que hay un constructor en la clase Horse. ¿O no es así?, echemos otro vistazo a la clase anterior. ¿Qué hay incorrecto en el constructor Horse()?. Resulta que no es un constructor. Simplemente es un método que tiene el mismo nombre que la clase. Recuerda que el tipo de retorno es un signo inequívoco de que estamos viendo un método y no un constructor. 8.5. ¿Cómo Podemos Saber el Aspecto del Constructor por Defecto? Por lo siguiente:  El constructor por defecto tiene el mismo modificador de acceso que la clase.  El constructor por defecto no tiene argumentos.  El constructor por defecto incluye una llamada sin argumentos al super constructor (super()). La siguiente tabla muestra qué generará (y qué no) para nuestra clase. Código de Clase (lo que Código de Constructor Generado por el escribimos) Compilador (en negrita) classFoo { classFoo { } Foo() { super ( ) ; } } classFoo { classFoo { Foo() { } Foo() { super() ; } } } class Foo { publicclassFoo { } public Foo() { super(); } } class Foo { class Foo { Foo(String s) { } Foo (String s) { super(); } } } class Foo { Foo(String s) { super(); } Nada, el compilador no necesita insertar nada. } classFoo { classFoo {

voidFoo() } { } voidFoo() Foo() } { { super(); } } (voidFoo() es un método, no un constructor.) 8.6. ¿Qué Ocurre si el Super Constructor tiene Argumentos? Los constructores pueden tener argumentos como los métodos y si intentamos invocar un método que toma, por ejemplo, un entero pero no le pasamos nada al método, el compilador se quejará tal y como se muestra a continuación: El compilador se quejará de que no podemos invocar el método sin pasarle un entero. Sin embargo, el compilador podría lanzar un mensaje dudoso en algunas versiones de la JVM: UseBar.java:7: takeInt(int) in Bar cannot be applied to() b.takeInt(); ^ La última línea es la que debería encajar con el método. Y por encajar, queremos decir que los tipos de argumentos deben ser capaz de aceptar los valores o variables que le estamos pasando y en el orden que se los pasamos. Los constructores trabajan de la misma forma. Por lo que si nuestro super constructor (el constructor de la inmediata superclarse) tiene argumentos, deberíamos aportar en la llamada a super() los argumentos apropiados. Aquí hay un punto crucial: si nuestra superclase no tiene un constructor sin argumentos, debemos escribir un constructor en nuestra clase (sublcase) puesto que necesitamos un lugar donde colocar la llamada a super con los argumentos apropiados.

A continuación se muestra un ejemplo del problema: Una vez más el compilador lanza un mensaje dudoso: Horse.java:7: cannot resolve symbol symbol : constructor Animal () location: class Animal super(); // Problem! ^ Si somos afortunados nuestro compilador podría ser un poco más explícito. Pero de nuevo, el problema es que no existe nada que encaje con lo que intentamos ejecutar -un constructor de Animal sin argumentos. Otra forma de decir esto es que nuestra superclase no tiene un constructor sin argumentos, por lo que en nuestra subclase no podemos usar el constructor por defecto aportado por el compilador. Así de simple. Puesto que el compilador sólo puede poner en una llamada la invocación a super() (sin argumentos), no seremos capaces de compilar algo parecido a lo siguiente: Si intentamos compilar este código obtendremos el mismo error que cuando ponemos un constructor en la subclase con una llamada a la versión sin argumentos de super(): Clothing.java:4: cannot resolve symbol symbol : constructor Clothing () location: class Clothing classTShirtextendsClothing { } ^

De hecho, el código anterior es implícitamente el mismo que el siguiente: Un último apunte sobre los constructores por defecto (y probablemente muy obvio), los constructores nunca se heredan. No son métodos. No pueden ser sobrescritos (puesto que no son métodos y sólo los métodos de instancia pueden sobrescribirse). Por lo que el/los tipos de constructores de nuestra superclase no determina de ninguna forma el tipo de constructor por defecto que obtendremos. Algunas personas creen equivocadamente que el constructor por defecto encaja de alguna forma con el super constructor, bien en los argumentos que tendrá el constructor por defecto (recuerda que el constructor por defecto es siempre sin argumentos) o en la llamada a super() aportada por el compilador. Por tanto, aunque los constructores no pueden ser sobrescritos, hemos visto que pueden ser sobrecargados. 8.8. Constructores Sobrecargados Sobrecargar un constructor significa escribir múltiples versiones del constructor, cada una con una lista diferente de argumentos como los siguientes ejemplos: La clase Foo anterior tiene dos constructores sobrecargados, uno toma un String y el otro nada. Puesto que no hay código en la versión sin argumentos, es idéntica al constructor por defecto que proporciona el compilador, pero recuerda que si hay algún constructor en la clase, el compilador no proporcionará un constructor por defecto. Si queremos un constructor sin argumentos, además de una versión sobrecargada con argumentos, debemos escribirlo nosotros, como en el ejemplo anterior. Sobrescribir un constructor se usa normalmente para proporcionar formas alternativas a los clientes para instanciar objetos de nuestra clase. Por ejemplo, si

el cliente conoce el nombre del animal, puede pasarlo al constructor que toma un String. Pero si no lo conoce, llamará al constructor sin argumentos y dicho constructor proporcionará un nombre por defecto. Veámoslo: Al ejecutar el código cuatro veces obtendremos la siguiente salida: % java Animal GigiZeus % java Animal FluffyZeus % java Animal RoverZeus % java Animal FluffyZeus La siguiente tabla muestra la pila de llamadas para las invocaciones de constructores cuando se sobrecarga un constructor. Luego veremos la explicación del código. 4 Object 3 Animal(Strings s) calls Super() 2 Animal() callsthis(randomlyChosenNameString) 1 main() calls new Animal() En lugar de llamar a super(), estamos llamando a this() y this() siempre significa una llamada a otro constructor de la misma clase. Tarde o temprano siempre se llamará al constructor super(), sólo lo estamos retrasando. Algún constructor, en

algún lugar, deberá llamar a super(). Concepto Clave: La primera línea en un constructor DEBE ser una llamada a super() o una llamada a this(). No hay excepciones. Si no hacemos dichas llamadas en nuestro constructor, el compilador insertará una llamada sin argumentos a super(). Un constructor nunca tendrá una llamada a super() y una llamada a this(). Cada una de esas llamadas deberían ser la primera sentencia en un constructor. Esto implica que el compilador no pondrá una llamada a super() en el constructor que tenga una llamada a this().Veamos un código de ejemplo: El compilador podría no detectar el problema, el asume que sabemos lo que estamos haciendo. El problema es que dado que un super constructor siempre debe llamarse. Recordemos que el compilador no pone un constructor por defecto si ya tenemos alguno. Y cuando el compilador no pone un constructor por defecto, inserta una llamada a super() en cualquier constructor que no tenga una llamada explícita al super constructor -a no ser que el constructor tenga una llamada a this(). Por lo que en el código anterior obtendríamos un bucle infinito y la pila se sobrecarga obteniendo un: % java A Exception in thread "main" java.lang.StackOverflowError El beneficio de tener constructores sobrecargados es que ofrecemos formas flexibles de instanciar objetos de nuestra clase. Los beneficios de tener un constructor que invoque a otro sobrecargado es evitar la duplicación de código. Básicamente, cada uno de los otros constructores "no reales" llamarán a otro constructor sobrecargado, pasándole lo que necesite. 9. Objetivo de Certificación 1.3 – Estáticos Desarrollar código que declare, inicialize y use tipos primitivos, enumerados, arrays y objetos como estáticos, de instancia y como variables locales. Ademas, usar identificadores legales para los nombres de variables. 9.1. Métodos y Variables Estáticos Para comprender cómo funcionan los miembros estáticos, veamos una razón para usarlo. Imaginemos que tenemos una clase de utilidad que dispone de un método que siempre se ejecuta de la misma forma, por ejemplo devolver un número aleatorio. No importa la instancia de la clase que realiza el método, siempre se

comportará igual. Dicho de otra forma, el comportamiento del método no tiene dependencia con el estado (valores de variables de instancia) de un objeto. Los métodos y variables estáticos siguen a la clase y no a una instancia en particular. De hecho, podemos usarlos sin necesidad de tener una instancia de dicha clase, sólo necesitamos tener accesible la clase. Si hubiera instancias, una variable estática de una clase se compartirá por todas las instancias de la clase. El siguiente código declara y usa una variable estática contador: En el código anterior, la variable frogCount se pone a 0 cuando se carga por primera vez la clase en la JVM, antes de que se cree cualquier instancia (aunque no sería necesario inicializarla puesto que toman el mismo valor por defecto que si se tratara de variables de instancia). Siempre que se crea una instancia de Frog, el constructor se ejecuta e incrementa la variable contador. El resultado de la ejecución del código anterior es el siguiente: Frogcountisnow 3 Ahora imaginemos qué hubiera pasado si la variable fuera una variable de instancia: Cuando se ejecuta este código, debería seguir creando las tres instancias de Frog, ¡pero el resultado es un error de compilador!. No podemos compilar este código y mucho menos ejecutarlo.

Frog.java:11: non-static variable frogCountcannot staticcontext System.out.println("Frog count is " + frogCount); ^ 1 error be referencedfrom a El problema es que main() es un método estático y por tanto no se está ejecutando contra una instancia de la clase particular. Un método estático no puede acceder a una variable no-estática (de instancia), puesto que no es una instancia. Esto no quiere decir que no haya objetos en la pila, sino que el método estático no sabe nada de ellos. Lo mismo se aplica a los métodos de instancia, uno estático no puede invocar directamente un método no-estático. Es decir, estático = clase, no-estático = instancia. 9.2. Accediendo a Métodos y Variables Estáticos Ya sabemos que no necesitamos una instancia para invocar miembros estáticos, pero entonces ¿cómo invocarlos?, ¿cuál es la sintaxis?. Sabemos que para un método de instancia usamos el operador punto sobre una instancia: En el código anterior, instanciamos un Frog, lo asignamos a la variable de referencia f y luego usamos la referencia para invocar un método de la instancia de Frog que hemos creado. Esto no debe aplicarse para acceder a un método estático, puesto que podría no haber instancias de la clase. Por tanto, la forma de acceder a un método estático (o variable estática) es usar el operador punto sobre el nombre de la clase, tal y como se muestra a continuación:

El lenguaje Java también nos permite usar una variable de referencia a objeto para acceder a un miembro estático: En el código anterior, instanciamos un Frog, lo asignamos a la variable f y luego usamos dicha variable para invocar un método estático. Pero aunque estemos usando una instancia específica para acceder a un método estático, las reglas no han cambiado. Es simplemente un truco de sintaxis, pero el miembro es invariable con respecto a la instancia particular que usemos. En el ejemplo, el compilador sólo se preocupa de que la variable de referencia sea declarada de tipo Frog. Recordar que los métodos estáticos no pueden ser sobrescritos. Esto no significa que no puedan ser redefinidos en una subclase, pero redefinir y sobrescribir no es lo mismo. Vemoas un ejemplo de un méotodo estático redefinido (no sobrescrito): La ejecución de este código produce la siguiente salida: aaa

Recuerda, la sintaxis a[x].dostuff() es simplemente un comando directo, el compilador lo sustituirá por algo como Animal.dostuff(). Observa que no hemos utilizado la mejora del bucle de Java 1.5. 10. Objetivo de Certificación 5.1 - Acoplamiento y Cohesión Desarrollar código que implemente una fuerte encapsulación, un bajo acoplamiento y una alta cohesión en clases, describiendo posteriormente los beneficios. Las definiciones del examen de Sun para la Cohesión y el Acoplamiento son un poco subjetivas, por lo que en este capítulo se discutirán los términos desde la perspectiva del examen y no significa que sean La Única Definición Real de estos dos principios de diseño de OO. En general, un buen diseño OO establece un bajo acoplamiento y evita un alto acoplamiento así como una alta cohesión, evitando una baja cohesión. Como la mayoría de las discusiones de diseño OO, los objetivos de una aplicación son:  Facilidad de creación  Facilidad de Mantenimiento  Facilidad de Mejoras 10.1. Acoplamiento: Acoplamiento es la cantidad de conocimiento que una clase tiene sobre otra. Si el único conocimiento que una clase A tiene sobre una clase B es lo que B expone a través de su interfaz, entonces la clase A y la clase B están bajamente acopladas, lo que es algo bueno. Usando el segundo escenario, imaginemos que ocurrirá cuando se mejore la clase B. Es posible que el desarrollador de la mejora no sepa de la clase A. El desarrollador de B piensa que cualquier mejora que no rompa la interfaz de la clase debería ser segura, por lo que podría cambiar algunas partes de la clase que no pertenecen a la interfaz, lo que causaría que se rompiera la clase A. En el extremo del espectro del acoplamiento es la horrible situación en el que la clase A conoce cosas que no son de la API de B y viceversa. Esto es MUY MALO. Si alguna de las clases se cambia, hay posibilidades de que la otra clase deje de funcionar. Veamos un ejemplo obvio de alto acoplamiento, que ha sido permitido por una encapsulación pobre:

Todas las aplicaciones OO no triviales son una mezcla de clases e interfaces trabajando juntas. De forma ideal, las interacciones entre objetos en un sistema OO deberían ser mediante APIs, o contratos, de las distintas clases de objetos. De forma teórica, si todas las clases de la aplicación tuvieran APIs bien diseñadas, debería ser posible que todas las interacciones usaran dichas APIs. Como hemos visto antes en este capítulo, un aspecto de un buen diseño de clase y API es que las clases deberían estar bien encapsuladas. En el fondo, el acoplamiento es un concepto un poco subjetivo. Debido a esto, el examen nos probará en ejemplos realmente obvios de alto acoplamiento, no se nos preguntarán por llamadas sutiles. 10.2. Cohesión Mientras que la cohesión trata sobre cómo interactúan las clases, la cohesión trata sobre cómo una única clase se diseña. El término cohesión se usa para indicar el grado mediante el cual una clase tiene un único y bien enfocado propósito. Ten en mente que la cohesión es un concepto subjetivo. Enfocado a una clase, una alta cohesión es una buena característica. El beneficio clave de alta cohesión es que las clases son más fáciles de mantener (y suelen cambiar menos). Otro beneficio es que las clases con un propósito bien enfocado tiene a ser más reutilizables que otras clases. Veamos un ejemplo:

Ahora imagina que nuestro jefe dice, "Los clientes han decidido que quieren generar un informe de proyección de ingresos y también algún informe de inventario. Les gusta nuestras características de informes, pero quieren asegurarse de que dichos informes puedan ser enviados a una base de datos, una impresora y salvar los informes generados en ficheros de datos". En lugar de poner todo el código de impresión en una clase de informe, probablemente sería mejor haber hecho el siguiente diseño desde el principio: Este diseño es mucho más cohesivo. En lugar de tener una clase que haga todo, hemos dividido el sistema en cuatro clases principales, cada una con un papel muy específico o cohesivo. Puesto que hemos construido estas clases especializadas y reutilizables, será mucho más fácil escribir un nuevo informe, puesto que tenemos una clase de conexión a base de datos, la clase para la impresora y la clase para salvar el contenido en fichero. Esto significa que pueden reutilizarse por otras clases que quisieran imprimir un informe. 2. Orientación a Objetos Guía para el Examen 2.1 Observar el código que aparece con preguntas sobre el comportamiento de un método, cuando el problema es realmente una falta de encapsulación. Observa el siguiente ejemplo y veremos a qué nos referimos:

Ahora consideremos esta pregunta: ¿El valor de la variable 'right' siempre va a ser un tercio del valor de la variable 'left'?. Tiene pinta de que sí excepto por el hecho de que los usuarios de la clase Foo no necesitan usar el método setLeft(). Simplemente pueden cambiar directamente el valor de las variables a un valor entero arbitrario. Guía para el Examen 2.2 Si un método está sobrescrito pero usamos una referencia polimórfica (supertipo) para hacer referencia al objeto subtipo con el método que sobrescribe, el compilador asume que estamos llamado a la versión supertipo del método. Si la versión supertipo declara una excepción checked, aunque no lo haga el método que la sobrescribe, el compilador sigue creyendo que estamos llamando a un método que declara una excepción. Veamos un ejemplo: Este código no compilará por la Excepción declarada en el método eat() de

Animal. Esto ocurre siempre, aun cuando en tiempo de ejecución, el método eat() usado sea el de la versión Dog, que no declara la excepción. Guía para el Exámen 2.3 No te dejes engañar por un método que está sobrecargado pero no sobrescrito por una subclase. Es perfectamente legal hacer lo siguiente: La clase Bar tiene dos métodos doStuff(): la versión sin argumentos que hereda de Foo (y que no sobrecribe) y la versión sobrecargada dostuff (string s) definida en la clase Bar. Un código con una referencia a un Foo puede invocar sólo la versión sin argumentos, pero si se trata de un Bar podría invocar cualquiera de las versiones sobrecargadas. Guía para el Exámen 2.4 Los creadores del examen te dirán que están obligados a apretar mucho código en espacios pequeños "por el funcionamiento del examen". Aunque esto es parcialmente cierto, también les gusta liar. El siguiente código: Puede ser reemplazado con esta otra forma comprimida: En este caso, el compilador necesita todos esos paréntesis, de otra forma pensaría que está manejando una sentencia incompleta. Guía para el Exámen 2.5 Buscar usos ilegales de extends e implements. A continuación se muestran ejemplos de declaraciones legales e ilegales de clases e interfaces:

Independientemente de lo que nos estén preguntando, el problema real de una pregunta podría estar en la declaración de la clase o interfaz. Antes de quedar atrapados en la traza de un flujo de hilos complejo, debemos comprobar si el código compila. Seremos impresionados por el esfuerzo que ponen los desarroladores del examen en distraernos del problema real. Guía para el Exámen 2.6 Buscar métodos que declaren un tipo de retorno que sea clase abstracta o interfaz y saber que cualquier objeto que pase la prueba IS-A (dicho de otra forma, el operador instanceofdevolerá true) puede ser devuelto desde dicho método, por ejemplo: Este código compilará, el valor de retorno es un subtipo. Guía para el Exámen 2.7 Uno de los principales misterios para los programadores Java novatos es cuando intentan acceder a una variable de instancia (no estática) desde el método estático main() (que no sabe nada de cualquier instancia y que, por lo tanto, no puede acceder a la variable). El siguiente código es un ejemplo de acceso ilegal a una variable no estática desde un método estático:

Este código nunca compilará puesto que no podemos acceder a variables no estáticas desde un método estático. Por supuesto, el truco del examen es que la pregunta no será tan obvia como el código anterior. El problema puede estar en que no aparece tan claro. Por ejemplo, el código anterior podría quedar: Por tanto, mientras que estamos intentando seguir la lógica, el problema real es que x e y no pueden ser usados en el cuerpo del método main(). Lo mismo se aplica al acceso a métodos no-estáticos desde un método estático. La regla es, un método estátido de una clase no puede acceder a una variable o método de instancia (no-estático) de su propia clase.

Add a comment

Related presentations

Related pages

Oracle Certified Professional, Java SE 6 Programmer

Programmers with advanced Oracle Java SE 6 Professional Certifications under their belt have an edge in the market. ... Certified Java Programmer
Read more

Programmer Level II Exam (The Java™ Tutorials > Bonus ...

Programmer Level II Exam. ... This exam is associated with the "Oracle Certified Professional, Java SE 7 Programmer" certificate. ... Java File I/O (NIO.2)
Read more

Oracle Certified Professional, Java SE 6 Programmer - Acclaim

An Oracle Certified Professional, Java SE 6 Programmer ... Vamsi Krishna Sandra ... An Oracle Certified Professional, Java SE 6 Programmer has ...
Read more

NEW CERTIFICATION: Oracle Certified Professional (OCP ...

... new "Oracle Certified Professional, Java SE 7 Programmer ... for Java SE 7 Programmer, we have to take 2 ... Java SE 5 or SE 6 Programmer, ...
Read more

Oracle Certified Professional – Java SE 6 Programmer 2 ...

Specialties: Python, Java, Application Servers, Troubleshooting, Interoperability, Knowledge Management, VMware, vSphere, Monitoring, Automation
Read more

Oracle Certified Professional, Java SE 6 Programmer

Oracle Certified Professional, Java SE 6 Programmer. ... The Oracle Certified Professional, Java SE 6 Programmer certification provides certification ...
Read more

Oracle Certification Program - Wikipedia, the free ...

Oracle Certified Associate Java SE Programmer I ... Oracle Certified Professional Java SE Programmer ... Oracle Certification Program;
Read more

What is SCJP 6 - Java Certification Preparation and ...

What is SCJP 6? Oracle offers an SCJP 6 exam to achieve Oracle Certified Professional, Java SE 6 Programmer Certification. Oracle Certified Professional ...
Read more

Oracle Certified Professional, Java SE 6 Programmer ...

Oracle offers variety of Java Certifications for all Java Editions (Java SE, Java EE and Java ME). Oracle Certified Professional, Java SE 6 Programmer ...
Read more

Lesson: Preparation for Java Programmer Language ...

Preparation for Java Programmer Language ... to Java SE 7 Programmer exam results in the "Oracle Certified Professional, Java SE 7 Programmer ...
Read more