Interfaz gráfica de usuario.(Creación de una ventana y dibujos)

La interfaz gráfica de Usuario (GUI por sus siglas en ingles) En Java es un capitulo extenso que iremos viendo a medida que avanzamos en los temas referente a este lenguaje. Puedes encontrar libros completos sobre las GUI's en java,
Generalmente a la programación de interfaz gráfica se le denomina swing,  Swing es, hoy en dia, la API (si no sabes lo que es una API lee esto http://en.wikipedia.org/wiki/Application_programming_interface) principal para programación a nivel gráfico y es parte de la JFC (Java Fundation Classes) Osea, se integra de manera nativa en Java. Swing es la evolución -por decirle así- de la antigua AWT (Abstract windows Toolkit) que también es un conjunto de clases diseñadas para implementar la interfaz gráfica en java.
El nacimiento de Swing de debe a las falencias obvias que se fueron dando en AWT en su implementación, algunas de ellas fueron:

 - awt dependía de la plataforma en que se desarrollaba para mostrar sus componentes, es decir, se mostraba una barra distinta en windows y en unix lo que traía problemas con los esquemas y no funcionaba efectivamente para el objetivo primordial de java; la portabilidad extrema entre plataformas.

- Al depender íntimamente del sistema en que se ejecuta, awt añade funcionalidades básicas o mínimas para poder operar correctamente. aunque esto tiene un beneficio; los componentes de awt son mas livianos que los de Swing.

Por el contrario, Swing se implementa 100% a través del codigo volviéndose independiente de la plataforma en que se ejecuta. Swing a su vez es mucho mas personalizable de AWT al tratarse de clases insertadas en arboles amplios de herencia que pueden conbinarse sin romper los hilos del código. Swing permite emular estilos de ventanas como los de windows classic, windows en versiones posteriores, mac y linux entre otros, a través de su LOOK AND FEEL. una de las ventajas principales de swing es que permite a los programadores proveer su propia implementación de apariencia sin la necesidad de grandes cambios en el código, esto gracias a que swing apunta a la uniformidad visual de sus componentes.


  JFrame

Comenzaremos nuestra discusión acerca de una de las cosas mas emocionantes de cualquier lenguaje (me refiero a la interfaz gráfica y diseño en general) con dibujos simples sobre un panel desplegado en pantalla, dejaremos de lado el terminal de netbeans para comenzar con las ventanas y gráficos.

Antes de comenzar a dibujar en un panel tenemos que comprender el sistema de coordenadas en Java.
De manera automática Java nos ubica el punto 0,0 en la parte superior izquierda de la pantalla o ventana dependiendo del objeto que trabajemos. Aclaro esto, cuando creamos un componente, por ejemplo un botón, y le damos la coordenada 100,250. no nos referimos a la coordenada 100,250 de nuestro monitor sino del componente donde vaya a encajar ese botón. vale aclarar esto.
 
La primera coordenada es el eje X y se refiere a la posición horizontal de la ventana o componente y la segunda coordenada se refiere al eje Y y se refiere a la posición vertical de la ventana o componente, en el que cada punto es en realidad un pixel.


Lo primero que debemos crear es una ventana o mejor dicho un marco que contenga los componentes que iremos añadiendo a ella, esto lo haremos atraves de la clase JFrame.




Como podemos ver en esta imagen JFrame es una version extendida de la clase Frame del paquete AWT, como muchos de los componente del paquete javax,swing  JFrame añadió características avanzadas a los componentes básicos de Frame tal y como lo explique al principio de esta entrada. 
Para crear un marco  con JFrame vamos a crear una clase que extienda de esta.



linea 1 - importamos la clase JFrame del paquete javax.swing. lo que nos permitira ocupar a JFrame cuando queramos dentro de esta clase.
linea 3 - hacemos que nuestra clase extienda de JFrame (extends) con esto tenemos los constructors, métodos normales y variables de JFrame listas para ser sobrecargados o llamados. Es importante ya le dice al programa que nuestra clase es una subclase de JFrame con lo que podemos crear nuestros propios atributos para la ventana que construiremos.
linea 5 - Creamos un constructor de nuestra clase. de vital importancia ya que debemos hacer un llamado del constructor de JFrame.
linea 7 - atraves de super llamamos al constructor de JFrame con un argumento de tipo String, este argumento sera el titulo de la ventana. JFrame tambien tiene otras tres versiones de su constructor sobrecargadas. Lo mas comun es que llamemos a la que recibe un parametro String como en este caso o en su defecto podemos llamar simplemente a la version por defecto atraves de super(); sin mas.
Recuerda que podemos utilizar super para llamar al constructor de JFrame por que hemos especificado que nuestra clase extiende de esta ultima.
linea 8 - el metodo setSize de JFrame recibe dos argumentos (largo, alto) en este caso la ventana sera de 300  x 300. es uno de los métodos esenciales para la creación de ventanas o contenedores ya que determina el tamaño inicial del mismo. veremos que este tamaño puede ser o no modificable ya sea en expansión o contracción.
linea 9 - setVisible este metodo (de JFrame) establece si la ventana sera visible, para ello recibe un argumento de tipo booleano, true si queremos que se vea la ventana y false en caso contrario. este método no es implícito del frame por lo que en caso de no llamarlo por olvido o lo que sea, la ventana no se mostrara.
linea 10 - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); sirve para que al presionar sobre la X de la ventana osea cuando queremos cerrarla el proceso termine y no continúe ejecutándose en memoria. EXIT_ON_CLOSE es una variable de tipo int, declarada como final y static. osea el método setDefaultCloseOperation recibe un parámetro int para determinar si que debe hacer con la ventana.
lo mejor es siempre establecer este parámetro como en el ejemplo.

Con estos métodos dentro del constructor ya hemos creado la primera ventana!!!!

linea 15 - se crea un objeto de Ventana1 dentro del método main para que se ejecute el constructor. el resultado de la ejecución es la ventana que aparece a un constado del código. como podemos ver es un marco vacio con un encabezado y los respectivos botones de minimizar y maximizar. notese que la ventana tiene la misma apariencia de windows 7 que es el sistema donde se esta ejecutando.



Esta es otra manera de crear la ventana, hay libros donde lo hacen así, la única diferencia con el código anterior es que los métodos son llamados a través de la instancia de la clase directamente en el main. Ambos códigos son completamente validos y operativos.

tal vez hayas notado que la ventana aparece al costado superior izquierdo de la pantalla, pues bueno Java nos permite posicionar donde queramos la ventana para ello debemos hacer una llamada al metodo

setLocationRelativeTo();

JFrame hereda este método de la clase Window del paquete java.awt. y nos permite especificar en que lugar de la pantalla aparecerá nuestra aplicación a traves de un parametro de tipo component. por ahora solo utilizaremos como parámetro  - null - lo que nos deja la ventana en el centro de la pantalla.

setLocationRelativeTo(null);

debemos localizar este metodo junto a los otros que pusimos en el constructor o donde este llamado el objeto JFrame.


JPanel

Ya creamos un marco de ventana para nuestra aplicacion de dibujo, ahora crearemos el "lienzo" en el que pondremos nuestros dibujos. para ello ocuparemos la clase JPanel.





JPanel es un contenedor simple que nos permite añadirle objetos como botones, barras, dibujar sobre el , etc...
Al igual que con JFrame debemos hacer que nuestra clase extienda de JPanel. Como sabemos una clase no puede extender de mas de una clase a la vez por lo que se suelen crear dos clases a la hora de crear una aplicación que dibuje. Aunque si se puede hacer en una sola clase. a ver si se te ocurre como!!!

JPanel y las clases que extiendan de ella cuentan con un método llamado paintComponent() que el sistema llama automáticamente cada vez que se utiliza un objeto JPanel. y es necesario que nosotros lo declaremos pues de lo contrario los objetos de JPanel podrían no mostrarse. 

paintComponent requiere un argumento de tipo Graphics que sirve para crear dibujos y otras graficas. 
La primera linea dentro del método paintComponent debe ser:

super.paintComponen(g);

esto asegura que el panel se muestre apropiadamente en la pantalla. por lo tanto quedaria asi.

paintComponent(Graphics g)
{
super.paintComponent(g);
......
......
}

ahora a partir del objeto Graphics g podemos llamar a los metodos apropiados para dibujar ovalos, cuadrados, letras etc...



lineas 1 y 2 - se hacen las importaciones de las clases que utilizaremos para dibujar. JPanel y Graphics.
linea 4 - hacemos que nuestra clase herede de JPanel (extienda). 
linea 6 - hacemos una sobre escritura del método paintComponent pasandole un argumento Graphics para poder crear dibujos o lo que queramos (la g del parámetro es solo el nombre del argumento, se pone g para saber que es un objeto Graphics pero puede ser cualquiera).
linea 8 - se hace la llamada directa al método de la superclase, a traves de super.  esto se hace para asegurar que se muestre correctamente en pantalla. yo he probado ejecutando los códigos sin esta llamada y no he tenido problemas al mostrar los componentes, pero aun así, es mejor asegurarse, ademas que esta llamada no genera desgaste de memoria.

Los metodos getWidth() y getHeight(); devuelven el ancho y la altura del panel en el momento respectivamente. en caso de que agrandemos o disminuyamos el tamaño de la ventana y por lo tanto del panel, se llama implícitamente a un método llamado repaint(); que vuelve a dibujar el objeto en el panel.


la linea 11 - llama al metodo drawLine(x, y, x ,y);  Este método dibuja una linea recta y recibe 4 argumentos de coordenadas. los primeros dos especifican el primer punto y las otras dos el punto final, luego ambos puntos se unen formando la linea recta.
En este caso le pasamos (0 , 0 , ancho, alto); por lo que la linea inicial queda en la esquina superior izquierda
(0, 0) y el segundo punto queda en la esquina inferior derecha (ancho, alto).
Recordemos que las variables ancho y alto reciben el tamaño del panel en el momento. osea si el panel es de (300, 300) esas son las coordenadas de ancho y alto.
la linea 12 - pone drawLine(0 , alto, ancho , 0); con lo que ponemos el primer punto en la esquina inferior izquierda (0 , alto) recordar que alto tiene el valor del tamaño de alto del panel, por lo que queda de 0 horizontalmente y con alto para abajo.

acá el código de la clase ejecutora. 



Este es el código de la clase que usamos para ejecutar el panel, si te fijas es practicamente la misma que utilizamos para crear el marco. es mas ¡¡¡es la misma!!! solo tiene algunas variaciones.

en la linea 10 - llamamos al método setLocationRelativeTo(null); que como la dije se utiliza para dejar la ventana en el medio de la pantalla.

linea 17 - se crea un objeto Panela que por consiguiente es un objeto JPanel.
linea 18 - se utiliza el método add de JFrame (recordar que Ventana1 por herencia es un objeto JFrame) y añadimos el panel para que se muestre en la ventana, sino se hace esto a través de  "add"  se mostrara solo el marco como en el primer ejemplo.

Ahora si agrandamos y disminuimos el tamaño de la ventana una vez ya ejecutada veremos como las lineas se vuelven a dibujar quedando siempre de esquina a esquina.




 













Objetos, Composición y miembros static

En java Casi todo es un objeto, la única excepción como ya se ha explicado son los tipos primitivos.
Los modificadores de acceso (public private, protected, friendly) nos permiten definir el alcance y desde
donde se puede acceder a los objetos. Es una buena practica de programación que establezcamos los métodos y variables importantes de una clase como private, por lo general se declaran así cuando son objetos a los que no se deben acceder directamente, para ello establecemos -a su vez- métodos public para modificar dichos valores. por ejemplo:

private int saldo; //variable declarada como private

public void agregarSaldo(int valor)
{
   if(valor >0)
  {
   saldo = saldo+valor;
  }else{
  System.out.println("debe ingresar un monto mayor  a 0");
  }
}//fin método modificarSaldo

Como podemos ver acá tenemos una variable <<saldo>> que esta como private para que no pueda
ser modificada desde ningún otro lugar que no sea la misma clase en que se declara, y a su vez tenemos un método <<agregarSaldo>> que es la encargada de modificar el saldo, siempre y cuando se cumpla una condición, en este caso que el valor ingresado sea mayor que 0. también podríamos tener un método
restarSaldo que solo permita restar un monto no mayor a 200000 como es el caso de los cajeros automáticos chilenos. Con estos métodos ponemos un limite en el acceso a lo que seria un valor importante como saldo.

ejercicio sugerido: Crea una clase Cuenta, con metodos deposito, giro y consulta, cada metodo debe validar sus datos y enviar mensajes apropiados en caso por ejemplo que de se intente hacer un giro mayor al saldo disponible. la variable saldo debe ser private y los memtodos public. crea el metodo main en otra clase.

this

Todos los objetos pueden acceder a una referencia de si mismos a través de la palabra clave this 
ej:

private int saldo;
   
   public void agregarSaldo(int saldo)
   {
   this.saldo = this.saldo + saldo;
   }


En este pequeño fragmento de código utilizamos la palabra "this" para hacer referencia a la variable saldo de
la clase, pues en caso contrario estaríamos haciendo referencia a la variable local "saldo" del método.
con this también podemos diferenciar entre dos variables en las relaciones de herencia, en caso de que nos vemos a declarar variables con el mismo nombre tanto en la superclase como el la subclase.

otro uso de this, es para referenciar a un constructor de la misma clase, por ejemplo:

public class referenciaThis{
  
    referenciaThis(int n1)
   {
    this(2,5); //esta llamada debe ser siempre la primera linea del constructor
   }
   
   referenciaThis(int n1, int n2)
   {
   

   }
}//fin de la clase refenciaThis.


Para llamar a un constructor dentro de otro, en la misma clase utilizamos simplemente this sin el nombre
del constructor y debemos asegurarnos que esta llamada sea la primera declaración en el constructor. poner un punto después de this para llamar a un constructor es un error de sintaxis.
¿pero como sabemos cual es el constructor al cual llamamos? La llamada se distingue a través de sus paramentos, en este caso como ponemos 2 y 5 estamos indicándole a this que llamamos al constructor de dos parámetros.
es un error auto-invocar a un constructor, es decir hacer que un constructor se llame a si mismo, esto produce recursividad infinita.
también cable aclarar que esta llamada a un constructor a través de this, es solo valida dentro de otro constructor y no de un método normal.

Un constructor puede llamar a los metodos de una clase simplemente utilizando su nombre y la lista de parámetros adecuada, pero debes tener en cuenta que el constructor esta inicializando el objeto (la clase) por lo que las variables de instancia pueden no estar en un estado consistente. Para evitar errores lógicos no realices llamadas a métodos que muestren o modifiquen variables de instancia que no han sido inicializadas

Composición

La capacidad de una clase de contener referencias de otra clase como miembros se conoce como Composición. por ejemplo una clase Calculadora debería tener referencia a la clase Numeros para realizar sus operaciones. La composición es una de las herramientas de re-utilización de software mas poderosas, ya que podemos crear objetos que sean útiles para múltiples objetos futuros.. por ejemplo podemos tener una clase Fecha que nos devuelva la fecha actual, y dicha clase la podriamos utilizar al interior de una clase agendaDespertador que utilice a la clase Fecha para comprobar si tenemos citas o recordatorios para la fecha actual.

Ejemplo:
Creamos una clase Triangulo que puede ser instanciada en cualquier parte donde necesitemos un triangulo. este ejemplo básico puede ser ampliado para obtener mas especificaciones del triangulo como el perimetro o la superficie.

public class Triangulo {

private int  altura, b, posX, posY;  
   
    Triangulo(int altura, int b) //constructor encargado de crear el triangulo e inicializar las variables
    {
    this.altura = altura;
    this.b = b;
    }
   
    int areaTri()
    {
    return (altura*b)/2;       //retorna el area
    }
   
    void setPosicion(int pX, int pY) //establece la posicion en el plano
    {
    posX = pX;
    posY = pY;
    }
   
    String getPosicion()  //retorna la posicion como un String con formato
    {
    String pos = ("X="+posX+",Y="+posY);  
    return pos;
    }
}//FIN DE LA CLASE


public class PruebaFiguras {

  public static void main(String []args)
  {
    Triangulo t1 = new Triangulo(15, 10); //crea el primer triangulo
    t1.setPosicion(100, 100);
    System.out.printf("%s\n%s%d\n%s%s\n\n","Triangulo 1","Area :",t1.areaTri()
            ,"Posicion :",t1.getPosicion());
   
    Triangulo t2 = new Triangulo(22,18); //crea el segundo triangulo
    t2.setPosicion(300, 100);
    System.out.printf("%s\n%s%d\n%s%s\n\n","Triangulo 2","Area :",t2.areaTri()
            ,"Posicion :",t2.getPosicion());
  }  //fin del metodo main
}//fin de la clase PruebaFiguras

*******************
la salida es la siguiente:

Triangulo 1
Area :75
Posicion :X=100,Y=100

Triangulo 2
Area :198
Posicion :X=300,Y=100

GENERACIÓN CORRECTA (total time: 0 seconds)

Este simple ejemplo nos demuestra que una clase "es una fabrica de objetos" en este caso objetos triángulos que podríamos utilizar en cualquier momento para saber un área, un perímetro o incluso posicionarlo en un plano o interfaz gráfica. Al crear objetos que sean re-utilizables ahorraremos tiempo en escribir codigo que ya puede estar probado y depurado de antemano.


Miembros static

Cada objeto mantiene una copia de todas las variables de instancia de clase, en algunos casos solo es necesario compartir una copia de alguna variable entre todos los objetos de la misma clase, con el objetivo de que los cambios escritos en dicha variable sean vistos por cualquier objeto de las clases.
Para estos fines utilizamos los denominados campos static o mejor llamadas variables de clase 

Veamos un ejemplo con datos static. Suponga que tenemos un videojuego con Marcianos y otras criaturas espaciales. Cada Marciano tiende a ser valiente y deseoso de atacar a otras criaturas espaciales cuando sabe que hay al menos otros cuatro Marcianos presentes. Si están presentes menos de cinco Marcianos, cada Marciano se vuelve cobarde. Por lo tanto, cada uno de ellos necesita saber el valor de cuentaMarcianos. Podríamos dotar a la clase Marciano con la variable cuentaMarcianos como variable de instancia. Si hacemos esto, entonces cada Marciano tendrá una copia separada de la variable de instancia, y cada vez que creemos un nuevo Marciano, tendremos que actualizar la variable de instancia cuentaMarcianos en todos los objetos Marciano. Las copias redundantes desperdician espacio y tiempo en actualizar cada una de las copias de la variable, además de ser un proceso propenso a errores. En vez de ello, declaramos a cuentaMarcianos como static, lo cual convierte a cuentaMarcianos en datos disponibles en toda la clase. Cada objeto Marciano puede ver la cuentaMarcianos como si fuera una variable de instancia de la clase Marciano, pero sólo se mantiene una copia de la variable static cuentaMarcianos. Esto nos ahorra espacio. Ahorramos tiempo al hacer que el constructor de Marciano incremente a la variable static cuentaMarcianos; como sólo hay una copia, no tenemos que incrementar cada una de las copias de cuentaMarcianos para cada uno de los objetos Marciano.



Las variables static tienen alcance a nivel de clase. Los miembros public static de una clase pueden
utilizarse a través de una referencia a cualquier objeto de esa clase, o califi cando el nombre del miembro con el nombre de la clase y un punto (.), como en Math.random(). Los miembros private static de una clase
pueden utilizarse solamente a través de los métodos de esa clase. En realidad, los miembros static de una
clase existen a pesar de que no existan objetos de esa clase; están disponibles tan pronto como la clase se carga en memoria, en tiempo de ejecución. Para acceder a un miembro public static cuando no existen objetos de la clase (y aún cuando sí existen), se debe anteponer el nombre de la clase y un punto (.) al miembro static de la clase, como en Math.PI. Para acceder a un miembro private static cuando no existen objetos de la clase debe proporcionarse un método public static, y para llamar a este método se debe califi car su nombre con el nombre de la clase y un punto. (Las clases, por ejemplo Math son importadas indirectamente atrevez del árbol de herencia implícito de java, Recuerda que toda clase en java hereda de la clase Object.)




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.