Herencia, Reutilización y La Clase Object



Una de las características esenciales de la programación orientada a objetos (POO) es la herencia, ya que esta es una poderosa herramienta de reutilización de software; en ella podemos utilizar los componentes de una clase ya existente de forma total o parcial, dándonos la oportunidad, incluso, de mejorar ese código o utilizarlo tal cual. Una de las ventajas que podemos nombrar es que permite la reutilizacion de software probado y depurado por otros programadores, disminuyendo asi el tiempo del desarrollo. Para utilizar la herencia debemos crear una clase que "Extienda o herede" de otra, la clase plantilla en este caso es conocida como SUPERCLASE y la clase que extiende de esta es la SUBCLASE, en algunos lenguajes como C++ la superclase es conocida como la clase base y la subclase como la clase derivada. Podemos pensar este tema de la siguiente manera; una superclase es una idea  general sobre un objeto y la subclase es mas especifica y trata con los detalles. por consiguiente podemos afirmar que la herencia se trata de una especialización de las diferentes clases heredadas. En java hay dos tipos de herencia, la directa y la indirecta; en la directa, nosotros mismos somos los encargados de especificar la superclase de la nuestra a través de la palabra extends (ya veremos como usarla), La herencia indirecta es aquella que se hace de forma implicita solo por el hecho de crear un clase, por ejemplo al crear un clase, de manera predeterminada esta extiende (hereda) de la clase Object (la clase mas alta en el arbol). ¡¡ java es un lenguaje de herencia simple !! es decir solo puede extender de una clase a la vez. Mas adelante cuando veamos poliformismo e interfaces veremos como podemos usar otras tecnicas para emular lo que hace la herencia multiple.

LA relación "Es un" y "Tiene un"

La relacion "es un" habla simpre de herencia y puede explicarse atraves de una frase simple como "es auto ES UN medio de transporte" como puedes darte cuenta auto es un subtipo de medio de transporte, en este caso podriamos decir que medio de transporte es la superclase y auto es la subclase:

caracteristicas (atributos) de Medios de Transporte: 
    -trasladan cosas, personas, animales.
    -puede ser aéreo, terrestre, marítimo
    -recorre un camino con punto de inicio y final.
    
características(atributos) de Autos;
    -trasladan cosas, personas, animales.
    -es terrestre.
    -recorre un camino con punto de inicio y final.
    -tiene un volante-
    -puede ser manual o automático
    -acelera y frena
Como puedes ver "medios de transporte" podría considerarse como una plantilla que podria definirse a si misma y a sus atributos, pero también serviría para crear especificaciones de si misma, a través de la creación de clases que describan por ejemplo a; autos, aviones, helicópteros, barcos etc...
En este caso describimos auto para hacer notar que comparte características de medios de transporte, pero no podríamos declarar un auto simplemente como medio de transporte ya que tiene detalles que no podrían especificarse en ella, como por ejemplo ; si es automático o manual, que es terrestre, etc... no podríamos poner como atributos estas cosas en medios de transporte ya que tenemos, por ejemplo, un avion que no es terrestre.
En contraste la relación "tiene un " hace referencia a la composición (que ya veremos en detalle) es decir a los atributos propios de la clase, por ejemplo un auto "tiene un" máximo de velocidad. por lo general estos atributos de clase suelen especificarse mediante "static"

Alcance de las variables y clases en la herencia
ya hablamos en otras entradas sobre los modificadores de acceso, Es importante a esta altura recordarlo para utilizar lo aprendido en el campo de la herencia.
hablamos sobre los miembros public de una clase que pueden ser vistos desde cualquier otra clase, del mismo u otro paquete. tambien hablamos sobre los miembros private que solo son visibles desde la clase en que se declaran ESTE TIPO DE VARIABLES NO SE HEREDAN.  y miembros protected que tienen un nivel de acceso intermedio, estos miembros pueden ser vistos por las clases heredadas y por las demas clases del mismo paquete en que se declaran.
*A pesar de no tenemos visibilidad desde una subclase d elos miembros private de la superclase, si podemos modificar dichas variables a través de métodos public heredados de la superclase.
*Se declaran variables private para proteger al máximo los valores de las mismas de la intrucion no permitida.
*El compilador establece de forma automática a  Object como la clase predeterminada en caso de no encontrar alguno otra clase declarada como superclase de la nuestra.
ej:
public class Animal {
    private String tipo;//se establece como private para que no pueda ser cambiada desde la subclase
    
    public void establecerNombre(String tipo)//establece el nombre del animal
    {
    this.tipo = tipo;
    }
    
    public void mensaje()
    {
    System.out.println("Tu animal es un "+tipo);
    }
    
    public void camina()
    {
    System.out.println(tipo+" camina");
    }
    
    public void vuela()
    {
    System.out.println(tipo+" vuela");
    }
}
en esta clase, puse metodos publicos, uno para establecer el nombre o tipo de animal y otras para mostrar mensajes respecto al tipo de animal
public class Perro extends Animal{ //subclase de animal
    
    Perro() //creamos un constructor para poder llamar  a los metodos de la superclase
    {
    this.establecerNombre("perro");  //establesco el tipo de animal 
    this.mensaje(); //llamamos solo a los metodos que utilizaremos de la super clase
    this.camina();
    }
    
    public static void main(String[]args)
    {
    Perro ini = new Perro();
    }
}
por ejemplo, en la superclase a hay un método que se llama vuela, como nuestro animal (perro) no vuela, no utilizaremos este método, pero podríamos crear otra clase llamada "Pajaro"  que si va a necesitar el metodo vuela(). 
(En la superclase podriamos haber puesto infinidad de metodos para aludir a acciones)
la salida de este programa seria asi:
run:
Tu animal es un perro
perro camina
BUILD SUCCESSFUL (total time: 0 seconds)
La herencia no solo nos permite llamar a los métodos o atributos de una clase, sino que tambien podemos sobre escribirlos
por ejemplo, en la superclase animal esta el método
public void mensaje()
    {
    System.out.println("Tu animal es un "+tipo);
    }
Al cual llamamos desde el constructor de la subclase Perro, pero que pasa si quisieramos modificar ese mensaje?? pues podríamos hacerlo asi, Desde la clase perro sobreescribimos el metodo mensaje() lo que nos quedaría asi.
public class Perro extends Animal{
    
    Perro()
    {
    this.establecerNombre("perro");
    mensaje();
    this.camina();
    }
    public void mensaje()
    {
    System.out.println("tu animal es el mejor amigo del hombre");
    }
    
    public static void main(String[]args)
    {
    Perro ini = new Perro();
    }
}
//al hacer esto estamos sobrescribiendo el metodo mensaje() (el compilador nos mostrara un mensaje de advertencia diciendonos que estamos sobreescribiendo un metodo de la super clase). fijense que en el constructor de perro elimine el this y llame al metodo solo con el nombre.
la salida de este programa seria esta :
run:
tu animal es el mejor amigo del hombre
perro camina
BUILD SUCCESSFUL (total time: 1 second)
Como tercera opcion tambien podemos sobreescribir un metodo de la superclase y al mismo tiempo llamar a dicho metodo a traves de la palabra clave super. Con super podemos llamar al metodo o constructor de la superclase, para ello debemos estar en el metodo sobrescrito o en el constructor. Para ello modificaremos unas simples lineas de nuestra clase perro.
public class Perro extends Animal{
    
    Perro()
    {
    this.establecerNombre("perro");
    mensaje();
    this.camina();
    }
    public void mensaje()
    {
    super.mensaje();
    System.out.println("tu animal es el mejor amigo del hombre");
    }
    
    public static void main(String[]args)
    {
    Perro ini = new Perro();
    }
}
 //Si te fijas, este codigo es casi igual al anterios, lo unico diferente es que tiene la sentencia super.mensaje();
lo cual hace un llamado al metodo original, lo ejecuta y despues continua con el codgio dentro del metodo sobreescrito.
La salida seria la siguiente.
run:
Tu animal es un perro
tu animal es el mejor amigo del hombre
perro camina
BUILD SUCCESSFUL (total time: 0 seconds)
si te fijas, se ha agregado a la salida la linea de código del método mensaje original al cual llamamos mediante super.
*Es un error de compilación sobrescribir un método con un modificador de acceso más restringido; un método public de la superclase no puede convertirse en un método protected o private en la subclase; un método protected de la superclase no puede convertirse en un método private en la subclase. Hacer esto sería quebrantar la relación es un, en la que se requiere que todos los objetos de la subclase puedan responder a las llamadas a métodos que se hagan a los métodos public declarados en la superclase. Si un método public pudiera sobrescribirse como protected o private, los objetos de la subclase no podrían responder a las mismas llamadas a métodos que los objetos de la superclase. Una vez que se declara un método como public en una superclase, el método sigue siendo public para todas las subclases directas e indirectas de esa clase.
*Llamar a un metodo o constructor de la superclase con un numerod e argumentos y tipos diferentes de los requeridos produce un error de compilacion.
*Es recomendable no usar  variables protected, en vez de eso utiliza  variables private y especifica metodos public para modificar dichas variables.
*si queremos llamar a un metodo de la superclase desde un metodo sobrescrito de la subclase, debemos asegurarnos de llamarlo con la palabra clave super y un punto. ya que de no hacerlo se genera una recursividad infinita, en la que el metodo se llame asi mismo, hasta agotar la memoria.
miMetodo() //metodo sobreescrito de la superclase
{
miMetodo(); // sin super, provoca recursividad ya que el metodo se llama a si mismo.
}
asi se hace correctamente.
miMetodo()
{
super.miMetodo(); //llama al metodo de la super clase.
}
La recursividad infinita e suna poderosa herramienta que veremos mas adelante.
La clase Object
Como ya hemos dicho varias veces todas las clases en java heredan de la Clase Object, y con ello obtienen sus 8 metodos, puedes probarlo creando una clase e instanciandola, pones el nombre de la instancia y un punto y te saldran los 11 metodos heredados de object.
clone()
Este método protected, que no recibe argumentos y devuelve una referencia Object, realiza una copia
del objeto en el que se llama. Cuando se requiere la clonación para los objetos de una clase, ésta debe
sobrescribir el método clone como un método public, y debe implementar la interfaz Cloneable
(paquete java.lang). La implementación predeterminada de este método realiza algo que se conoce
como copia superfi cial: los valores de las variables de instancia en un objeto se copian a otro objeto del
mismo tipo. Para los tipos por referencia, sólo se copian las referencias. Una implementación típica
del método clone sobrescrito sería realizar una copia en profundidad, que crea un nuevo objeto para
cada variable de instancia de tipo por referencia. Hay muchos detalles sutiles en cuanto a sobrescribir el
método clone. Puede aprender más acerca de la clonación en el siguiente artículo:
java.sun.com/developer/JDCTechTips/2001/tt0306.html
equals()
Este método compara la igualdad entre dos objetos; devuelve true si son iguales y false en caso contrario.
El método recibe cualquier objeto Object como argumento. Cuando debe compararse la igualdad
entre objetos de una clase en particular, la clase debe sobrescribir el método equals para comparar
el contenido de los dos objetos. La implementación de este método debe cumplir los siguientes requerimientos:
• Debe devolver false si el argumento es null.
• Debe devolver true si un objeto se compara consigo mismo, como en objeto1.equals( objeto1 ).
• Debe devolver true sólo si tanto objeto1.equals( objeto2 ) como objeto2.equals( objeto1 )
devuelven true.
• Para tres objetos, si objeto1.equals( objeto2 ) devuelve true y objeto2.equals( objeto3 )
devuelve true, entonces objeto1.equals( objeto3 ) también debe devolver true.
• Si equals se llama varias veces con los dos objetos, y éstos no cambian, el método debe devolver true
de manera consistente si los objetos son iguales, y false en caso contrario.
Una clase que sobrescribe a equals también debe sobrescribir hashCode para asegurar que los objetos
iguales tengan códigos de hash idénticos. La implementación equals predeterminada utiliza el operador
== para determinar si dos referencias se refi eren al mismo objeto en la memoria. La sección 30.3.3
demuestra el método equals de la clase String y explica la diferencia entre comparar objetos String
con == y con equals,
finalize()
El recolector de basura llama a este método protected  para
realizar las tareas de preparación para la terminación en un objeto, justo antes de que el recolector de
basura reclame la memoria de ese objeto. No se garantiza que el recolector de basura vaya a reclamar un
objeto, por lo que no se puede garantizar que se ejecute el método finalize del objeto. El método debe
especifi car una lista de parámetros vacía y debe devolver void. La implementación predeterminada de
este método sirve como un receptáculo que no hace nada.
getClass()
Todo objeto en Java conoce su tipo en tiempo de ejecución. El método getClass devuelve un objeto de la clase Class (paquete java.lang), el cual contiene
información acerca del tipo del objeto, como el nombre de su clase (devuelto por el método getName de
Class). Puede aprender más acerca de la clase Class en la documentación de la API en línea, en java.
sun.com/javase/6/docs/api/java/lang/Class.html.
hashCode()
 Una tabla de hash es una estructura de datos  que relaciona a un objeto,
llamado la clave, con otro objeto, llamado el valor. Cuando inicialmente se inserta un valor en una
tabla de hash, se hace una llamada al método hashCode de la clave. La tabla de hash utiliza el valor de
código de hash devuelto para determinar la ubicación en la que se debe insertar el valor correspondiente.
La tabla de hash también utiliza el código de hash de la clave para localizar el valor correspondiente
de la misma.
notify()
notifyAll()
wait()
Los métodos notify, notifyAll y las tres versiones sobrecargadas de wait están relacionados con el
subprocesamiento múltiple. En versiones recientes de Java, el modelo
de subprocesamiento múltiple ha cambiado en forma considerable, pero estas características se siguen
soportando.
toString()
Este método devuelve una representación String de un objeto. La
implementación predeterminada de este método devuelve el nombre del paquete y el nombre de la clase
del objeto, seguidos por una representación hexadecimal del valor devuelto por el método hashCode del
objeto.

String, una mirada más detallada.



La clase String representa a las cadenas de caracteres y como tal no hemos de olvidarnos que es un objeto, por lo tanto es del tipo por referencia, siempre que puedo hago hincapié en la diferencia entre los primitivos y los de referencia pues es importante ya que cada tipo tiene características que lo hacen único  así como las herramientas para trabajar con ellos, por ejemplo en la comparación.
Una variable de tipo primitivo como un int puede compararse a través de los operadores binarios (binarios porque necesitan dos operandos)

int num = 5, num2 = 10;

num == num2;  //compara la igualdad entre num y num2

pero este tipo de comparación no es valida para un String, en realidad el compilador no dará error si se utiliza el operador == para comparar dos String, pero si mostrara la advertencia y esto es porque == compara a nivel binario los datos como si se tratase de un valor primitivo, esto puede generar errores en tiempo de ejecución difíciles de encontrar y depurar, sobretodo si estamos comparando variables con cadenas de caracteres entrantes a través del teclado.
en vez de eso utilizaremos un método de la clase String llamado equals. lo utilizaremos como ejemplo en un if.

String nombre1 = "horacio";
String nombre2 = "carlos";

if(nombre1.equals(nombre2))
{
System.out.println("Son iguales");
}

para utilizar equals ponemos el nombre de la variable a comparar, un punto y equals() entre los parentesis va la variable que se comparara con la primera. Este metodo (equals) devuelve un valor booleano, true si son iguales y false si son diferentes.
Si te fijas, llamamos al método atraves de un punto despues del nombre de la variable al igual como lo hacemos cuando instanciamos una clase (clase instancia = new clase();) y esto es porque cuando creamos un String en realidad estamos creando una variable de aquella clase. por eso podemos acceder a los métodos de la clase String simplemente poniendo un punto después de la variable.

Comparar String de distintos tamaños.

String tiene un metodo llamado compareTo que es heredado desde una interfaz generica llamada Comparable, ya veremos de que se trata esto. Lo importante es que podemos usar el metodo recien nombrado en cualquier cadena de caracteres,
compareTo devuelve 0 si los String son iguales, un valor negativo si la primera palabra es lexicograficamente menor y un valor positivo si la palabra es lexicograficamente mayor

ej:

String hola = "hola";
String hola1 = "hola como estas";
   
   if(hola.compareTo(hola1) > 0) //compareTo devuelve un numero positivo si hola es mayor que hola1
   {
       System.out.println("mayor");
   }
   else if(hola.compareTo(hola1)<0) //compareTo devuelve un numero negativo si hola es menor que hola1
   {
      System.out.println("menor"); //este es el que se ejecuta ya que se cumple el encabezado del if. osea, el
   }                                            //numero que devulve compareTo es menor que 0  
   else if(hola.compareTo(hola1)==0) //compareTo devuelve 0 si ambos String son iguales
   {
       System.out.println("igual");
   }

El resultado de esto seria "menor". ya que hola es lexicograficamente menor que hola1.


Comparar String con mayusculas y minusculas.

A veces sera necesario comparar Strings que se diferencien solo por sus mayus o minusculas. Para ello utilizares el metodo equalsIgnoreCase(). Este metodo funciona igual que el equals.

String nombre = "chuña";
String nombre2="CHUÑA";

if(nombre.equalsIgnoreCase(nombre2))
{
System.out.println("Los nombres son iguales");.
}

Esto es muy util cuando tenemos una aplicación que pide ingreso de datos y no necesitamos ser tan estrictos con ello.


El metodo toString

El método toString es heredado por todos los objetos en java a través de clase Object (la clase mas alta en el árbol, por decirle así)  y nos permite mostrar al objeto como un String. por ahora solo lo veremos en arreglos. por ejemplo tenemos un pequeño problema, cada vez que queremos mostrar un arreglo (que es un objeto) debemos llamar a un for o a un while  y mostrar sus valores de forma individual. La solución para esto fue presentada en java 5 a traves de la clase Arrays con ayuda del método toString.
por ejemplo, supòngamos que deseamos imprimir el siguiente arreglo como un String.

int arregloA[] = {22,15,103,1,896};
para hacerlo debemos importar java.util.Arrays; , la clase quedaria algo asi.
///////////////////////////////////////////////////////
import java.util.Arrays;

public class saludos {
    
   public static void main(String[]args)
   {
   int variables[]={22,15,103,1,896};
   System.out.println(Arrays.toString(variables));
   }
}
////////////////////////////////////////////////////
La salida seria esta:
run:
[22, 15, 103, 1, 896]
GENERACIÓN CORRECTA (total time: 0 seconds)
 También se puede guardar el String de retorno del método Arrays.toString( ); en un String diferente.
por ejemplo
String resultante = Arrays.toString(variables);
Lo que va entre el paréntesis es el arreglo a imprimir. 


Otros metodos.... (Lo que esta entre paréntesis al lado del nombre del método es el tipo de valor de retorno del metodo)

length();   (int)// retorna el largo del String como un valor entero.
ej:
String dato ="vocales";
int vDato = dato.length();

por consiguiente vDato valdrá 7. Si contamos el largo de un String y este tiene espacios entre medio estos igual cuentan ya que esos espacios cuentan como caracteres también.


indexOf(); (int) //retorna la posición donde encuentre el String o el char pedido en su lista de parámetros
ej:
String cadena = "the beatles";
int busca = cadena.indexOf("beatles");

entonces busca ahora tendrá el valor 4 ya que encuentra a "beatles" a partir de la posición 4, recordar que se empieza a contar de 0 y que cuenta el espacio en blanco. en caso de no encontrar al String o char.


replace(); (String, char)//reemplaza un caracter (char) por otro especificado en la lista de parametros.
ej:
String cadena = "Pata";
String NCadena;
Ncadena = cadena.replace('a' , 'o');

Ncadena ahora tiene el String "Poto" ya que se reemplazaron las 'a' por las 'o'


replaceAll(); (String)// reemplaza un fragmento completo del String o el String completo.
ej:
String nombre = "juanin juanjarri";
nombre = nombre.replaceAll("juanjarri","larrain");

nombre, entonces, queda como "juanin larrain";

toUpperCase();(String) //cambia todas las letras a mayusculas
ej:
String nombre = "maria mercedes";
String nombre = nombre.toUpperCase();

ahora String contiene "MARIA MERCEDES" Cambien esta la opción inversa, es decir, convertir todo a minúsculas a través de toLowerCase();


subString(); (String) // toma un fragmento de un String y lo almacena en la misma o otra variable.
ej:
String frase = "resident evil es un buen juego"
frase = frase.subString(20,30);

frase ahora valdria "buen juego";

Transformaciones desde y hacia String

int valor = Integer.parseInt(String)                 // Pasar de String a int 
String resultado = Integer.toString(12345)      // Pasar de int a String 
String resultado = Double.toString(12345.25)  // Pasar de double a String 
String resultado = Float.toString(12345.70)     // Pasar de float a String 
String hilera = Character.toString(caracter)   //Pasar de caracter a hilera 


*los valores entre parentesis son solo ejemplos.

Temario de Preguntas 1


Preguntas:


1_ ¿Çual es la diferencia entre la programación estructurada y la orientada a objetos?



2: ¿Cual es la sentencia que hace ejecutable a una clase en Java?


3: ¿Cuales son los tipos primitivos en Java?


4: ¿Que palabra nos permite abandonar inmediatamente un case de la sentencia switch?


5: ¿Que sentencia nos permite mostrar texto en pantalla?


6: Para poder ingresar datos mediante el teclado ¿que clase debemos importar?


7:¿Cuantas veces puede cambiarse el tamaño de un arreglo una vez dimensionado o inicializado?


8: ¿Un metodo puede sobrecargarse?(escribir otro metodo con el mismo nombre pero con diferentes parametros)


9: ¿Cual es el valor predeterminado de una variable String, int y boolean?


10:¿Se puede declarar un metodo dentro de otro?


completa la frase




1: La palabra clave ________ crea un nuevo objeto de la clase especificada a la derecha de la palabra clave.


2: La palabra clave _________ es un especificador de acceso.


3: La instruccion __________ se utiliza para ejecutar una accion si una condicion es verdadera y otra cuando es falsa.


4: La instrucción ____________ evalúa la continuación del ciclo ___________ de ejecutar cuerpo del ciclo; por lo tanto el cuerpo siempre se ejecutara al menos una vez


5: Un objeto de la clase __________ produce numeros aleatorios.


6: Un __________ nos permite crear conjunto de objetos que comparten un mismo tipo.


7: Si al principio de una instrucción while la condición es __________ el cuerpo nunca se ejecutara.


8: El operador _________ resulta true solo si ambas condiciones son verdaderas.


9: Un ___________ no lleva tipo de retorno, ni de dato, tampoco especificador de acceso.


10 La sentencia _________________ solo nos permite iterar en un arreglo y no modificar sus valores.




Descubre el Error


1: int valor1 = 2.2;





2: public static void(String[]){}




3: for(int i=0;i==10; i++)
  {
  suma = i+1;
  }



4: int metodoA()
{
 int b=10
    int metodoB()
   {
    b=45;
   }
}




5: String arregloA []= new arregloA[];



6: 
if(numero == 10);
{
System.out.println("numero es igual a 10");
}//fin del if




7: 
double arreglo2[] = new double[10];
arreglo2[10] = 12;