Ens este apartado vamos a
tener unos pequeños roses con los conceptos de programaciòn orientada a
objetos(POO) y un ejemplo completo del cual daremos un inicio a lo que es
clase, herencia, paquete, què es java2,
super-clase, y sub-clase.
ESTRUCTURA
GENERAL DE UN PROGRAMA JAVA
la estructura habitual de un
programa realizado en cualquier lenguaje orientado a objetos u OOP (Object
Oriented Programming), y en particular en el lenguaje Java. Aparece una clase
que contiene el programa principal (aquel que contiene la función main()) y algunas
clases de usuario (las específicas de la aplicación que se está desarrollando)
que son utilizadas por el programa principal. Los ficheros fuente tienen la extensión
*.java, mientras que los ficheros compilados tienen la extensión *.class.
Un fichero fuente (*.java)
puede contener más de una clase, pero sólo una puede ser public. El nombre del
fichero fuente debe coincidir con el de la clase public (con la extensión
*.java). Si por ejemplo en un fichero aparece la declaración (public class
MiClase {...}) entonces el nombre del fichero deberá ser MiClase.java. Es
importante que coincidan mayúsculas y minúsculas ya que MiClase.java y
miclase.java serían clases diferentes para Java. Si la clase no es public, no
es necesario que su nombre coincida con el del fichero. Una clase puede ser
public o package (default), pero no private o protected. Estos conceptos se
explican posteriormente.
De ordinario una aplicación
está constituida por varios ficheros *.class. Cada clase realiza unas funciones
particulares, permitiendo construir las aplicaciones con gran modularidad e independencia
entre clases. La aplicación se ejecuta por medio del nombre de la clase que
contiene la función main() (sin la extensión *.class). Las clases de Java se
agrupan en packages, que son librerías de clases. Si las clases no se definen
como pertenecientes a un package, se utiliza un package por defecto (default)
que es el directorio activo.
Concepto
de Clase
Una clase es
una agrupación de datos (variables o campos) y de funciones
(métodos) que operan sobre esos datos. A estos datos y funciones
pertenecientes a una clase se les denomina variables y métodos
o funciones miembro. La programación orientada a objetos
se basa en la programación de clases. Un programa se construye a partir de un
conjunto de clases. Una vez definida e implementada una clase, es posible declarar
elementos de esta clase de modo similar a como se declaran las variables del
lenguaje (de los tipos primitivos int, double, String, …).
Los elementos declarados de una clase se denominan objetos de la
clase. De una única clase se pueden declarar o crear numerosos objetos.
La clase es lo genérico: es el patrón o modelo para crear objetos.
Cada objeto tiene sus propias copias de las variables miembro, con sus propios
valores, en
general distintos de los demás objetos de la clase. Las clases pueden tener
variables static, que son propias de la clase y no de cada
objeto.
Concepto
de Interface
Una interface es un conjunto
de declaraciones de funciones. Si una clase implementa (implements) una
interface, debe definir todas las funciones especificadas por la interface. Una
clase puede implementar más de una interface, representando una forma
alternativa de la herencia múltiple.
A su vez, una interface
puede derivar de otra o incluso de varias interfaces, en cuyo caso incorpora
todos los métodos de las interfaces de las que deriva.
Concepto
de Package
Un package es una agrupación
de clases. Existen una serie de packages incluidos en el lenguaje
(ver jerarquía de clases que
aparece en el API de Java).
Además el usuario puede
crear sus propios packages. Lo habitual es juntar en packages las clases que
estén relacionadas. Todas las clases que formen parte de un package deben estar
en el mismo directorio.
Es importante distinguir
entre lo que significa herencia y package. Un package es una agrupación
arbitraria de clases, una forma de organizar las clases. La herencia sin
embargo consiste en crear nuevas clases en base a otras ya existentes. Las
clases incluidas en un package no derivan por lo general de una única clase.
Java 2 (antes llamado Java
1.2 o JDK 1.2) es la tercera versión importante del lenguaje de programación
Java.
No hay cambios conceptuales
importantes respecto a Java 1.1 (en Java 1.1 sí los hubo respecto a Java 1.0),
sino extensiones y ampliaciones, lo cual hace que a muchos efectos –por ejemplo,
para esta introducción- sea casi lo mismo trabajar con Java 1.1 o con Java 1.2.
Los programas desarrollados en Java presentan diversas ventajas frente a los desarrollados
en otros lenguajes como C/C++. La ejecución de programas en Java tiene muchas posibilidades:
ejecución como aplicación independiente (Stand-alone Application), ejecución
como applet, ejecución como servlet, etc. Un applet es una aplicación especial
que se ejecuta dentro de un navegador o browser (por ejemplo Netscape Navigator
o Internet Explorer) al cargar una página HTML desde un servidor Web. El applet
se descarga desde el servidor y no requiere instalación en el ordenador donde
se encuentra el browser. Un servlet es una aplicación sin interface gráfica que
seejecuta en un servidor de Internet. La ejecución como aplicación
independiente es análoga a los programas desarrollados con otros lenguajes.
Las
variables PATH y CLASSPATH
Java utiliza además una
nueva variable de entorno denominada CLASSPATH, la cual determina dónde buscar
tanto las clases o librerías de Java (el API de Java) como otras clases de usuario.
A partir de la versión 1.1.4 del JDK no es necesario indicar esta variable,
salvo que se desee añadir conjuntos de clases de usuario que no vengan con
dicho JDK. La variable CLASSPATH puede incluir la ruta de directorios o
ficheros *.zip o *.jar en los que se encuentren los ficheros *.class. En el
caso de los ficheros *.zip hay que observar que los ficheros en él incluidosno
deben estar comprimidos. En el caso de archivos *.jar existe una herramienta
(jar.exe), incorporada en el JDK, que permite generar estos ficheros a partir
de los archivos compilados *.class. Los ficheros *.jar son archivos comprimidos
y por lo tanto ocupan menos espacio que los archivos *.class por separado o que
el fichero *.zip equivalente. Una forma general de indicar estas dos variables
es crear un fichero batch de MS-DOS (*.bat) donde se indiquen los valores de
dichas variables. Cada vez que se abra una ventana de MS-DOS será necesario
ejecutar este fichero *.bat para asignar adecuadamente estos valores. Un
posible fichero llamado jdk117.bat, podría ser como sigue:
set
JAVAPATH=C:\jdk1.1.7
set
PATH=.;%JAVAPATH%\bin;%PATH%
set
CLASSPATH=.\;%JAVAPATH%\lib\classes.zip;%CLASSPATH%
lo cual sería válido en el
caso de que el JDK estuviera situado en el directorio C:\jdk1.1.7.
A continuación se muestra el
programa principal, contenido en el fichero Ejemplo1.java. En realidad, este
programa principal lo único que hace es utilizar la clase Geometría y sus
clases derivadas. Es pues un programa puramente “usuario”, a pesar de lo cual
hay que definirlo dentro de una clase, como todos los programas en Java
También es posible utilizar
la opción –classpath en el momento de llamar al compilador javac.exe o al
intérprete java.exe. En este caso los ficheros *.jar deben ponerse con el
nombre completo en el CLASSPATH: no basta poner el PATH o directorio en el que
se encuentra. Por ejemplo, si se desea compilar y ejecutar el fichero
ContieneMain.java, y éste necesitara la librería de clases
G:\MyProject\OtherClasses.jar, además de las incluidas en el CLASSPATH, la
forma de compilar y ejecutar sería:
javac
-classpath .\;G:\MyProject\OtherClasses.jar ContieneMain.java
java
-classpath .\;G:\MyProject\OtherClasses.jar ContieneMain
Se aconseja consultar la
ayuda correspondiente a la versión que se esté utilizando, debido a que existen
pequeñas variaciones entre las distintas versiones del JDK.
Clase
Ejemplo1
A continuación se muestra el
programa principal, contenido en el fichero Ejemplo1.java. Enrealidad, este
programa principal lo único que hace es utilizar la clase Geometría y sus
clases derivadas.Es pues un programa puramente “usuario”, a pesar de lo cual
hay que definirlo dentro de una clase, como todos los programas en Java.
1. // fichero Ejemplo1.java
2. import java.util.Vector;
3. import java.awt.*;
4. class Ejemplo1 {
5. public static void main(String arg[])
throws InterruptedException
6. {
7. System.out.println("Comienza
main()...");
8. Circulo c = new Circulo(2.0, 2.0, 4.0);
9. System.out.println("Radio = " +
c.r + " unidades.");
10. System.out.println("Centro = ("
+ c.x + "," + c.y + ") unidades.");
11. Circulo c1 = new Circulo(1.0, 1.0, 2.0);
12. Circulo c2 = new Circulo(0.0, 0.0, 3.0);
13. c = c1.elMayor(c2);
14. System.out.println("El mayor radio es
" + c.r + ".");
15. c = new Circulo(); // c.r = 0.0;
16. c = Circulo.elMayor(c1, c2);
17. System.out.println("El mayor radio es
" + c.r + ".");
18. VentanaCerrable ventana =
19. new VentanaCerrable("Ventana abierta
al mundo...");
20. ArrayList v = new ArrayList();
21. CirculoGrafico cg1 = new
CirculoGrafico(200, 200, 100, Color.red);
22. CirculoGrafico cg2 = new
CirculoGrafico(300, 200, 100, Color.blue);
23. RectanguloGrafico rg = new
24. RectanguloGrafico(50, 50, 450, 350,
Color.green);
25. v.add(cg1);
26. v.add(cg2);
27. v.add(rg);
28. PanelDibujo mipanel = new PanelDibujo(v);
29. ventana.add(mipanel);
30. ventana.setSize(500, 400);
31. ventana.setVisible(true);
32. System.out.println("Termina
main()...");
33. } // fin de main()
34. } // fin de class Ejemplo1
Explicaciòn
del código
La sentencia 1 es
simplemente un comentario que contiene el nombre del fichero. El compilador de
Java ignora todo lo que va desde los caracteres // hasta el final de la línea. Las
sentencias 2 y 3 “importan” clases de los packages de Java, esto es, hacen
posible acceder a dichas clases utilizando nombres cortos. Por ejemplo, se puede
acceder a la clase Vector simplemente con el nombre Vector en lugar de con el
nombre completo java.util.Vector, por haber introducido la sentencia import de
la línea 2. Un package es una agrupación de clases que tienen una finalidad
relacionada. Existe una jerarquía de packages que se refleja en
nombres compuestos, separados por un punto (.). Es habitual nombrar los packages
con letras minúsculas (como java.util o java.awt),
mientras que los nombres de las clases suelen empezar siempre por una letra
mayúscula (como Vector). El asterisco (*) de la sentencia 3
indica que se importan todas las clases del package. Hay un package,
llamado java.lang, que se importa siempre automáticamente. Las
clases de java.lang se pueden utilizar directamente, sin importar
el package.
La sentencia 4 indica que se
comienza a definir la clase Ejemplo1. La definición de dicha clase
va entre llaves {}. Como también hay otras construcciones que van
entre llaves, es habitual indentar o sangrar el código, de forma que quede
claro donde empieza (línea 4) y donde termina (línea 34) la definición de la
clase. En Java todo son clases: no se puede definir una variable
o una función que no pertenezca a una clase. En este caso, la clase Ejemplo1
tiene como única finalidad acoger al método main(), que
es el programa principal del ejemplo. Las clases utilizadas por main()
son mucho más importantes que la propia clase Ejemplo1. Se puede
adelantar ya que una clase es una agrupación de variables
miembro (datos) y funciones miembro (métodos)
que operan sobre dichos datos y permiten comunicarse con otras clases. Las
clases son verdaderos tipos de variables o datos, creados por el
usuario. Un objeto (en ocasiones también llamado instancia)
es una variable concreta de una clase, con su propia copia de las variables
miembro.
Las líneas 5-33
contienen la definición del programa principal de la aplicación, que en Java siempre
se llama main(). La ejecución siempre comienza por el programa o método main().
La palabra public indica que esta función puede ser utilizada por cualquier
clase; la palabra static indica que es un método de clase, es decir, un método
que puede ser utilizado aunque no se haya creado ningún objeto de la clase
Ejemplo1 (que de hecho, no se han creado); la palabra void indica que este
método no tiene valor de retorno. A continuación del nombre aparecen, entre
paréntesis, los argumentos del método. En el caso de main() el argumento es siempre
un vector o array (se sabe por la presencia de los corchetes []), en este caso
llamado arg, de cadenas de caracteres (objetos de la clase String). Estos
argumentos suelen ser parámetros que se pasan al programa en el momento de
comenzar la ejecución (por ejemplo, el nombre del fichero donde están los
datos).
El cuerpo (body)
del método main(), definido en las
líneas 6-33, va también encerrado entre llaves {...}. A un conjunto de
sentencias encerrado entre llaves se le suele llamar bloque. Es conveniente
indentar para saber dónde empieza y dónde terminan los bloques del método main() y de la clase Ejemplo1. Los
bloques nunca pueden estar entrecruzados; un bloque puede contener a otro, pero
nunca se puede cerrar el bloque exterior antes de haber cerrado el interior. La
sentencia 7(System.out.println("Comienza main()...");) imprime una
cadena de caracteres o Stringen la salida estándar del sistema, que normalmente
será una ventana de MSDOS o una ventana especial del entorno de programación
que se utilice (por ejemplo Visual J++, de Microsoft). Para ello se utiliza el
método println(), que está asociado con una variable static llamada out,
perteneciente a la clase System (en el package por defecto, java.lang). Una variable
miembro static, también llamada variable de clase, es una variable miembro que
es única para toda
la clase y que
existe aunque no se haya creado ningún objeto de la clase. La variable out es
una variable static de la clase System. La sentencia 7, al igual que las que siguen,
termina con el carácter punto y coma (;).
La sentencia 8 (Circulo c = new Circulo(2.0, 2.0, 4.0);)
es muy propia de Java. En ella
se crea un
objeto de la clase Circulo, que se define en el Apartado 1.3.4, en la página
11. Esta sentencia es equivalente a las dos sentencias siguientes:
Circulo c;
c = new Circulo(2.0, 2.0, 4.0);
que quizás son
más fáciles de explicar. En primer lugar se crea una referencia llamada c a un
objeto de la clase Circulo. Crear una referencia es como crear un “nombre”
válido para referirse a un objeto de la clase Circulo. A continuación, con el
operador new se crea el objeto propiamente dicho. Puede verse que el nombre de
la clase va seguido por tres argumentos entre paréntesis. Estos argumentos se
le pasan al constructor de la clase como datos concretos para crear el objeto
(en este caso los argumentos son las dos coordenadas del centro y el radio).
Interesa ahora
insistir un poco más en la diferencia entre clase y objeto. La clase Circulo es lo genérico: es el patrón o
modelo para crear círculos concretos. El
objeto c es un círculo concreto, con su centro y su radio. De la clase Circulo se pueden crear tantos objetos
como se desee; la clase dice que cada objeto necesita tres datos (las dos
coordenadas del centro y el radio) que son las variables miembro de la clase.
Cada objeto tiene sus propias copias de las variables miembro, con sus propios valores,
distintos de los demás objetos de la clase.
La sentencia 9
(System.out.println("Radio = " + c.r + " unidades.");)
imprime por la salida estándar una cadena de texto que contiene el valor del
radio. Esta cadena de texto se compone de tres sub-cadenas, unidas mediante el
operador de concatenación (+). Obsérvese cómo se accede al radio del objeto c:
el nombre del objeto seguido del nombre de la variable miembro r, unidos por el
operador punto (c.r). El valor numérico del radio se convierte automáticamente
en cadena de caracteres. La sentencia 10 es similar a la 9, imprimiendo las
coordenadas del centro del círculo.
Las sentencias
11 y 12 crean dos nuevos objetos de la clase Circulo, llamados c1 y c2.
La sentencia 13
(c = c1.elMayor(c2);) utiliza el método elMayor() de la clase Circulo. Este método
compara los radios de dos círculos y devuelve como valor de retorno una
referencia al círculo que tenga mayor radio. Esa referencia se almacena en la
referencia previamente creada c. Un punto importante es que todos los métodos
de Java (excepto los métodos de clase o static) se aplican a un objeto de la
clase por medio del operador punto (por ejemplo, c1.elMayor()). El otro objeto
(c2) se pasa como argumento entre paréntesis. Obsérvese la forma “asimétrica”
en la que se pasan los dos argumentos al método elMayor(). De ordinario se llama
argumento implícito a c1, mientras que c2 sería el argumento explícito del
método.
La sentencia 14
imprime el resultado de la comparación anterior y la sentencia 15 crea un nuevo
objeto de la clase Circulo guardándolo en la referencia c. En este caso no se
pasan argumentos al constructor de la clase. Eso quiere decir que deberá
utilizar algunos valores “por defecto” para el centro y el radio. Esta
sentencia anula o borra el resultado de la primera comparación de radios, de
modo que se pueda comprobar el resultado de la segunda comparación.
La sentencia 16
(c = Circulo.elMayor(c1, c2);) vuelve a utilizar un método llamado
elMayor() para comparar dos
círculos: ¿Se trata del mismo método de la sentencia 13, utilizado de otra
forma? No. Se trata de un método diferente, aunque tenga el mismo nombre. A las
funciones o métodos que son diferentes porque tienen distinto código, aunque
tengan el mismo nombre, se les llama funciones sobrecargadas (overloaded). Las
funciones sobrecargadas se diferencian por el nùmero y tipo de sus argumentos.
El método de la sentencia 13 tiene un único argumento, mientras que el de la
sentencia 16 tiene dos (en todos los casos objetos de la clase Circulo). En
realidad, el método de la sentencia 16 es un método static (o método de clase),
esto es, un método que no necesita ningún objeto como argumento implícito. Los
métodos static suelen ir precedidos por el
nombre de la
clase y
el operador punto (Java también permite que vayan precedidos por
el nombre de cualquier objeto, pero es considerada una nomenclatura más
confusa.). La sentencia 16 es absolutamente equivalente a la sentencia 13, pero
el método static de la sentencia 16 es más “simétrico”. Las
sentencias 17 y 18 no requieren ya comentarios especiales.
Las sentencias
18-31 tienen que ver con la parte gráfica del ejemplo. En las líneas 18-19 (VentanaCerrable ventana = new
VentanaCerrable("Ventana abierta al mundo...");) se crea una ventana
para dibujar sobre ella. Una ventana es un objeto de la clase Frame,
del package java.awt. La clase VentanaCerrable,
explicada en el Apartado 1.3.9 en la página 18, añade a la clase Frame
la capacidad de responder a los eventos que provocan el cierre de
una ventana. La cadena que se le pasa como argumento es el título que aparecerá
en la ventana (ver Figura de ejemplo1). En la sentencia 20 (Vector v = new Vector();) se crea un
objeto de la clase ArrayList (contenida o definida en el package java.util).
La clase ArrayList permite almacenar referencias a objetos de
distintas clases. En este caso se utilizará para almacenar referencias a varias
figuras geométricas diferentes.
Las siguientes
sentencias 21-27 crean elementos gráficos y los incluyen en la lista v para
ser dibujados más tarde en el objeto de la clase PanelDibujo. Los
objetos de la clase Circulo creadosanteriormente no eran objetos
aptos para ser dibujados, pues sólo tenían información del centro y el radio, y
no del color de línea. Las clases RectanguloGrafico y CirculoGrafico,
definidas en los Apartados siguientes, derivan respectivamente de las clases Rectangulo
(Apartado rectangulo) y Circulo (Apartado 1.3.4),
heredando de dichas clases sus variables miembro y métodos, añadiendo la
información y los métodos necesarios para poder dibujarlos en la pantalla.
En las
sentencias 21- 22 se definen dos objetos de la clase CirculoGrafico;
a las coordenadas del centro y al radio se une el color de la línea. En la
sentencia 23-24 se define un objeto de la clase RectanguloGrafico,
especificando asimismo un color, además de las coordenadas del vértice superior
izquierdo, y del vértice inferior derecho. En las sentencias 25-27 los objetos
gráficos creados se añaden al vector v, utilizando el método addElement()
de la propia clase Vector. En la sentencia 28 (PanelDibujo mipanel = new
PanelDibujo(v);)
se crea un objeto de la clase PanelDibujo, definida en el
Apartado 1.3.8. Por decirlo de alguna manera, los objetos de dicha clase son paneles,
esto es superficies en las que se puede dibujar. Al constructor de PanelDibujo
se le pasa como argumento el vector v con las referencias
a los objetos a dibujar. La sentencia 29 (ventana.add(mipanel);) añade o
incluye el panel (la superficie de dibujo) en la ventana; la
sentencia 30 (ventana.setSize(500,
400);)
establece el tamaño de la ventana en pixels; finalmente, la sentencia 31
(ventana.setVisible(true);) hace visible
la ventana creada.
¿Cómo se consigue que se dibuje todo esto? La clave está
en la serie de órdenes que se han ido dando al computador. La clase PanelDibujo
deriva de la clase Container a través de Panel, y redefine el método paint() de
Container. En este método, explicado en el Apartado 1.3.8, se realiza el dibujo
de los objetos gráficos creados. El usuario no tiene que preocuparse de llamar
al método paint(), pues se llama de modo automático cada vez que el sistema
operativo tiene alguna razón para ello (por ejemplo cuando se crea la ventana,
cuando se mueve, cuando se minimiza o maximiza, cuando aparece después de haber
estado oculta, etc.). La Figura 1.1 muestra la ventana resultante de la
ejecución del programa main() de la clase Ejemplo1. Para entender más a fondo este
resultado es necesario considerar detenidamente las clases definidas en los
apartados que siguen.
En este apartado
se describe la clase más importante de esta aplicación. Es la más importante no
en el sentido de lo que hace, sino en el de que las demás clases “derivan” de
ella, o por decirlo de otra forma, se apoyan o cuelgan de ella. La Figura 1.2
muestra la jerarquía de clases utilizada en este ejemplo. La clase Geometria es la base de la
jerarquía. En realidad no es la base, pues en Java la clase base es siempre la
clase Object. Siempre que no se diga explícitamente que una clase deriva de otra,
deriva implícitamente de la clase Object (definida
en el package java.lang). De las clases Rectangulo y Circulo derivan
respectivamente las clases RectanguloGrafico
y CirculoGrafico. En ambos casos
está por en medio un elemento un poco especial donde aparece la palabra
Dibujable. En términos de Java, Dibujable es una interface. Más adelante se
verá qué es una interface. Se suele utilizar la nomenclatura de super-clase y sub-clase para referirse
a la clase padre o hija de una clase determinada. Así Geometría es una super-clase
de Circulo, mientras que CirculoGrafico es
una sub-clase.
NOTA: en próximos artículos estaremos continuando
con los siguientes ejemplos y temas que hemos pospuesto.