T5-01-Desarrollo de Clases
T5-01-Desarrollo de Clases
T5-01-Desarrollo de Clases
objetos.
5.A. Introducción a clases.
5.B. Atributos.
5.C. Métodos.
5.D. Encapsulación, control de acceso y
visibilidad.
5.E. Utilizando métodos y atributos de clase.
5.F. Constructores.
1
5.A. Introducción y conceptos de la POO
1. Concepto de clase.
Como ya has visto en anteriores unidades, las clases están compuestas por
atributos y métodos. Una clase especifica las características comunes de un
conjunto de objetos. De esta forma los programas que escribas estarán
formados por un conjunto de clases a partir de las cuales irás creando objetos
que se interrelacionarán unos con otros.
Recomendación
• Un coche de color rojo, marca SEAT, modelo Toledo, del año 2003. En este
ejemplo tenemos una serie de atributos, como el color (en este caso rojo),
la marca, el modelo, el año, etc. Así mismo también podríamos imaginar
determinadas características como la cantidad de combustible que le
queda, o el número de kilómetros recorridos hasta el momento.
• Un coche de color amarillo, marca Opel, modelo Astra, del año 2002.
• Otro coche de color amarillo, marca Opel, modelo Astra y también del año
2002. Se trataría de otro objeto con las mismas propiedades que el
anterior, pero sería un segundo objeto.
• Un cocodrilo de cuatro metros de longitud y de veinte años de edad.
2
• Un círculo de radio 2 centímetros, con centro en las coordenadas (0,0) y
relleno de color amarillo.
• Un círculo de radio 3 centímetros, con centro en las coordenadas (1,2) y
relleno de color verde.
• Los coches.
• Los círculos.
• Los cocodrilos.
Por otro lado, también podrías imaginar algunos atributos cuyos valores
podrían ir cambiando en función de las acciones que se realizaran sobre el
objeto: ubicación del coche (coordenadas), velocidad instantánea, kilómetros
recorridos, velocidad media, cantidad de combustible en el depósito, etc. En el
caso de los cocodrilos podrías imaginar otros atributos como: peso actual, el
número de dientes actuales (irá perdiendo algunos a lo largo de su vida), el
número de presas que ha cazado hasta el momento, etc.
Como puedes ver, un objeto puede ser cualquier cosa que puedas describir en
términos de atributos y acciones.
3
1.2. El concepto de clase.
Es aquí donde entra en escena el concepto de clase. Está claro que no podemos
definir la estructura y el comportamiento de cada objeto cada vez que va a ser
utilizado dentro de un programa, pues la escritura del código sería una tarea
interminable y redundante. La idea es poder disponer de una plantilla o modelo
para cada conjunto de objetos que sean del mismo tipo, es decir, que tengan los
mismos atributos y un comportamiento similar.
• Los atributos que van a ser comunes a todos los objetos que pertenezcan
a esa clase (información).
En unidades anteriores ya se indicó que para declarar una clase en Java se usa
la palabra reservada class. En la declaración de una clase vas a encontrar:
4
• Cabecera de la clase. Compuesta por una serie de modificadores de
acceso, la palabra reservada class y el nombre de la clase.
5
El mismo código copiable:
class Punto {
// Atributos
int x,y;
// Métodos
int obtenerX () { return x; }
int obtenerY() {return y;}
void establecerX (int vx) { x= vx; };
void establecerY (int vy) { y= vy; };
}
En este caso se trata de una clase muy sencilla en la que el cuerpo de la clase
(el área entre las llaves) contiene el código y las declaraciones necesarias para
que los objetos que se construyan (basándose en esta clase) puedan funcionar
apropiadamente en un programa (declaraciones de atributos para contener el
estado del objeto y métodos que implementen el comportamiento de la clase y
los objetos creados a partir de ella).
Si te fijas en los distintos programas que se han desarrollado en los ejemplos
de las unidades anteriores, podrás observar que cada uno de esos programas
era en sí mismo una clase Java: se declaraban con la palabra reservada class y
contenían algunos atributos (variables) así como algunos métodos (como
mínimo el método main).
En el ejemplo anterior hemos visto lo mínimo que se tiene que indicar en la
cabecera de una clase (el nombre de la clase y la palabra reservada class).
Se puede proporcionar bastante más información mediante modificadores y
otros indicadores como por ejemplo el nombre de su superclase (si es que
esa clase hereda de otra), si implementa algún interfaz y algunas cosas más
que irás aprendiendo poco a poco.
6
A la hora de implementar una clase Java (escribirla en un archivo con un editor
de textos o con alguna herramienta integrada como por ejemplo Netbeans o
Eclipse) debes tener en cuenta:
• Por convenio, se ha decidido que en lenguaje Java los nombres de las
clases deben de empezar por una letra mayúscula. Así, cada vez que
observes en el código una palabra con la primera letra en mayúscula
sabrás que se trata de una clase sin necesidad de tener que buscar su
declaración. Además, si el nombre de la clase está formado por
varias palabras, cada una de ellas también tendrá su primera
letra en mayúscula. Siguiendo esta recomendación, algunos ejemplos
de nombres de clases podrían ser: Recta, Circulo, Coche,
CocheDeportivo, Jugador, JugadorFutbol, AnimalMarino, AnimalAcuatico,
etc.
7
Una lista separada por comas de interfaces que son implementadas por la
clase, precedida por la palabra reservada implements ("implementa").
[modificadores]
class <NombreClase> [extends <NombreSuperClase>][implements
<NombreInterface1>] [[implements <NombreInterface2>] ...] {
class Punto {
La herencia y las interfaces las verás más adelante. Vamos a ver ahora
cuáles son los modificadores que se pueden indicar al crear la clase y qué
efectos tienen. Los modificadores de clase son:
8
• Modificador final. Indica que no podrás crear clases que hereden de ella.
También volverás a este modificador cuando estudies el concepto de
herencia. Los modificadores final y abstract son excluyentes (sólo se
puede utilizar uno de ellos).
En el ejemplo anterior de la clase Punto tendríamos una clase que sería sólo visible
(utilizable) desde el mismo paquete en el que se encuentra la clase
(modificador de acceso por omisión o de paquete, o package). Desde fuera de
ese paquete no sería visible o accesible. Para poder utilizarla desde cualquier
parte del código del programa bastaría con añadir el atributo public: public
class Punto.
Es decir, dos valores de tipo entero. Cualquier objeto de la clase Punto que sea
creado almacenará en su interior dos números enteros (x e y). Cada objeto
diferente de la clase Punto contendrá sendos valores x e y, que podrán
coincidir o no con el contenido de otros objetos de esa misma clase Punto.
Por otro lado, la clase Punto también definía una serie de métodos:
Cada uno de esos métodos puede ser llamado desde cualquier objeto que sea
una instancia de la clase Punto. Se trata de operaciones que permiten
manipular los datos (atributos) contenidos en el objeto bien para calcular otros
datos o bien para modificar los propios atributos.
Cada vez que se produce una instancia de una clase (es decir, se crea un
objeto de esa clase), se desencadenan una serie de procesos (construcción del
objeto) que dan lugar a la creación en memoria de un espacio físico que
constituirá el objeto creado. De esta manera cada objeto tendrá sus propios
miembros a imagen y semejanza de la plantilla propuesta por la clase.
Por otro lado, podrás encontrarte con ocasiones en las que determinados
miembros de la clase (atributos o métodos) no tienen demasiado sentido como
partes del objeto, sino más bien como partes de la clase en sí (partes de la
plantilla, pero no de cada instancia de esa plantilla). Por ejemplo, si creamos
una clase Coche y quisiéramos disponer de un atributo con el nombre de la
clase (un atributo de tipo String con la cadena "Coche"), no tiene mucho
sentido replicar ese atributo para todos los objetos de la clase Coche, pues
para todos va a tener siempre el mismo valor (la cadena "Coche"). Es más, ese
atributo puede tener sentido y existencia al margen de la existencia de
cualquier objeto de tipo Coche. Podría no haberse creado ningún objeto de la
clase Coche y sin embargo seguiría teniendo sentido poder acceder a ese
atributo de nombre de la clase, pues se trata en efecto de un atributo de la
propia clase más que de un atributo de cada objeto instancia de la clase.
10
5.B. Atributos.
1. Atributos.
Los atributos pueden ser de cualquier tipo de los que pueda ser cualquier otra
variable en un programa en Java: desde tipos elementales como int, boolean
o float hasta tipos referenciados como arrays, Strings u objetos.
11
public int x;
public int y;
De esta manera estarías indicando que ambos atributos son públicos, es decir,
accesibles por cualquier parte del código programa que tenga acceso a un
objeto de esa clase.
Ejemplos:
int x;
public int elementoX, elementoY;
private int x, y, z;
static double descuentoGeneral;
final bool casado;
Te suena bastante, ¿verdad? La declaración de los atributos en una clase es exactamente igual a la
declaración de cualquier variable tal y como has estudiado en las unidades anteriores y similar a
como se hace en cualquier lenguaje de programación. Es decir mediante la indicación del tipo y a
continuación el nombre del atributo, pudiéndose declarar varios atributos del mismo tipo mediante
una lista de nombres de atributos separada por comas (exactamente como ya has estudiado al
declarar variables).
12
Dentro de la declaración de un atributo puedes encontrar tres partes:
• Tipo. Indica el tipo del atributo. Puede tratarse de un tipo primitivo (int,
char, bool, double, etc) o bien de uno referenciado (objeto, array, etc.).
Como puedes observar, los atributos de una clase también pueden contener
modificadores en su declaración (como sucedía al declarar la propia clase).
Estos modificadores permiten indicar cierto comportamiento de un atributo a la
hora de utilizarlo. Entre los modificadores de un atributo podemos distinguir:
Ejercicio resuelto
14
Con estos dos puntos (x1, y1) y (x2, y2) se puede definir perfectamente la
ubicación de un rectángulo en el plano.
Escribe una clase que contenga todos esos atributos teniendo en cuenta que
queremos que sea una clase visible desde cualquier parte del programa y que
sus atributos sean también accesibles desde cualquier parte del código.
Solución:
Dado que se trata de una clase que podrá usarse desde cualquier parte del
programa, utilizaremos el modificador de acceso public para la clase:
Los cuatro atributos que necesitamos también han de ser visibles desde
cualquier parte, así que también se utilizará el modificador de acceso public
para los atributos:
• Modificador static. Hace que el atributo sea común para todos los
objetos de una misma clase. Es decir, todas las clases compartirán ese
mismo atributo con el mismo valor. Es un caso de miembro estático o
miembro de clase: un atributo estático o atributo de clase o
variable de clase.
15
expresiones geométricas (figuras, superficies, volúmenes, etc.) y necesitas
utilizar muy a menudo la constante pi con abundantes cifras significativas, por
ejemplo, 3.14159265. Utilizar esa constante literal muy a menudo puede
resultar tedioso además de poco operativo (imagina que el futuro hubiera que
cambiar la cantidad de cifras significativas). La idea es declararla una sola vez,
asociarle un nombre simbólico (un identificador) y utilizar ese identificador
cada vez que se necesite la constante. En tal caso puede resultar muy útil
declarar un atributo final con el valor 3.14159265 dentro de la clase en la que
se considere oportuno utilizarla. El mejor identificador que podrías utilizar para
ella será probablemente el propio nombre de la constante (y en mayúsculas,
para seguir el convenio de nombres), es decir, PI.
class claseGeometria {
// Declaración de constantes
public final float PI= 3.14159265;
Como ya has visto, el modificador static hace que el atributo sea común (el
mismo) para todos los objetos de una misma clase. En este caso sí podría
decirse que la existencia del atributo no depende de la existencia del objeto,
sino de la propia clase y por tanto sólo habrá uno, independientemente del
número de objetos que se creen. El atributo será siempre el mismo para todos
los objetos y tendrá un valor único independientemente de cada objeto. Es
más, aunque no exista ningún objeto de esa clase, el atributo sí existirá y
podrá contener un valor (pues se trata de un atributo de la clase más que
del objeto).
16
Uno de los ejemplos más habituales (y sencillos) de atributos estáticos o de
clase es el de un contador que indica el número de objetos de esa clase que
se han ido creando. Por ejemplo, en la clase de ejemplo Punto podrías incluir
un atributo que fuera ese contador para llevar un registro del número de
objetos de la clase Punto que se van construyendo durante la ejecución del
programa.
class Punto {
// Coordenadas del punto
private int x, y;
// Atributos de clase: cantidad de puntos creados hasta el momento
public static cantidadPuntos;
public static final nombre;
Obviamente, para que esto funcione como estás pensando, también habrá que
escribir el código necesario para que cada vez que se cree un objeto de la clase
Punto se incremente el valor del atributo cantidadPuntos. Volverás a este
ejemplo para implementar esa otra parte cuando estudies los constructores.
Ejercicio resuelto
Solución:
Por último, hay que tener en cuenta que se desea que la clase sólo sea
accesible desde el interior del paquete al que pertenece, por tanto habrá que
utilizar el modificador por omisión o de paquete. Esto es, no incluir ningún
modificador de acceso en la cabecera de la clase.
18
5.C. Métodos.
1.1. Declaración de un método.
1. Métodos.
Como ya has visto anteriormente, los métodos son las herramientas que nos
sirven para definir el comportamiento de un objeto en sus interacciones con
otros objetos. Forman parte de la estructura interna del objeto junto con los
atributos.
En el proceso de declaración de una clase que estás estudiando ya has visto
cómo escribir la cabecera de la clase y cómo especificar sus atributos dentro
del cuerpo de la clase. Tan solo falta ya declarar los métodos, que estarán
también en el interior del cuerpo de la clase junto con los atributos.
Los métodos suelen declararse después de los atributos. Aunque atributos y
métodos pueden aparecer mezclados por todo el interior del cuerpo de la clase
es aconsejable no hacerlo para mejorar la claridad y la legibilidad del
código. De ese modo, cuando echemos un vistazo rápido al contenido de una
clase, podremos ver rápidamente los atributos al principio (normalmente
ocuparán menos líneas de código y serán fáciles de reconocer) y cada uno de
los métodos inmediatamente después. Cada método puede ocupar un número
de líneas de código más o menos grande en función de la complejidad del
proceso que pretenda implementar.
Los métodos representan la interfaz de una clase. Son la forma que tienen
otros objetos de comunicarse con un objeto determinado solicitándole cierta
información o pidiéndole que lleve a cabo una determinada acción. Este modo
de programar, como ya has visto en unidades anteriores, facilita mucho la
tarea al desarrollador de aplicaciones, pues le permite abstraerse del contenido
de las clases haciendo uso únicamente del interfaz (métodos).
19
1.1. Declaración de un método.
• Cabecera del método, que contiene el nombre del método junto con el
tipo devuelto, un conjunto de posibles modificadores y una lista de
parámetros.
• Los paréntesis.
int obtenerX ()
{
// Cuerpo del método
...
}
Donde:
20
1.2. Cabecera de método.
21
excepciones separadas por comas. No es obligatorio que un método
incluya una lista de excepciones, aunque muchas veces será
conveniente. En unidades anteriores ya has trabajado con el concepto de
excepción y más adelante volverás a hacer uso de ellas.
Como sucede con todos los identificadores en Java (variables, clases, objetos,
métodos, etc.), puede usarse cualquier identificador que cumpla las normas.
Ahora bien, para mejorar la legibilidad del código, se ha establecido el
siguiente convenio para nombrar los métodos: utilizar un verbo en
minúscula o bien un nombre formado por varias palabras que
comience por un verbo en minúscula, seguido por adjetivos, nombres,
etc. los cuales sí aparecerán en mayúsculas.
22
Algunos ejemplos de métodos que siguen este convenio podrían ser: ejecutar,
romper, mover, subir, responder, obtenerX, establecerValor, estaVacio,
estaLleno, moverFicha, subirPalanca, responderRapido, girarRuedaIzquierda,
abrirPuertaDelantera, CambiarMarcha, etc.
* int obtenerX ()
* int obtenerY ()
• Modificadores de contenido. Son también los mismos que en el caso de los atributos
(static y final) junto con, aunque su significado no es el mismo.
• Otros modificadores (no son aplicables a los atributos, sólo a los métodos): abstract,
native, synchronized.
23
Un método final es un método que no permite ser sobrescrito por las clases
descendientes de la clase a la que pertenece el método. Volverás a ver este
modificador cuando estudies en detalle la herencia.
<tipo> <nombreMetodo> ( )
Vargars.
26
Sentencia de devolución del valor de retorno (return). Aparecerá al final
del método y es la que permite devolver la información que se le ha pedido al
método. Es la última parte del proceso y la forma de comunicarse con la parte
de código que llamó al método (paso de mensaje de vuelta). Esta sentencia de
devolución siempre tiene que aparecer al final del método. Tan solo si el tipo
devuelto por el método es void (vacío) no debe aparecer (pues no hay que
devolver nada al código llamante).
int obtenerX ()
{
return x;
}
27
int obtenerY ()
{
return y;
}
Además de esos dos métodos, la clase también disponía de otros dos que
sirven para la función opuesta (establecerX y establecerX). Veamos uno de
ellos:
Ejercicio resuelto
28
• Método desplazar, que mueve la ubicación del rectángulo en el plano en
una cantidad X (para el eje X) y otra cantidad Y (para el eje Y). Se trata
simplemente de sumar el desplazamiento X a las coordenadas x1 y x2, y
el desplazamiento Y a las coordenadas y1 e y2. Los parámetros de
entrada de este método serán por tanto X e Y, de tipo double.
• Método obtenerNumRectangulos, que devuelve el número de
rectángulos creados hasta el momento.
Solución
return nombre;
nombre= nom;
29
variables locales para almacenar los cálculos intermedios (como la base o la
altura).
30
Por último, el método obtenerNumRectangulos simplemente debe devolver
el valor del atributo numRectangulos. En este caso es razonable plantearse
que este método podría ser más bien un método de clase (estático) más que
un método de objeto, pues en realidad es una característica de la clase más
que algún objeto en particular. Para ello tan solo tendrías que utilizar el
modificador de acceso static:
**----------------------------------------------------------------
* Clase Rectangulo
----------------------------------------------------------------
*/
public class Rectangulo {
// Atributos de clase
private static int numRectangulos;
// Número total de rectángulos creados
public static final String nombreFigura= "Rectángulo"; //
Nombre de la clase
public static final double PI= 3.1416;
// Constante PI
// Atributos de objeto
private String nombre; // Nombre del rectángulo
public double x1, y1; // Vértice inferior izquierdo
public double x2, y2; // Vértice superior derecho
// Método obtenerNombre
public String obtenerNombre () {
return nombre;
}
// Método establecerNombre
public void establecerNombre (String nom) {
nombre= nom;
}
// Método CalcularSuperficie
public double CalcularSuperficie () {
double area, base, altura;
// Cálculo de la base
base= x2-x1;
31
// Cálculo de la altura
altura= y2-y1;
// Método CalcularPerimetro
public double CalcularPerimetro () {
double perimetro, base, altura;
// Cálculo de la base
base= x2-x1;
// Cálculo de la altura
altura= y2-y1;
// Método desplazar
public void desplazar (double X, double Y) {
// Desplazamiento en el eje X
x1= x1 + X;
x2= x2 + X;
// Desplazamiento en el eje X
y1= y1 + Y;
y2= y2 + Y;
}
// Método obtenerNumRectangulos
public static int obtenerNumRectangulos () {
return numRectangulos;
}
En principio podrías pensar que un método puede aparecer una sola vez en la
declaración de una clase (no se debería repetir el mismo nombre para varios
métodos). Pero no tiene porqué siempre suceder así. Es posible tener varias
32
versiones de un mismo método (varios métodos con el mismo nombre) gracias
a la sobrecarga de métodos.
Imagínate que estás desarrollando una clase para escribir sobre un lienzo que
permite utilizar diferentes tipografías en función del tipo de información que se
va a escribir. Es probable que necesitemos un método diferente según se vaya
a pintar un número entero (int), un número real (double) o una cadena de
caracteres (String). Una primera opción podría ser definir un nombre de
método diferente dependiendo de lo que se vaya a escribir en el lienzo. Por
ejemplo:
33
La posibilidad que te ofrece la sobrecarga es utilizar un mismo nombre para
todos esos métodos (dado que en el fondo hacen lo mismo: pintar). Pero para
poder distinguir unos de otros será necesario que siempre exista alguna
diferencia entre ellos en las listas de parámetros (bien en el número de
parámetros, bien en el tipo de los parámetros). Volviendo al ejemplo anterior,
podríamos utilizar un mismo nombre, por ejemplo pintar, para todos los
métodos anteriores:
34
Dado que this es una referencia a la propia clase en la que te encuentras en
ese momento, puedes acceder a sus atributos mediante el operador punto (.)
como sucede con cualquier otra clase u objeto. Por tanto, en lugar de poner el
nombre del atributo (que estos casos haría referencia al parámetro), podrías
escribir this.nombreAtributo, de manera que el compilador sabrá que te
estás refiriendo al atributo y se eliminará la ambigüedad.
En algunos casos puede resultar útil hacer uso de la referencia this aunque no
sea necesario, pues puede ayudar a mejorar la legibilidad del código.
Ejercicio resuelto
Solución
35
1.8. Sobrecarga de operadores.
En algunos casos puede resultar útil para ayudar a mejorar la legibilidad del
código, pues esos operadores resultan muy intuitivos y pueden dar una idea
rápida de cuál es su funcionamiento.
Dado que en este módulo se está utilizando el lenguaje Java para aprender a
programar, no podremos hacer uso de esta funcionalidad. Más adelante,
cuando aprendas a programar en otros lenguajes, es posible que sí tengas la
posibilidad de utilizar este recurso.
37
String enteroCadena= String.valueOf (23).
• static String valueOf (float f). Algo similar para un valor de tipo
float. Ejemplo de uso:
Todos los ejemplos anteriores son casos en los que se utiliza directamente la clase como una especie
de caja de herramientas que contiene métodos que pueden ser utilizados
desde cualquier parte, por eso suelen ser métodos públicos.
Puedes echar un vistazo a algunas clases del paquete java.lang (por ejemplo
Integer, String, Float, Double, Boolean y Math) y observar la gran
cantidad de métodos estáticos que ofrecen para ser utilizados sin necesidad de
tener que crear objetos de esas clases:
Package java.lang.
38
una clase completa (declaración de la clase, declaración de sus atributos y
declaración de sus métodos), vamos a hacer un repaso general de las opciones
de visibilidad (control de acceso) que has estudiado.
39
• Público (modificador public), igual que en el caso global de la clase y
con el mismo significado (miembro visible desde cualquier parte del
código).
Los atributos de una clase suelen ser declarados como privados a la clase o,
como mucho, protected (accesibles también por clases heredadas), pero no
como public. De esta manera puedes evitar que sean manipulados
inadecuadamente (por ejemplos modificarlos sin ningún tipo de control) desde
el exterior del objeto.
En estos casos lo que se suele hacer es declarar esos atributos como privados
o protegidos y crear métodos públicos que permitan acceder a esos atributos.
Si se trata de un atributo cuyo contenido puede ser observado pero no
modificado directamente, puede implementarse un método de "obtención" del
atributo (en inglés se les suele llamar método de tipo get) y si el atributo
puede ser modificado, puedes también implementar otro método para la
modificación o "establecimiento" del valor del atributo (en inglés se le suele
llamar método de tipo set). Esto ya lo has visto en apartados anteriores.
Si recuerdas la clase Punto que hemos utilizado como ejemplo, ya hiciste algo
así con los métodos de obtención y establecimiento de las coordenadas:
private int x, y;
// Métodos get
public int obtenerX () { return x; }
public int obtenerY () { return y; }
// Métodos set
public void establecerX (int x) { this.x= x; }
40
public void establecerY (int y) { this.y= y; }
Así, para poder obtener el valor del atributo x de un objeto de tipo Punto será
necesario utilizar el método obtenerX() y no se podrá acceder directamente al
atributo x del objeto.
También pueden darse casos en los que no interesa que pueda observarse
directamente el valor de un atributo, sino un determinado procesamiento o
cálculo que se haga con el atributo (pero no el valor original). Por ejemplo
podrías tener un atributo DNI que almacene los 8 dígitos del DNI pero no la
letra del NIF (pues se puede calcular a partir de los dígitos). El método de
acceso para el DNI (método getDNI) podría proporcionar el DNI completo (es
decir, el NIF, incluyendo la letra), mientras que la letra no es almacenada
realmente en el atributo del objeto. Algo similar podría suceder con el dígito
de control de una cuenta bancaria, que puede no ser almacenado en el
objeto, pero sí calculado y devuelto cuando se nos pide el número de cuenta
completo.
41
En el ejemplo anterior de objetos que contienen un DNI, será necesario
calcular la letra correspondiente a un determinado número de DNI o
comprobar si una determinada combinación de número y letra forman un DNI
válido. Este tipo de cálculos y comprobaciones podrían ser implementados en
métodos privados de la clase (o al menos como métodos protegidos).
Vamos a intentar implementar una clase que incluya todo lo que has visto
hasta ahora. Se desea crear una clase que represente un DNI español y que
tenga las siguientes características:
42
* public void establecer (String nif) throws ...
* public void establecer (int dni) throws ...
Solución
Está claro que para poder trabajar con los DNI/NIF vas a necesitar
implementar el algoritmo para calcular la letra de un número de DNI. Para ello
puedes crear un método (que en principio podría ser privado) que realice ese
cálculo. Para facilitar la implementación de ese método, crearemos un arrray
estático y constante (final) con las letras posibles que puede tener un NIF y en
el orden adecuado para la aplicación del algoritmo de cálculo de la letra
(algoritmo conocido como módulo 23):
43
Este método estático ha sido definido como privado, aunque también podría
haber sido definido como público para que otros objetos pudieran hacer uso de
él (típico ejemplo de uso de un método estático).
Una vez que disponemos de todos estos métodos es bastante sencillo escribir
un método de comprobación de la validez de un NIF:
44
if (letra_leida == letra_calculada) { // Comparamos la
letra extraída con la calculada
// Todas las comprobaciones han resultado válidas. El
NIF es válido.
valido= true;
}
else {
valido= false;
}
}
return valido;
}
* DNI.extraerLetraNIF.
* DNI.extraerNumeroNIF.
* DNI.calcularLetraNIF.
Y por último tan solo quedarían por implementar los métodos públicos (la
interfaz):
45
cadenaNIF= Integer.toString(numDNI) +
String.valueOf(letraNIF);
// Devolución del resultado
return cadenaNIF;
}
/**---------------------------------------------------------------
-
* Clase DNI
----------------------------------------------------------------
*/
46
private static final String LETRAS_DNI=
"TRWAGMYFPDXBNJZSQVHLCKE";
// Atributos de objeto
private int numDNI;
// Métodos
// Comprobación de rangos
if (dni>999999 && dni<99999999) {
this.numDNI= dni; // Valor válido: lo almacenamos
}
else { // Valor inválido: lanzamos una excepción
throw new Exception ("DNI inválido: " +
String.valueOf(dni));
}
}
47
private static char calcularLetraNIF (int dni) {
char letra;
48
}
return valido;
}
}
Una vez que ya tienes implementada una clase con todos sus atributos y
métodos, ha llegado el momento de utilizarla, es decir, de instanciar objetos
de esa clase e interaccionar con ellos. En unidades anteriores ya has visto
cómo declarar un objeto de una clase determinada, instanciarlo con el
operador new y utilizar sus métodos y atributos.
Creating Objects.
Using Objects.
<tipo> nombreVariable;
En este caso el tipo será alguna clase que ya hayas implementado o bien
alguna de las proporcionadas por la biblioteca de Java o por alguna otra
biblioteca escrita por terceros.
Por ejemplo:
Punto p1;
Rectangulo r1, r2;
Coche cocheAntonio;
String palabra;
Esas variables (p1, r1, r2, cocheAntonio, palabra) en realidad son referencias
(también conocidas como punteros o direcciones de memoria) que apuntan
(hacen "referencia") a un objeto (una zona de memoria) de la clase indicada
en la declaración.
49
Como ya estudiaste en la unidad dedicada a los objetos, un objeto recién
declarado (referencia recién creada) no apunta a nada. Se dice que la
referencia está vacía o que es una referencia nula (la variable objeto contiene
el valor null). Es decir, la variable existe y está preparada para guardar una
dirección de memoria que será la zona donde se encuentre el objeto al que
hará referencia, pero el objeto aún no existe (no ha sido creado o instanciado).
Por tanto se dice que apunta a un objeto nulo o inexistente.
Para que esa variable (referencia) apunte realmente a un objeto (contenga una
referencia o dirección de memoria que apunte a una zona de memoria en la
que se ha reservado espacio para un objeto) es necesario crear o instanciar el
objeto. Para ello se utiliza el operador new.
Ejercicio resuelto
Utilizando la clase Rectangulo implementada en ejercicios anteriores, indica como declararías tres
objetos (variables) de esa clase llamados r1, r2, r3.
Solución
Rectangulo r1;
Rectangulo r2:
Rectangulo r3:
También podrías haber declarado los tres objetos en la misma sentencia de declaración:
50
De la tarea de reservar memoria para la estructura del objeto (sus atributos
más alguna otra información de carácter interno para el entorno de ejecución)
se encarga el propio entorno de ejecución de Java. Es decir, que por el hecho
de ejecutar un método constructor, el entorno sabrá que tiene que realizar una
serie de tareas (solicitud de una zona de memoria disponible, reserva de
memoria para los atributos, enlace de la variable objeto a esa zona, etc.) y se
pondrá rápidamente a desempeñarlas.
Ampliar el ejercicio anterior instanciando los objetos r1, r2, r3 mediante el constructor por defecto.
Solución
Habría que añadir simplemente una sentencia de creación o instanciación (llamada al constructor
mediante el operador new) por cada objeto que se desee crear:
<nombreObjeto>.<nombreMiembro>
Por ejemplo, en el caso de los objetos de tipo Punto que has declarado e
instanciado en los apartados anteriores, podrías acceder a sus miembros de la
siguiente manera:
Es decir, colocando el operador punto (.) a continuación del nombre del objeto
y seguido del nombre del miembro al que se desea acceder.
Ejercicio resuelto
52
Utilizar el ejemplo de los rectángulos para crear un rectángulo r1, asignarle los valores x1=0, y1=0,
x2=10, y2=10, calcular su área y su perímetro y mostrarlos en pantalla.
Solución
Se trata de declarar e instanciar el objeto r1, rellenar sus atributos de ubicación (coordenadas de las
esquinas), e invocar a los métodos calcularSuperficie y calcularPerimetro
utilizando el operador punto (.). Por ejemplo:
Rectangulo.java
package ejemplorectangulos01;
/**---------------------------------------------------------------
-
* Clase Rectangulo
----------------------------------------------------------------
*/
public class Rectangulo {
// Atributos de clase
private static int numRectangulos;
// Número total de rectángulos creados
public static final String nombreFigura= "Rectángulo"; //
Nombre de la clase
public static final double PI= 3.1416;
// Constante PI
// Atributos de objeto
private String nombre; // Nombre del rectángulo
public double x1, y1; // Vértice inferior izquierdo
public double x2, y2; // Vértice superior derecho
// Método obtenerNombre
public String obtenerNombre () {
return nombre;
}
53
// Método establecerNombre
public void establecerNombre (String nom) {
nombre= nom;
}
// Método CalcularSuperficie
public double CalcularSuperficie () {
double area, base, altura;
// Cálculo de la base
base= x2-x1;
// Cálculo de la altura
altura= y2-y1;
// Método CalcularPerimetro
public double CalcularPerimetro () {
double perimetro, base, altura;
// Cálculo de la base
base= x2-x1;
// Cálculo de la altura
altura= y2-y1;
// Método desplazar
public void desplazar (double X, double Y) {
// Desplazamiento en el eje X
x1= x1 + X;
x2= x2 + X;
// Desplazamiento en el eje X
y1= y1 + Y;
y2= y2 + Y;
54
// Método obtenerNumRectangulos
public static int obtenerNumRectangulos () {
return numRectangulos;
}
EjemploRectangulos01.java
/*
Ejemplo de uso de la clase Rectangulo
*/
package ejemplorectangulos01;
/**
*
* Programa Principal (clase principal)
*/
public class EjemploRectangulos01 {
}
}
55
5.F. Constructores.
Como ya has estudiado en unidades anteriores, en el ciclo de vida de un objeto
se pueden distinguir las fases de:
56
Un constructor es un método que tiene el mismo nombre que la clase a la
que pertenece y que no devuelve ningún valor tras su ejecución. Su función es
la de proporcionar el mecanismo de creación de instancias (objetos) de la
clase.
Por tanto para crear un nuevo objeto es necesario realizar una llamada a un
método constructor de la clase a la que pertenece ese objeto. Ese proceso se
realiza mediante la utilización del operador new.
57
Una vez que incluyas un constructor personalizado a una clase, el compilador
ya no incluirá el constructor por defecto (sin parámetros) y por tanto si
intentas usarlo se produciría un error de compilación. Si quieres que tu clase
tenga también un constructor sin parámetros tendrás que escribir su código
(ya no lo hará por ti el compilador).
• El tipo de acceso.
• Si lanza o no excepciones.
Reflexiona
58
Si se ha creado un constructor con parámetros y no se ha implementado el
constructor por defecto, el intento de utilización del constructor por defecto
producirá un error de compilación (el compilador no lo hará por nosotros).
Un ejemplo de utilización del constructor que has creado para la clase Punto en
el apartado anterior podría ser:
Punto p1;
p1= new Punto (10, 7);
59
Para saber más
Ejercicio resuelto
1. Un constructor sin parámetros (para sustituir al constructor por defecto) que haga que los
valores iniciales de las esquinas del rectángulo sean (0,0) y (1,1);
2. Un constructor con cuatro parámetros, x1, y1, x2, y2, que rellene los valores iniciales de los
atributos del rectángulo con los valores proporcionados a través de los parámetros.
3. Un constructor con dos parámetros, base y altura, que cree un rectángulo donde el vértice
inferior derecho esté ubicado en la posición (0,0) y que tenga una base y una altura tal y
como indican los dos parámetros proporcionados.
Solución
En el caso del primer constructor lo único que hay que hacer es "rellenar" los atributos x1, y1, x2,
y2 con los valores 0, 0, 1, 1:
public Rectangulo ()
{
x1= 0.0;
y1= 0.0;
x2= 1.0;
y2= 1.0;
}
Para el segundo constructor es suficiente con asignar a los atributos x1, y1, x2, y2 los valores de los
parámetros x1, y1, x2, y2. Tan solo hay que tener en cuenta que al tener los mismos nombres los
parámetros del método que los atributos de la clase, estos últimos son ocultados por los primeros y
para poder tener acceso a ellos tendrás que utilizar el operador de autorrerferencia this:
public Rectangulo (double x1, double y1, double x2, double y2)
{
this.x1= x1;
this.y1= y1;
this.x2= x2;
this.y2= y2;
}
En el caso del tercer constructor tendrás que inicializar el vértice (x1, y1) a (0,0) y el vértice (x2,y2)
a (0 + base, 0 + altura), es decir a (base, altura):
Rectangulo.java
package ejemplorectangulos02;
/**----------------------------------------------------------------
* Clase Rectangulo.
* Incluye constructores.
----------------------------------------------------------------*/
public class Rectangulo {
// Atributos de objeto
private String nombre; // Nombre del rectángulo
public double x1, y1; // Vértice inferior izquierdo
public double x2, y2; // Vértice superior derecho
//-----------------
// Constructores
//-----------------
public Rectangulo ()
{
x1= 0.0;
y1= 0.0;
x2= 1.0;
y2= 1.0;
}
public Rectangulo (double x1, double y1, double x2, double y2)
{
this.x1= x1;
this.y1= y1;
this.x2= x2;
this.y2= y2;
}
//-------------------------------
// Métodos estáticos (de clase)
//-------------------------------
61
// Métodos de estáticos públicos
// -----------------------------
// Método obtenerNumRectangulos
public static int obtenerNumRectangulos () {
return numRectangulos;
}
//-------------------
// Métodos de objeto
//-------------------
//Métodos públicos
//-----------------
// Método obtenerNombre
public String obtenerNombre () {
return nombre;
}
// Método establecerNombre
public void establecerNombre (String nom) {
nombre= nom;
}
// Método CalcularSuperficie
public double CalcularSuperficie () {
double area, base, altura;
// Cálculo de la base
base= x2-x1;
// Cálculo de la altura
altura= y2-y1;
// Método CalcularPerimetro
public double CalcularPerimetro () {
double perimetro, base, altura;
// Cálculo de la base
base= x2-x1;
// Cálculo de la altura
altura= y2-y1;
// Método desplazar
public void desplazar (double X, double Y) {
62
// Desplazamiento en el eje X
x1= x1 + X;
x2= x2 + X;
// Desplazamiento en el eje X
y1= y1 + Y;
y2= y2 + Y;
}
}
EjemploRectangulos02.java
/*
* Ejemplo de uso de la clase Rectangulo con constructores
*/
package ejemplorectangulos02;
/**
*
* Programa Principal (clase principal)
*/
public class EjemploRectangulos02 {
}
}
Una forma de iniciar un objeto podría ser mediante la copia de los valores de
los atributos de otro objeto ya existente. Imagina que necesitas varios objetos
63
iguales (con los mismos valores en sus atributos) y que ya tienes uno de ellos
perfectamente configurado (sus atributos contienen los valores que tú
necesitas). Estaría bien disponer de un constructor que hiciera copias idénticas
de ese objeto.
En este caso el constructor recibe como parámetro un objeto del mismo tipo
que el que va a ser creado (clase Punto), inspecciona el valor de sus atributos
(atributos x e y), y los reproduce en los atributos del objeto en proceso de
construcción (this).
En este caso el objeto p2 se crea a partir de los valores del objeto p1.
Ejercicio resuelto
Solución
64
Se trata de añadir un nuevo constructor además de los tres que ya habíamos creado:
// Constructor copia
public Rectangulo (Rectangulo r) {
this.x1= r.x1;
this.y1= r.y1;
this.x2= r.x2;
this.y2= r.y2;
}
Para usar este constructor basta con haber creado anteriormente otro Rectangulo para utilizarlo
como base de la copia. Por ejemplo:
Por otro lado, esta forma de funcionar del entorno de ejecución de Java
(destrucción de objetos no referenciados mediante el recolector de basura)
implica que no puedas saber exactamente cuándo un objeto va a ser
definitivamente destruido, pues si una variable deja de ser referenciada (se
cierra el ámbito de ejecución donde fue creada) no implica necesariamente que
sea inmediatamente borrada, sino que simplemente es marcada para que el
recolector la borre cuando pueda hacerlo.
System.runFinalization ();
Este método se encarga de llamar a todos los métodos finalize de todos los
objetos marcados por el recolector de basura para ser destruidos.
66