Documentos de Académico
Documentos de Profesional
Documentos de Cultura
- Modificadores y genéricos 1
GUIONES DE CLASE
PROGRAMACIÓN JAVA
Iñaki Martín
CAPÍTULO 9
MODIFICADORES
©
Y CLASES GENERICAS
Temario de curso JavaSE
© Iñaki Martín
9.- Modificadores y genéricos Ambito (scope) de una variable 2
a) Una variable de instancia, cuyo ámbito es static public int intAleatorio (int min, int max) {
int sol = (int) (Math.random () * (max - min + 1) + min);
toda la clase, return sol;
}
b) Una variable de método, cuyo ámbito es public static int calcular (int valorparametro) {
todo el metodo, // Aqui dentro, var1 esta viva, pero fuera de ámbito
// No se puede acceder a var1, pero si a varglobal
c) Una variable de bloque cuyo ámbito es int varlocal;
if (varglobal < valorparametro) {
todo el bloque varlocal = varglobal++;
int res = varglobal;
return res;
✴ Viva, fuera de ámbito. Puede darse por ejemplo }
return 0;
durante la ejecución de un método, y desde éste }
la invocación a otro método. Durante la ejecución public static void main (String[] args) {
int res = calcular (10);
del segundo método, una variable local del primer // A estas alturas varlocal ya no existe, esta muerta
método no está visible, pero esta viva, no está int var1 = intAleatorio (10, 50);
int h1 = intAleatorio (1 , 60);
muerta, y volverá a estar visible cuando el segundo // varglobal sigue existiendo aqui
• Esta lista explica los valores por defecto que toman diferentes tipos de variables, según donde se declaren:
Tipos de variables Valores que toman
arrays null
Variables locales (a Tanto primitivas como referencias a objetos, NO tienen valor por
defecto.
Temario de curso JavaSE
métodos o a bloques)
Una vez declaradas y sin asignacion, no tienen valor alguno, ni
siquiera null
public static void main (String[] args) {
Date unDate;
if (unDate == null) // da ERROR, pues unDate ni siquiera es null
System.out.println("unDate es null");
}
Modificadores de acceso
• Son unas palabras reservadas de Java que indican donde son visibles los elementos a los que afectan
• Se pueden aplicar a las clases, a los atributos y metodos, y a las variables de un método
Default (nada) : Se puede acceder al elemento desde dentro del mismo paquete
Iñaki Martín
Private : Solo se puede acceder al elemento desde dentro de la clase, no pueden ser
llamados desde fuera de la clase
Public : Se puede acceder desde cualquier paquete.
Protected : Es como private, pero sí pueden acceder a ellos las clases que
hereden de la clase donde se define el protected.
©
static
La palabra static se usa para identificar que un miembro de la clase pertenece únicamente a la clase, y no a los diferentes
objetos (instancias) que de dicha clase se creen en el futuro. Según donde apliquemos static hace cosas diferentes :
✓ Al igual que los métodos de clase, un atributo de clase existe y puede utilizarse aunque no existan objetos de
la clase. Se puede leer su valor con el nombre de la clase directamente: NombreClase.atributo
Temario de curso JavaSE
✓ Cualquier objeto puede acceder y modificar este valor, pero si uno lo modifica lo modifica para todos
✓ Dado que los atributos son accesibles a nivel de clase, y sin necesidad de instancias nada, los atributos
estáticos no deben inicializarse al crear un objeto, sino al cargar la clase.
✓ Se usa para tener control de conceptos que afectan a toda la clase. Un ejemplo es el de contar las instancias
que hay de la propia clase (incluyendo un atributo static que haga de contador, en el constructor de la clase)
✴ atributos de instancia (o variables de instancia) son variables sin el uso de static (como se han visto
normalmente hasta ahora los atributos),
✓ En este caso se crea una variable distinta para cada instancia (ocupan diferente espacio en memoria).
✴ constante de clase es una constante (final) combinada con static, static final.
✓ En este caso se crea un atributo común para todos los objetos de esa clase, y fijo (no se puede modificar
su valor una vez asignado, ni pueden ser sobrescritos ni redefinidos). Además, siempre debe ser inicializada
en el momento de declararse.
9.- Modificadores y genéricos Modificador static (II) 6
• Para usar un método estático no es necesario instanciar un objeto de la clase. Puesto que existe una copia
única de el, se puede invocar directamente con el simple nombre de la clase: NombreClase.metodo()
Iñaki Martín
Ya se ha visto el uso de métodos estáticos antes: Integer.parseInt(), Math.abs() (de clases Math e Integer:)
• Como se pueden llamar sin crear un objeto, dentro de un método static no se pueden usar variables
normales de clase (por que puede que no exista ni una instancia!), pues estas se crean al crear un objeto.
Por eso si se llama a miembros externos del metodo estos han de ser siempre static
©
Temas avanzados
} Un método static no puede ser sobrescrito. Vale.
// constructor. Pero eso no impide que pueda ser redefinido en
public ClaseConEstaticos() {
numInstancias ++; una clase heredada: En tal caso no es
System.out.println (“Num. actual de instancias: "+ numeroInstancias); sobrescritura (ni siquiera sobrecarga), es una
} nueva definición del método (como si fueran
} clases diferentes sin ninguna relación)
9.- Modificadores y genéricos Modificador static (III) 7
import static
• Se trata de una operativa similar a la de una import normal, solo que afecta solo a los miembros estáticos
de clase (un import normal importa clases enteras con todos sus miembros).
• Con ello, se puede luego usar un miembro estático sin necesidad de especificar el espacio de
nombres en el que se encuentran. import static java.lang.Math.*;
Iñaki Martín
}
}
bloques static
• Se puede incluir en una clase un bloque static, que no es mas que un bloque (delimitado por llaves) precedido
Temario de curso JavaSE
de la palabra static. Este bloque se ejecutará una sola vez, cuando se carga la clase en memoria (no cuando
se instancia un objeto, sino mucho antes, antes justo de usar por vez primera la clase, y antes de cualquier
constructor de la misma)
public class Pruebas {
public static void main (String[] args) {
System.out.println (“Linea desde el main"); /* — Ejemplo de ejecución del programa:
MiClaseCoche p1 = new MiClaseCoche (); Linea desde el main
MiClaseCoche p2 = new MiClaseCoche (); Linea desde el bloque static
} Linea desde el constructor de la clase
} Linea desde el constructor de la clase */
class MiClaseCoche {
static {
System.out.println ("Linea desde el bloque static");
}
public MiClaseCoche () {
System.out.println ("Linea desde el constructor de la clase”);
}
}
9.- Modificadores y genéricos Modificador static (IV) 8
de la clase (pero no dentro de un método, esos son simples bloques), y sin el identificador static por delante..
Iñaki Martín
• Este bloque se ejecuta en cada instancia de objeto que se crea, justo después de la llamada a super() del
constructor (sea implícita o explícita), pero por lo tanto, antes de la ejecución del código del propio constructor
• Puede haber más de un bloque de inicialización en una clase, y en este caso, el orden en que se encuentran
sí que importa, pues es el orden en que se ejecutan.
©
Es importante destacar también que private convierte los elementos en privados para otras clases, pero no
para otras instancias (objetos) de la misma clase. Dicho de otro modo, un objeto de una determinada clase
puede acceder a los miembros privados de otro objeto de la misma clase, así que esto es perfectamente válido:
Temario de curso JavaSE
class ejemploClase1 {
private int atributo1 = 0;
• protected : indica que los elementos sólo pueden ser accedidos desde su mismo paquete (como el acceso por
defecto) y desde cualquier clase que extienda la clase en que se encuentra, independientemente de si esta se
encuentra en el mismo paquete o no. Este modificador, como private, no tiene sentido a nivel de clases o interfaces
no internas.
• (default): el tipo por defecto (si no se indica uno de los anteriores), que no tiene ninguna palabra clave asociada,
pero se suele conocer como default o package-private.Consiste en que el elemento puede ser accedido sólo desde
las clases que están en el mismo paquete que el elemento que modificamos.
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (II) 10
strictfp
• strictfp es un modificador de lo más esotérico, muy poco utilizado y conocido cuyo nombre procede de strict
floating point, o punto flotante estricto.
• Su uso sobre una clase, interfaz o método sirve para mejorar su portabilidad haciendo que los cálculos con
números flotantes se restrinjan a los tamaños definidos por el estándar de punto flotante de la IEEE (float y
Iñaki Martín
double), en lugar de aprovechar toda la precisión que la plataforma en la que estemos corriendo el programa
pudiera ofrecernos.
• No es aconsejable su uso a menos que sea estrictamente necesario.
©
native
Temario de curso JavaSE
• native es un modificador utilizado cuando un determinado método está escrito en un lenguaje distinto a
Java, normalmente C, C++ o ensamblador para mejorar el rendimiento.
• Sólo se puede aplicar a métodos.
• La forma más común de implementar estos métodos es utilizar JNI (Java Native Interface).
transient
• transient es utilizado para indicar que los atributos de un objeto no son parte persistente del objeto o bien
que estos no deben guardarse y restaurarse utilizando el mecanismo de serialización estándar.
• Esto puede afectar a la hora de guardar objetos serializados en ficheros, que pueden tener atributos
transiten…
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (IV) 11
volatile
• volatile se usa en el tratamiento de atributos en hilos. No debe confundirse con synchronized, que es uno de
los mecanismos de sincronización básicos de Java.
• volatile se usa sobre los atributos de los objetos para indicar al compilador que es posible que dicho atributo
vaya a ser modificado por varios Threads de forma simultánea y asíncrona, y que no queremos guardar una
Iñaki Martín
copia local del valor para cada Thread a modo de caché, sino que queremos que los valores de todos los
Threads estén sincronizados en todo momento, asegurando así la visibilidad del valor siempre
actualizado, a costa de un pequeño impacto en el rendimiento.
• volatile es más simple y más sencillo que synchronized, lo que implica también un mejor rendimiento. Sin
©
embargo volatile, a diferencia de synchronized, no proporciona atomicidad, lo que puede hacer que sea
más complicado de utilizar.
Temario de curso JavaSE
• Esto es, un atributo que sea volatile, puede efectuar operaciones no atomices sin control de sincronismo.
• Una operación como el incremento, por ejemplo, no es atómica (la operación se divide en realidad en 3
instrucciones distintas: primero se lee la variable, después se incrementa, y por último se actualiza el valor).
Así que un incremento de un atributo volatile no puede asegurar que el acceso al dato sea continuo en las tres
operaciones sin interferencia de otros hilos.
• Sin embargo, en las operaciones atómicas, volatile actúa como un synchronized, bloqueando el acceso a la
variable (asignacion, lectura, etc)
• Si necesitamos asegurar atomicidad podemos usar synchronized (a nivel de atributo o método, ver capítulo
de Procesos)
• Comentar que ambos pueden aplicarse sobre atributos, pero si los atributos son primitivos, sólo puede usarse
volatile
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (V) 12
abstract
Como ya se dijo en el capítulo de POO, abstract se puede aplicar a:
• una clase: en tal caso, la clase puede (o no) incluir métodos abstractos.
✴ Una clase abstracta no puede ser instanciada
✴ Una clase abstracta puede (y debe) tener constructores. Aunque no se instancia, una clase abstracta al ser
heredada necesitará que sus subclases llamen a super() en su constructor, así que debe tener un constructor. De
hecho, la JVM añadirá uno por defecto si no se incluye uno explícitamente
• un método: es un método declarado sin implementarse, sin llaves, acabado en punto y coma
✴ Un método abstracto en una clase obliga a esta a ser declarada abstracta
©
Son similares en que no puedes instanciar ninguna de las dos y tienen métodos abstractos, y se diferencian en que los
atributos de la interfaz solo pueden ser public, static, y final, pero, cuando haya dudas… ¿Cuál usar?
• Mejor usar clases abstractas si:
◦ Se desea compartir código entres varias clases similares.
◦ Las clases que hereden la clase abstracta tienen muchos methods comunes o necesitan acceso a los
miembros mas restrictivo que publico (protected o private).
◦ Se desea declarar miembros no static's ni final. This enables you to define methods that can access and
modify the state of the object to which they belong.
• Mejor usar interfaces si:
◦ Deseas generar métodos a usar por clases muy diferentes (las interfaces Comparable y Cloneable por
ejemplo son usadas por muchas clases muy distintas).
◦ Se necesita explicar el comportamiento de un modelo de dato específico, sin importar quien lo vaya a usar
después.
◦ Se necesita multiherencia.
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (VI) 13
final
• Indica que una variable, método o clase no se va a modificar, o se quiere proteger para no permitir que se modifique
• Si se desea que una clase no pueda ser heradada, se añade, al declararla, el modificador final delante de class
Iñaki Martín
• Una variable declarada final no pueda cambiar de referencia, pero SI DE VALOR. Más en detalle:
a. Una variable de tipo primitivo, declarada final, no puede cambiar el valor (pues guardan solo valor, no referencia)
b. Una variable de un tipo objeto, declarada final, quiere decir que no puede cambiar su referencia, pero sí el objeto al que apunta la referencia. Esto
se resume en que no se puede reasignar (usar el operador = ) con un objeto final. Pero sí cambiar el contenido del objeto al que referencia:
final Persona p = new Persona();
Persona q = new Persona();
p = q; // ERROR no puedo cambiar la referencia
p.setNombre ( “JUAN”) ; // OK ! Puedo actualizar el contenido de la referencia
9.- Modificadores y genéricos Resumen static, final, abstract, public, private…. 14
Resumen de modificadores
aplicado a un El atributo es único para todos los objetos El atributo no se puede cambiar (es una
Iñaki Martín
Clases Genéricas
• Cuando se crean objetos de algunas clases, como por ejemplo colecciones, puede no tener que indicarse el tipo de dato que
se almacena, puedo hacer como una colección genérica donde meter cualquier cosa. Por ejemplo:
• Claro que, si al declarar la colección, se indica el tipo, ya no se puede meter en la colección más que lo que haya declarado
ArrayList<String> b = new ArrayList (); // Se define la coleccion para que solo contenga String
©
b.add ("HOLA");
// b.add(123); // Dazor pues el array es de String
• Hay que tener en cuenta que en una colección con un tipo definido, gracias al polimorfismo, puedo meter dentro de dicha
Temario de curso JavaSE
• Bien, esto con clases de JSE, como estas colecciones. ¿Pero y con mis clases?. Cuando se han creado clases, hasta ahora,
no tenían un “tipo” de dato asociado. El tipo de dato lo tenia asignado cada miembro (atributo o método) en particular.
• Lo que se hace con ArrayList en el primer ejemplo, esto es, NO decir que tipo de dato va a almacenar, no se puede hacer con
los miembros de mi clase. Éstos han de tener un tipo. Pero lo que se puede hacer es no usar un tipo específico, sino
uno genérico.
• ¿Y esto de genérico, cuando se convierte en un tipo real? Pues será a la hora de crear posteriormente objetos de
nuestra clase cuando se decida qué tipo de datos va a almacenar.
• Se trata de usar los llamados Prototipos o Tipos Genéricos cuando se trabaje con la clase,
36
9.- Modificadores y genéricos Clases genéricas (II) 16
¿Cómo crear clases genéricas? Usando un marcado <T> tras el nombre de la clase y al usar sus miembros. Ejemplo:
class MiClaseSinTipo<T> { // Con esta T indico que luego al crear objetos diré qué tipo de dato es T
// T es un tipo “formal” que luego será sustituido por el tipo “real” cuando se cree un objeto.
private T informacion; // Ese tipo de dato T será el que tendrá mi atributo “info”
this.informacion = informacion;
}
public T getInfo () { // igual que el parámetro de devuelve el getter
return this.informacion;
Iñaki Martín
}
public void setInfo (T informacion) { // o el parámetro que necesita el setter
this.informacion = informacion;
}
public String toString ( // aquí devuelve el toString() del atributo, sea cual sea éste
return (informacion != null) ? informacion.toString () : null;
}
}
©
Y será luego, al crear los objetos, cuando definamos qué tipo de dato vamos a usar… en cada objeto que creemos.
Temario de curso JavaSE
// Al crear un objeto de la clase MiClaseSinTipo, es cuando le digo que tipo voy a usar sustituyendo a T
MiClaseSinTipo<String> miObjetoString = new MiClaseSinTipo<String> ("HOLA");
String data = miObjetoString.getInformacion();
System.out.println (data); // imprime HOLA
// Ahora en vez de un String como antes, uso un Integer para que sustituya el tipo formal T
MiClaseSinTipo<Integer> miOBjetoInteger = new MiClaseSinTipo<Integer> (3456);
int s = miOBjetoInteger.getInformacion();
System.out.println (s); // imprime 3456
System.out.println (data2);
System.out.println (s2);
CUIDADO: Al usar tipos genéricos, se necesita que la referencia sean objetos, por lo que NO se admiten tipos primitivos
(int, double,… ), se deben usar sus clases envoltorio (Integer, Double, …)
9.- Modificadores y genéricos Clases genéricas (III) 17
• Cuidado, al usar el atributo dentro de la propia clase, hay que tener en cuenta que no esta definido que tipo
tiene, por lo que en el ejemplo anterior, no puedo concatenar un elemento con un String, no puedo hacer
public T getInfo () {
return this.informacion + " con sufijo"; // DA ERROR, no sabe si informacion es String
• ¿Y cómo sería un método, fuera de mi clase, en el que estemos utilizando genéricos? Pues debemos en la
Iñaki Martín
• Tener en cuenta, claro, que he llamado al método con el objeto miObjetoString creado como
MiClaseSinTipo<String>, que es lo que pide el método como parámetro, si hubiera usado miObjetoInteger en la
llamada, no me hubiera dejado ni compilar.
• Existen una serie de convenciones para nombrar a los genéricos (aunque se puede usar lo que se quiera):
E – Element (usado bastante por Java Collections Framework)
K – Key (Llave, usado en mapas)
N – Number (para números)
T – Type (Representa un tipo, es decir, una clase)
V – Value (representa el valor, también se usa en mapas)
S,U,V etc. – usado para representar otros tipos.
NOTA : También se puede usar más de un argumento de tipo: class MiClaseSinTipos <T,U> { …. }
38
9.- Modificadores y genéricos Clases genéricas (IV) 18
• Esto quiere decir que el objeto unVehiculo podrá ser un ArrayList de “cualquier cosa (el ?) que herede de Vehículo”
• Hay que tener en cuenta que eso es muy útil en casos como el siguiente. Si tenemos estas clases:
class ClaseMadre { class ClaseHija extends ClaseMadre {
String aa; String bbb;
public ClaseMadre(String aa) { public ClaseHija(String aa, String bbb) {
©
• Si cambiamos la ultima linea por esta; ArrayList < ? extends ClaseMadre> ob = new ArrayList<ClaseHija>();
ahora ya sí funciona.
• Otro ejemplo, recordando (o aprendiendo) que Integer es una subclase de Number,
ArrayList < Integer> miLista = new ArrayList<Integer>( ); // NO compila siquiera, mientras que
ArrayList < ? extends Integer> miLista = new ArrayList<Integer>( ); // SI funciona correctamente
9.- Modificadores y genéricos Clases genéricas (V) 19
Es decir, esto:
List<Object>
sólo admite objetos que pertenezcan a la clase Object (no a clases creadas por un usuario), y no podemos hacer lo siguiente:
List<Object> miLista = new List< MiClaseA >( ); // no compila, MiClaseA es una clase mia, de usuario, no de Object
• Si usamos el tipo comodín, hay que cuidado pues el comodín representa cualquier tipo de dato, por lo que no se puede
añadir elementos de un tipo específico a una colección que contenga un tipo marcado con comodín, pues no sabe que
tipo ha de añadir (consultar y recuperar datos si es posible, o usar como paso de parámetros)
Métodos Genéricos
• No solo se pueden declarar clases genéricas, tambien se pueden crear métodos genéricos. Si tenemos este ejemplo donde
una clase Utilidades tiene un metodo que nos dice si un elemento dado esta o no en un array:
class Utilidades {
public boolean existe(int[] elementos, int elemento) {
boolean res = false;
Iñaki Martín
• ¿Como hacer para que el metodo funcione con parametros, no de tipo int, sino genericos? Pues añadiendo la marca del tipo
generico antes del tipo de retorno del metodo, y usando dicho tipo generico ya directamente en el metodo, tanto en sus
parametros como en su codigo:
Temario de curso JavaSE
class Utilidades {
// MARCAR EL GENERICO (<T> O EL QUE SEA) ANTES DEL TIPO DE RETORNO DEL METODO
public <T> boolean existe(T[] elementos, T elemento) {
boolean res = false;
for (int i = 0; i < elementos.length && (!res); i++) {
if (elementos[i] == elemento) res = true;
}
return res;
}
}
• En Java NO se pueden crear arrays de tipo generico. En Java los arrays (a direfencia de los genericos) necesitan saber su tipo
en tiempo de ejecucion, asi que se necesita saber el tipo cuando se crea el array.
class Baul<T> {
private T nombre; // Puedo crear atributos de tipo generico
Iñaki Martín
}
}
9.- Modificadores y genéricos Reglas a aplicar en modificadores de acceso 22
1. Si el miembro de una superclase es publico, sus subclases lo van a heredar, independientemente de en que paquete
esten superclase y subclase (o los import que haya)
Iñaki Martín
acceder a un miembro protected usando una referencia a una instancia de la superclase, o dicho de otro modo,
Temas avanzados
class InitError {
Temas avanzados
En este ejemplo la variable xxx aun no esta dimensionada, y da error, pero no lanza un NullPointerException, sino una
©
ExceptionInitializerError
Temario de curso JavaSE
Protected y herencia.
Cuidado: Las subclases acceden a los miembros protegidos a través de la herencia, no pudiendo utilizar una referencia a un
objeto de la superclase para acceder al miembro protegido.
package claseMobiliario;
public class Mobiliario {
protected int precio;
Temas avanzados