La creación de una clase consiste en declarar ésta y todos los elementos que la componen.
a. Declaración de la clase
La declaración de una clase se lleva a cabo utilizando la palabra clave class seguida del nombre de la clase y de un bloque de código delimitado por los caracteres { y } (llaves). En este bloque de código se encuentran las declaraciones de variables que serán los campos de la clase y las funciones que serán los métodos de la clase. Se pueden añadir varias palabras clave para modificar las características de la clase. Por lo tanto, la sintaxis de la declaración de una clase es la siguiente.
{
Código de la clase }
public class Persona {
}
Los signos [ y ] (corchetes) son utilizados para indicar qué elemento es opcional. No se deben utilizar en el código de declaración de una clase.
Los modificadores permiten determinar la visibilidad de la clase y cómo ser utilizada. A continuación presentamos la lista de los modificadores disponibles:
public: indica que la clase puede ser utilizada por cualquier otra clase. Sin este modificador, la clase sólo será utilizable por clases que forman parte del mismo paquete.
abstract: indica que la clase es abstracta y no puede ser instanciada. Sólo se la puede utilizar como clase básica en una relación de herencia. En este tipo de clase, sólo se suele definir las declaraciones de métodos, y habrá que escribir el contenido de los métodos en las clases derivadas.
final: la clase no puede ser utilizada como clase de base en una relación de herencia y sólo puede ser instanciada.
Al ser contradictorio el significado de las palabras clave abstract y final, su uso simultáneo está prohibido.
Para indicar que su clase recupera las características de otra clase por una relación de herencia, debe utilizar la palabra clave extends seguida del nombre de la clase base. También puede implementar en su clase una o varias interfaces utilizando la palabra implements seguida de la lista de las interfaces implementadas. Se detallarán estas dos nociones más adelante en este capítulo.
El inicio de la declaración de nuestra clase Persona es, por lo tanto, el siguiente:
Se debe introducir obligatoriamente este código en un fichero que lleve el mismo nombre que la clase y la extensión .java.
b. Creación de los campos
Ahora, nos vamos a interesar en el contenido de nuestra clase. Debemos crear los diferentes campos de la clase. Para ello, basta con declarar variables en el interior del bloque de código de la clase e indicar la visibilidad de la variable, su tipo y su nombre.
[private | protected | public] tipoDeLaVariable nombreDeLaVariable; La visibilidad de la variable responde a las reglas siguientes:
private: la variable sólo es accesible en la clase donde está declarada.
protected: la variable es accesible en la clase donde está declarada, en las otras clases que forman parte del mismo paquete y en las clases que heredan la clase donde esa misma variable está declarada.
public: la variable es accesible desde cualquier ubicación.
Si no se proporciona ninguna información relativa a la visibilidad, la variable es accesible desde la clase donde está declarada y desde las otras clases que forman parte del mismo paquete. Cuando
http://www.eni-training.com/client_net/mediabook.aspx?idR=65883 3/19
[modificadores] tipoDeRetorno nombreDelMétodo ([listaDeLosParámetros]) [throws listaExcepción]
{ }
public class Persona {
private String apellido; private String nombre;
private GregorianCalendar fecha_naci; }
elejimos la visibilidad de una variable, debemos respetar en lo posible el principio de encapsulación y limitar al máximo la visibilidad de las variables. Lo ideal sería tener siempre variables private oprotected pero nunca public.
La variable debe también tener un tipo. No hay limite en cuanto al tipo de una variable y por lo tanto podemos utilizar tanto los tipos básicos del lenguaje Java tales como int, float, char… como tipos de objetos.
En cuanto al nombre de la variable, debe respetar sencillamente las reglas de nombramiento (no utilizar palabras clave del lenguaje).
Ahora, por lo tanto, la clase Persona tiene la forma siguiente:
c. Creación de métodos
Los métodos son simplemente funciones definidas en el interior de una clase. Se suelen utilizar para manipular los campos de la clase. La sintaxis general de declaración de un método está descrita a continuación.
Java cuenta con los siguientes modificadores:
private: indica que el método sólo puede ser usado en la clase donde está definido.
protected: indica que sólo se puede utilizar el método en la clase donde está definido, en las subclases de esta clase y en las otras clases que forman parte del mismo paquete.
public: indica que se puede utilizar el método desde cualquier otra clase.
Si no se utiliza ninguna de estas palabras, entonces la visibilidad se limitará al paquete donde está definida la clase.
static: indica que el método es un método de clase.
abstract: indica que el método es abstracto y que no contiene código. La clase donde está definido también debe ser abstracta.
final: indica que el método no puede ser sobrescrito en una subclase.
native: indica que el código del método se encuentra en un fichero externo escrito en otro lenguaje.
synchronized: indica que el método sólo puede ser ejecutado por un único hilo a la vez.
El tipo de retorno puede ser cualquier tipo de dato, tipo básico del lenguaje o tipo objeto. Si el método no tiene información a devolver, deberemos usar la palabra clave void en sustitución del tipo de retorno.
public class Persona {
private String apellido; private String nombre;
private GregorianCalendar fecha_naci;
public long calculoEdad() { long edad; fecha_naci=new GregorianCalendar(1963,11,29); edad=new GregorianCalendar().getTimeInMillis()- fecha_naci.getTimeInMillis(); edad=edad/1000/60/60/24/365; return edad; }
public void visualización() { System.out.println("apellido: " + apellido); System.out.println("nombre: " + nombre); System.out.println("edad: " + calculoEdad()); } }
public void visualización(boolean español) { if (español) { System.out.println("apellido: " + apellido); System.out.println("nombre: " + nombre); System.out.println("edad: " + calculoEdad()); } else
La lista de los parámetros es idéntica a una lista de declaración de variables. Hay que especificar el tipo y el nombre del parámetro. Si se esperan varios parámetros, hay que separar sus declaraciones con una coma. Incluso si no se espera ningún parámetro, los paréntesis son obligatorios.
La palabra clave throws indica la lista de excepciones que este método puede lanzar durante su ejecución.
Añadimos dos métodos a nuestra clase Persona.
En algunos lenguajes de programación, no es posible tener varias funciones con el mismo nombre. El lenguaje Java, como muchos otros lenguajes orientados a objeto, permite esquivar el problema al crear funciones sobrecargadas. Una función sobrecargada lleva el mismo nombre que otra función de la clase pero presenta una firma diferente. Se toma en cuenta la información siguiente para determinar la firma de una función:
el nombre de la función
el número de parámetros esperados por la función el tipo de los parámetros.
Para poder crear una función sobrecargada, hace falta que al menos uno de sus elementos cambie respeto a una función ya existente. Como el nombre de la función debe seguir siendo el mismo para poder hablar de sobrecarga, sólo podemos actuar en el número de parámetros o su tipo. Por ejemplo, podemos añadir la función siguiente a la clase Persona:
http://www.eni-training.com/client_net/mediabook.aspx?idR=65883 5/19
{
System.out.println("name: " + apellido); System.out.println("first name: " + nombre); System.out.println("age: " + calculoEdad()); }
}
public void visualización(boolean mayúscula) { if (mayúscula) { System.out.println("apellido: " + apellido.toUpperCase()); System.out.println("nombre: " + nombre.toUpperCase()); System.out.println("edad: " + calculoEdad()); } else { System.out.println("apellido: " + apellido.toLowerCase()); System.out.println("nombre: " + nombre.toLowerCase()); System.out.println("edad: " + calculoEdad()); } }
public void visualización(String...colores) { { if (colores==null) { System.out.println("ningun color"); return; } switch (colores.length) { case 1: System.out.println("un color"); break; case 2: System.out.println("dos colores"); break; case 3: System.out.println("tres colores");
En efecto, posee una firma diferente de la primera función visualización que hemos creado ya que espera un parámetro de tipo boolean. Si ahora añadimos la función siguiente, el compilador rechaza la compilación del código.
De hecho determina que dos funciones tienen rigurosamente la misma firma, el mismo nombre, el mismo número de parámetros, incluso el mismo tipo de parámetros. Este ejemplo nos muestra también que el nombre de los parámetros no se tiene en cuenta para determinar la firma de una función.
Se puede diseñar una función para aceptar un número variable de parámetros. La primera solución consiste en usar como parámetro un array y comprobar en el código de la función el tamaño de dicho array para obtener los parámetros. Sin embargo, esta solución requiere la creación de un array en el momento de la llamada a la función. Por lo tanto no es tan fácil como el uso de una lista de parámetros. Para simplificar la llamada a este tipo de función, podemos utilizar la declaración siguiente para indicar que una función espera un número cualquiera de parámetros.
break; default:
System.out.println("más de tres colores"); } } } p.visualización("rojo"); p.visualización("verde","azul","rojo"); p.visualización();
public class Persona {
En el interior del método, el parámetro colores se considera como un array (de cadenas de caracteres en nuestro caso).
Por el contrario, durante la llamada a la función, utilizamos una lista de cadenas de caracteres separadas por comas. Las sintaxis siguientes son perfectamente válidas para la llamada a esta función.
Sólo hay una pequeña anomalía en el momento de la ejecución, porque la última llamada de la función visualización no ejecuta la función que acabamos de diseñar sino la primera versión que no espera parámetro. En efecto, el compilador no puede adivinar que se trata de la versión que espera un número variable de parámetros que deseamos ejecutar sin pasarle parámetros. Por lo tanto para indicarle nuestra intención, debemos llamar a la función con la sintaxis siguiente.
p.visualización(null);
En el momento de la llamada a una función, los parámetros son pasados por valores tanto para los tipos básicos del lenguaje (int, float, boolean…) como para los tipos objetos. Sin embargo, si un objeto es pasado como parámetro a una función, el código de la función tiene acceso a los campos del objeto y puede por lo tanto modificar los valores. Por el contrario, si el código de la función modifica la referencia hacia el objeto, ésta volverá a ser establecida después del retorno de la función.
Hay que resaltar que todo este comportamiento es uniforme incluso si los campos de la clase están declarados como private, porque estamos en el interior de la clase.
d. Los métodos accesores
La declaración de los atributos con una visibilidad privada es una buena práctica para respetar el principio de encapsulación. Sin embargo, esta solución es limitativa ya que sólo el código de la clase donde están declarados puede acceder a ello. Para paliar este problema, debemos establecer unos métodos accesores. Son funciones ordinarias que simplemente tienen como meta hacer visibles a los campos desde el exterior de la clase. Por convención, las funciones encargadas de asignar un valor a un campo se llaman set seguido del nombre del campo, las funciones encargadas de proporcionar el valor del campo se llaman get seguido del nombre del campo. Si el campo es de tipo boolean, el prefijo get se sustituye por el prefijo is. Si un campo debe ser de sólo lectura, el accesor set no debe estar disponible; si un campo debe ser de sólo escritura, entonces se debe omitir la función get. Con esta técnica, podemos controlar el uso que se hace de los campos de una clase. Por lo tanto, podemos modificar la clase Persona al añadirle algunas reglas de gestión.
El apellido se debe escribir en mayúscula. El nombre se debe escribir en minúscula.
http://www.eni-training.com/client_net/mediabook.aspx?idR=65883 7/19
private String apellido; private String nombre;
private GregorianCalendar fecha_naci;
public String getApellido() {
return apellido; }
public void setApellido(String a) {
apellido = a.toUpperCase(); }
public String getNombre() {
return nombre; }
public void setNombre(String n) { nombre = n.toLowerCase(); } } public Persona() { apellido=""; nombre=""; fecha_naci=null; }
public Persona(String a,String n,GregorianCalendar f) { apellido=a; nombre=n; p.setApellido("garcía"); p.setNombre("josé"); System.out.println(p.getApellido()); System.out.println(p.getNombre());
Ahora, los campos de la clase son accesibles desde el exterior mediante estos métodos.
e. Constructores y destructores
Los constructores son métodos particulares de una clase por diferentes aspectos. El constructor es un método que lleva siempre el mismo nombre que la propia clase. No devuelve ningún tipo, ni siquiera void. No se le llama nunca de manera explícita en el código sino de manera implícita a la creación de una instancia de clase. Como en el caso de un método clásico, un constructor puede esperar parámetros. El constructor de una clase que no espera parámetros es designado como el constructor por defecto de la clase. El papel principal del constructor es la inicialización de los campos de una instancia de clase. Como para los otros métodos de una clase, también se puede sobrecargar los constructores. La creación de un constructor por defecto no es obligatoria ya que el compilador proporciona uno automáticamente. Este constructor efectúa simplemente una llamada al constructor de la superclase que, por supuesto, debe existir. Al contrario, si su clase contiene un constructor sobrecargado, debe también poseer un constructor por defecto. Una buena costumbre consiste en crear siempre un constructor por defecto en cada una de sus clases. Añadimos constructores a la clase Persona.
fecha_naci=f; }
protected void finalize() throws Throwable {
}
public class Persona
Los destructores son otros métodos particulares de una clase. De la misma manera que los constructores, son llamados implícitamente pero únicamente durante la destrucción de una instancia de clase. Se impone la firma del destructor. Este método debe ser protected, no devuelve ningún valor, se llama obligatoriamente finalize, no toma ningún parámetro y es susceptible de activar una excepción de tipo Throwable. Con motivo de esta firma impuesta, sólo puede haber un único destructor para una clase, y por lo tanto, la sobrecarga no es posible para los destructores.
Así pues, la declaración de un destructor es la siguiente:
El código presente en el destructor debe permitir la liberación de recursos utilizados por la clase. En ello podemos encontrar, por ejemplo, código que cierra un fichero abierto por la clase o el cierre de una conexión hacia un servidor de base de datos. Veremos en detalles en el párrafo Destrucción de una instancia, las circunstancias en las cuales se llama al destructor.
f. Campos y métodos estáticos
Los miembros estáticos son campos o métodos que son accesibles por la propia clase o por cualquier instancia de la clase. También, en algunos lenguajes se habla de miembros compartidos. Son muy útiles cuando es necesario gestionar, en una clase, información que no es específica a una instancia de la clase sino a la propia clase. En oposición con los miembros de instancia para los cuales existe un ejemplar por instancia de la clase, los miembros estáticos existen en un único ejemplar. La modificación del valor de un miembro de instancia sólo modifica el valor para esta instancia de clase mientras que la modificación del valor de un miembro estático modifica el valor para todas las instancias de la clase. Los miembros estáticos son asimilables a variables globales en una aplicación. Son utilizables en el código haciendo referencia a ello por el nombre de la clase o gracias a una instancia de la clase. No se aconseja esta segunda solución ya que no demuestra el hecho de que estamos trabajando con un miembro estático.
Los métodos estáticos siguen las mismas reglas y pueden ser útiles en la creación de bibliotecas de funciones. El ejemplo clásico es la clase Math ya que cuenta con un gran número de funciones estáticas. Los métodos estáticos poseen sin embargo una limitación, y es que sólo pueden utilizar variables locales u otros miembros estáticos de la clase. Nunca deben usar miembros de instancia de una clase porque puede ocurrir que el método sea utilizado sin que exista una instancia de la clase. El compilador detectará esta situación y lo indicará: non-static variable cannot be referenced from a static context.
Los miembros estáticos deben ser declarados con la palabra clave static. Como para cualquier otro miembro de una clase, podemos especificar una visibilidad. En cambio, una variable local a una función no puede ser estática.
Para ilustrar la utilización de los miembros estáticos, vamos a añadir a la clase Persona un campo numérico. El valor de este campo se forma automáticamente en la creación de cada instancia de la clase y será único para cada instancia. Los constructores de nuestra clase están perfectamente adaptados para llevar a cabo este trabajo. En cambio, tenemos que memorizar cuántas instancias han sido creadas para poder asignar un único número a cada instancia. Una variable estática privada se encargará de esta operación. A continuación, le presentamos el código correspondiente.
http://www.eni-training.com/client_net/mediabook.aspx?idR=65883 9/19
{
private String apellido; private String nombre;
private GregorianCalendar fecha_naci;
// campo privado representando el número de la Persona private int numero;
// campo estático privado representando el contador de Personas private static int numInstancias;
public String getApellido() {
return apellido; }
public void setApellido(String a) {
apellido = a.toUpperCase(); }
public String getNombre() {
return nombre; }
public void setNombre(String n) {
nombre = n.toLowerCase(); }
// método de instancia permitiendo obtener el número de una Persona public int getNumero()
{
return numero; }
// método estático permitiendo obtener el número de instancias creadas public static int getNumInstancias()
{ return numInstancias; } public Persona() { apellido=""; nombre=""; fecha_naci=null;
// creación de una nueva Persona y por lo tanto incrementación del contador
numInstancias++;
// asignación a la nueva Persona de su número numero=numInstancias;
} }
g. Las anotaciones
Se utilizan las anotaciones para añadir información adicional a un elemento. Esta información no surte ningún efecto en el código pero puede ser utilizada por el compilador, por la máquina virtual que se encargará de la ejecución de la aplicación o por ciertas herramientas de desarrollo. Se pueden aplicar a una clase, un campo, o a un método. Debe ser especificada antes del elemento al cual se refiere. Una anotación viene precedida por el símbolo @ y está seguida del nombre de la anotación. El compilador reconoce tres tipos de anotaciones que van a permitir la modificación de su comportamiento en el momento de la compilación del código.
public class Persona {
private String apellido; private String nombre;
private GregorianCalendar fecha_naci; private int número;
private static int numInstancias; ...
@Deprecated
public void visualización() { System.out.println("apellido: " + apellido); System.out.println("nombre: " + nombre); System.out.println("edad: " + calculoEdad()); } ... }
javac -Xlint:deprecation Principale.java
Principale.java:16: warning: [deprecation] visualización() in Persona has been deprecated
p.visualización(); ˆ
1 warning
public class Client extends Persona {
@Override
public long calculoEdad() {
... ...
@Deprecated se utiliza para indicar que un método ya no debe ser utilizado. Es el caso, por ejemplo, en el que decidimos hacer evolucionar un método y deseamos que no se use más la versión anterior. Esta anotación no cambia el resultado de la compilación pero añade información adicional al código