Sunteți pe pagina 1din 41

nli s detallado

de mtodos
clases
Conceptos fundamentales:
Controlar el acceso a miembros.
Pasar objetos a un mtodo y devolver objetos desde un mtodo.
Sobrecargar mtodos y constructores.
Usar recursin.
Aplicar s t a tic.
Usar clases internas.
Usar vara g s .
Este captulo retoma el examen de clases y mtodos. Comienza con la descripcin de
cmo controlar el acceso a los miembros de una clase. Tras ello, veremos cmo pasar y
devolver objetos, sobrecargar mtodos, la recursin y el uso de la palabra clave s ta t ic.
Tambin veremos clases anidadas y argumentos de longitud variable.
Controlar el acceso a los miembros de una clase
Debido a su compatibilidad con la encapsulacin, una clase ofrece dos ventajas princi-
pales. Por un lado, vincula datos al cdigo para manipularlas. Hemos usado este aspecto
desde el captulo 4. Por otra parte, proporciona el medio para poder controlar el acceso a
f ..lt!tl 6. Anlisis detallado de mtodos y clases
miembros, funcin que explicaremos a continuacin. Aunque el enfoque de Java sea ms
complicado, en esencia, hay dos tipos bsicos de miembros de clase: pblicos y privados.
Un miembro pblico es accesible para el cdigo definido fuera de su clase. Es el tipo de
miembro que hemos usado hasta ahora. Un miembro privado slo es accesible por otros
mtodos definidos por su clase. El acceso se controla a travs de miembros privados.
La restriccin del acceso a los miembros de una clase es una parte fundamental de la
programacin orientada a objetos ya que evita el uso incorrecto del objeto. Al permitir
el acceso a datos privados solamente a travs de un conjunto de mtodos bien definidos,
puede impedir que se asignen valores inadecuados a dichos datos, por ejemplo realizando
una comprobacin de tipos. El cdigo externo a la clase no puede establecer directamente
el valor de un miembro privado. Tambin puede controlar cmo y cundo se usan los
datos dentro de un objeto. Por tanto, si se implementa correctamente, una clase crea una
caja negra que se puede usar, pero sin acceso a su funcionamiento interno.
Hasta el momento, no nos hemos preocupado del control de acceso ya que Java cuenta
con una configuracin de acceso predeterminada en la que los miembros de una clase
estn disponibles para otro cdigo del programa. (Por tanto, dicha configuracin es bsi-
camente pblica.) Aunque es til para clases sencillas (y programas de ejemplo como
los de este libro), esta configuracin predeterminada resulta inadecuada para muchas
situaciones reales. Aprenderemos a usar otras funciones de control de acceso de Java.
Modificadores de acceso de Java
El control de acceso a miembros se consigue a travs de tres modificadores de acceso:
public, pri va t e y protected. Corno hemos mencionado, si no se usa un modifi-
cador de acceso, se asume la configuracin de acceso predeterminada. En este captulo
nos centraremos en public y p riva te. El modificador protect e d slo se aplica en
casos de herencia, corno veremos en el captulo 8.
Al modificar un miembro de una clase con el especificador publ i e, cualquier cdigo
del programa puede acceder a ese miembro, incluidos los mtodos definidos en otras
clases.
Al especificar un miembro de una clase corno priva te, slo pueden acceder al mismo
otros miembros de su clase. Por tanto, los mtodos de otras clases no pueden acceder a
un miembro p ri vate de otra clase.
La configuracin de acceso predeterminada (en la que no se usan modificadores de
acceso) es igual que publ i c a menos que el programa se divida en paquetes. Un paquete
es una agrupacin de clases. Sirven corno medios organizativos y como funcin de control
de acceso pero los veremos en el captulo 8. Para los tipos de programas de captulos
anteriores, el acceso publ ic es el mismo que el predeterminado. Un modificador de
acceso precede al resto de la especificacin de tipo del miembro. Es decir, debe iniciar la
instruccin de declaracin de un miembro. Veamos algunos ejemplos:
public String errMsg;
private accountBalance bal ;
private boolean isError(byte status) { // . ..
Java 7 f.l1ll
Para comprender los efectos de public y private, fjese en el siguiente
programa:
// Acceso pblico frente a privado.
class MyClass {
private int alpha; // acceso privado
public int beta; // acceso pblico
int gamma ; //acceso predeterminado (bsicamente es pblico)
/* Mtodos para acceder a alpha. Un miembro de
una clase puede acceder a un miembro privado
de la misma clase .
*/
void setAlpha(int a)
alpha = a;
int getAlpha() {
return alpha ;
class AccessDemo {
public static void main(String args[ ) ) {
MyClass ob = new MyClass();
/* El acceso a alpha slo se permite a travs
de sus mtodos de acceso . * /
ob.setAlpha(-99);
System. out . println( " ob.alpha is " + ob . getAlpha());
// No puede acceder a alpha de esta forma :
// ob . alpha = 10 ; // Error! alpha es prvate!
// Correcto ya que beta y gamma son publc .
ob . beta = 88;
ob . gamma = 99 ;
Como puede apreciar, dentro de la clase MyClas s, se especifica alpha como priva te,
beta como public y gamma usa el acceso predeterminado que, en este ejemplo, equivale
a especificar public. Como alpha es privado, no es accesible para cdigo externo a su
clase. Por tanto, dentro de la clase AccessDemo, no se puede usar directamente alpha.
Debe accederse a travs de sus mtodos de acceso pblicos: setAlpha () y getAlpha ().
Si eliminara el smbolo de comentario al inicio de la siguiente lnea:
// ob . alpha = 10; //Error! alpha es prvate!
no podra compilar este programa debido a la violacin de acceso. Aunque no se
permita el acceso a alpha desde el exterior de MyClass, los mtodos definidos en
MyClass pueden acceder libremente a los mtodos setAlpha () y getAlpha ().
6. Anlisis detallado de mtodos y clases
La clave es que un mtodo privado puede usarse libremente en otros miembros de
su clase pero no es accesible a cdigo externo a su clase.
Para aplicar el control de acceso a un ejemplo ms prctico, fjese en el siguiente
programa, que implementa una matriz int en la que se evitan errores de lmite y la
generacin de una excepcin de tiempo de ejecucin. Para ello, se incluye la matriz como
miembro privado de una clase y se permite el acceso a la matriz nicamente a travs de
los mtodos miembro. Con este enfoque, se impide todo intento de acceder a la matriz
ms all de sus lmites. La matriz se implementa por medio de la clase FailSoftArray,
que se muestra a continuacin:
/* Esta clase implementa una matriz que evita
errores de tiempo de ejecucin.
*/
class FailSoftArray {
private int a[);
private int errval;
public int length;
11 referencia a la matr:z
11 valor que devolver s: falla get()
11 length es public
/* Crear la matriz dado su tamao y el valor que
devolver si falla get(). */
public FailSoftArray(int size, int errv) {
a= new int[size);
errval errv;
length = size;
11 Devolver el valor en el ndice indicado .
public int get(int index) {
if(ok(index)) return a[index]; //Captura un ndice fuera de los lmites
return errval;
// Aadir un valor a un ndice . Devolver false en caso de fallo .
public boolean put(int index, int val) {
if(ok(index)) { //Captura un ndice fuera de los lmites
a[index) = val;
return true;
return false;
// Devolver true si el ndice se encuentra dentro de los lmites.
private boolean ok(int index) {
if(index >= O & index < length) return true;
return false;
// Ejemplo de la matriz .
class FSDemo {
public static void main(String args[]) {
FailSoftArray fs = new FailSoftArray(5, -1);
int x;
11 mostrar fallos
System.out.println( " Fail quietly. " );
for(int i=O; i < (fs.length * 2); i++)
Java 7 f'11SI
fs.put(i , i*lO) ; //Acceso a la matriz a travs de su mtodos de acceso .
for(int i=O; i < (fs.length * 2) ; i++) {
x = fs . get(i) ; //Acceso a la matriz a travs de su mtodos de acceso .
if(x != -1) System.out.print(x + " " );
System.out . println( "" ) ;
//controlar los fallos
System.out.println( " \nFail with error reports. " );
for(int i=O; i < (fs . length * 2); i++)
if ( ! f s . pu t ( i , i * 1 O) )
System.out . println("Index " + i + " out- of- bounds " );
for(int i=O; i < (fs.length * 2) ; i++) {
x = fs . get(i);
if(x ! = - 1) System.out . prir.t(x + " " );
el se
System.out . println( " Index " + i + " out- of-bounds");
Este programa genera el resultado mostrado en la figura 6.1.
Figura 6.1 . Ejemplo de matriz que evita errores de tiempo de ejecucin.
Analicemos el ejemplo con detalle. Dentro de Fa i 1 So f tAr r a y se definen tres miem-
bros pri vate. El primero es a, que almacena una referencia a la matriz que contendr
informacin. El segundo es errval, el valor devuelto cuando falla la invocacin de
get ().El tercero es el mtodo privado ok (),que determina si un ndice se encuentra
entre sus lmites. Por tanto, estos tres miembros slo pueden usarse con otros miembros
de la clase FailSoftArray. En concreto, a y erraval slo pueden usarse por otros
mtodos de la clase y o k ( ) slo se puede invocar en otros miembros de Fa i 1 So f tAr r a y.
El resto de los miembros de clase son pub 1 i e y se pueden invocar desde cualquier cdigo
de un programa que use FailSoftArray.
f .. t.tl 6. Anlisis detallado de mtodos y clases
Al crear un objeto FailSo ftArray, debe especificar el tamao de la matriz y el
valor que desea devolver si falla una invocacin de get ().El valor de error debe ser un
valor que en caso contrario no se almacenara en la matriz. Una vez creada, la matriz a la
que hace referencia a y el valor de error almacenado en errval no son accesibles para
los usuarios del objeto FailSoftArray. Por tanto, no sufren riesgos. Por ejemplo, un
usuario no puede intentar indexar a directamente y superar sus lmites. El acceso slo
est disponible a travs de los mtodos get () y put ().
El mtodo ok () es private por motivos descriptivos. Se podra convertir en publi c
ya que no modifica el objeto. Sin embargo, como la clase FailSoftArray lo usa inter-
namente, puede ser priva te.
Habr comprobado que la variable de instancia length es public, de acuerdo a
la implementacin de matrices en Java. Para obtener la longitud de FailSoftArray,
basta con usar su miembro length.
Para usar una matriz FailSoftArray, invoque put () para almacenar un valor en
el ndice especificado. Invoque get () para recuperar un valor de un ndice especificado.
Si el ndice supera los lmites, put () devuelve false y get () devuelve errval.
Por comodidad, muchos de los ejemplos del libro seguirn usando el acceso prede-
terminado en la mayora de los miembros. Recuerde sin embargo que en el mundo real
la limitacin del acceso a los miembros, en especial a variables de instancia, es una parte
importante de la programacin orientada a objetos. Como veremos en el siguiente cap-
tulo, el control de acceso es fundamental cuando la herencia est presente.
Ejercicio 6.1. Mejorar la clase Queue
Puede usar el modificador private para mejorar considerablemente la clase Queue
desarrollada en el captulo anterior. En esa versin, todos los miembros de la clase usan
el acceso predeterminado, public. Significa que un programa que use Queue podra
acceder directamente a la matriz subyacente y a sus elementos sin orden alguno. Como
la funcin de una cola es proporcionar una lista, no es aconsejable permitir un acceso
sin orden. Adems, un programador malintencionado podra alterar los valores alma-
cenados en los ndices putloc y getloc, lo que afectara a la cola. Afortunadamente,
estos problemas se pueden evitar si se aplica el especificador pri vate.
l. Copie la clase Que u e original del ejercicio 5.2 en un nuevo archivo con el nombre
Queue. java.
2. En la clase Queue, aada el modificador private a la matriz q, y los ndices
putloc y get l oc, como se indica a continuacin:
11 Una clase de cola mejorada par a caract eres .
class Queue {
// est os miembros ahora son private
private char q(] ; //matriz que contiene la cola
private int putloc, getloc; // los ndices put y get
Queue(int size) {
q = new char[size+l] ; //asignar m e m ~ r i para la cola
putloc getloc O;
// Aadir un carcter a la cola.
void put(char ch) {
if (putloc==q. length-1) {
System.out . println( " - Queue is full. " );
return ;
putloc++;
q [putloc] ch ;
// Obtener un carcter de la cola.
char get () {
if(getloc == putloc) {
System. out.println( " - Queue is empty. " );
return (char) O;
getloc++;
return q[getloc];
Java 7 f .Jeilj
3. El cambio de q, putloc y getloc de acceso predeterminado a privado no afecta
a los programas que usen Queue. Por ejemplo, sigue funcionando con la clase
QDemo del ejercicio 5.2. Sin embargo, evita el uso incorrecto de una cola. Por
ejemplo, las siguientes instrucciones no son vlidas:
Queue test= new Queue(lO);
test.q[O] = 99; // error!
test.putloc = - 100; // no funcionar!
4. Ahora que q, putloc y getloc son privados, la clase Queue aplica de forma
estricta el atributo FIFO de una cola.
Pasar objetos a mtodos
Hasta el momento, en los ejemplos del libro hemos usado tipos simples como par-
metros de mtodos. Sin embargo, es correcto y habitual pasar objetos a mtodos. Por
ejemplo, el siguiente programa define la clase Block para almacenar las dimensiones
de un bloque tridimensional:
// Se pueden pasar objetos a mtodos.
class Block {
int a, b, c ;
int volume;
6. Anlisis detallado de mtodos y clases
Block (int i, int j , int k) {
a = i;
b = j ;
c = k;
volume a * b * e ;
11 Devolver true si ob define el mismo bloque.
boolean sameBlock(Block ob) { //Usar tipo de objeto como parmetro .
if((ob. a ==a) & (ob . b == b) & (ob.c == c)) return true;
else return false ;
11 Devolver true si obtiene el mismo valor .
boolean sameVolume(Block ob) { //Usar tipo de objeto como parmetro.
if(ob. volume == volume) return true;
else return false ;
class PassOb {
public static void main(String args[))
Block obl new Block(lO, 2 , 5) ;
Block ob2
Block ob3
new Block(lO, 2, 5) ;
new Block(4, 5 , 5);
System. out . println( " obl same dimensions
//Pasar un objeto .
System. out . println( " obl same dimensions
System. out . println( " obl same volume as
as
as
ob3 :
Este programa genera el siguiente resultado:
obl same dimensions as ob2 : true
obl same dimensions as ob3 : false
obl same volume as ob3 : true
ob2 :
"
+ obl.sameBlock(ob2)) ;
ob3:
"
+ obl . sameBlock(ob3));
" + obl . sameVolume(ob3)) ;
Los mtodos sameBlock ( ) y sameVol ume () comparan el objeto Block pasado
como parmetro al objeto invocador. Para s ameBl ock ( ) ,las dimensiones de los objetos
se comparan y se devuelve true slo si los dos bloques son iguales. Para sameVal ue (),
se comparan los dos bloques slo para determinar si tienen el mismo volumen. En ambos
casos, el parmetro ob especifica Block como su tipo. Aunque Block es un tipo de clase
creado por el programa, se usa como los tipos propios de Java.
Cmo pasar argumentos
Como hemos visto en el ejemplo anterior, pasar un objeto a un mtodo es una tarea
muy sencilla. Sin embargo, el ejemplo no ilustra muchos de los matices propios de la
operacin. En determinados casos, los efectos de pasar un objeto sern diferentes de
Java 7 f41fj
los experimentados al pasar argumentos que no son objetos. Para ver el motivo, debe
entender las dos formas en las que un argumento se puede pasar a una subrutina. La
primera forma se denomina invocar por valor. Este enfoque copia el valor de un argu-
mento en el parmetro formal de la subrutina. Por tanto, los cambios realizados en el
parmetro de la subrutina no afectan al argumento de la invocacin. La segunda forma
de pasar un argumento se denomina invocar por referencia. En este enfoque, se pasa una
referencia a un argumento (no el valor) al parmetro. En la subrutina, esta referencia se
usa para acceder al argumento real especificado en la invocacin. Esto significa que los
cambios realizados en el parmetro s afectan al argumento usado para invocar la subru-
tina. Como veremos, aunque Java usara invocacin por valor para pasar argumentos, el
efecto preciso difiere si se pasa un tipo primitivo o un tipo de referencia.
Al pasar un tipo primitivo, como int o doub le, a un mtodo, se pasa por valor. Por
tanto, se crea una copia del argumento y lo que sucede al parmetro que recibe el argu-
mento no afecta al exterior del mtodo. Veamos un programa:
// Los tipos primitivos se pasan por valor .
class Test {
/* Este mtodo no cambia los argumentos
usados en la invocacin. */
void noChange(int i, int j) {
i i + j;
j = -j ;
class CallByValue {
public static void main(String args[)) {
Test ob = new Test() ;
int a = 15, b = 20;
System. out.println( " a and b before call : "+a+ "" + b);
ob . noChange(a , b);
System. out . println("a and b after call: " + a + " " + b);
Este programa genera el siguiente resultado:
a and b before call : 15 20
a and b after call: 15 20
Como puede apreciar, las operaciones dentro de noChange () no afectan a los valores
de a y b usados en la invocacin.
Al pasar un objeto a un mtodo, la situacin cambia radicalmente, ya que los objetos
se pasan implcitamente por referencia. Recuerde que al crear una variable de un tipo
de clase, se crea una referencia a un objeto. Es la referencia, no el objeto, la que se pasa
al mtodo. Como resultado, al pasar esta referencia a un mtodo, el parmetro que lo
recibe hace referencia al mismo objeto al que hace referencia el argumento. Por tanto,
f ..lel:I 6. Anlisis detallado de mtodos y clases
los objetos se pasan a mtodos por invocacin de referencia. Los cambios realizados en
el objeto dentro del mtodo afectan al objeto usado como argumento. En el siguiente
programa:
11 Los objetos se pasan a travs de sus referencias .
class Test {
int a , b ;
Test(int i , int j) {
a = i ;
b = j;
/* Pasar un objeto. Ahora ob . a y ob . b del objeto
usado en la invocacin cambian. */
void change(Test ob) {
ob .a ob . a + ob.b;
ob .b = -ob.b;
class PassObRef {
public static void main(String args[]) {
Test ob = new Test(l5, 20) ;
System.out . println( " ob.a and ob.b before ca:.l : "+ ob . a +" "+ ob.b) ;
ob . change (ob);
System.out.println( " ob.a and ob .b after call : " + ob . a + " " + ob .b);
Este programa genera el siguiente resultado:
ob .a and ob . b before call : 15 20
ob . a and ob . b after call : 35 - 20
En este caso, las acciones dentro de change () afectan al objeto usado como argu-
mento.
Recuerde que cuando una referencia de objeto se pasa a un mtodo, se pasa la propia
referencia mediante una invocacin por valor. Sin embargo, como el valor pasado hace
referencia a un objeto, la copia del valor seguir haciendo referencia al mismo objeto al
que hace referencia su correspondiente argumento.
Preguntas al experto
P: Existe alguna forma de pasar un tipo primitivo por referencia?
R: No directamente. Sin embargo, Java define un conjunto de clases que envuelven
los tipos primitivos en objetos: Double, Float, Byte, Short, Integer, Long
y Character. Adems de permitir que se pase un tipo primitivo por referencia,
Java 7 tmi)I
estas clases contenedoras definen varios mtodos que le permiten manipular sus
valores. Por ejemplo, los contenedores de tipos numricos incluyen mtodos para
convertir un valor numrico binario a formato s tri ng, y viceversa.
Devolver objetos
Un mtodo puede devolver cualquier tipo de datos, incluidos tipos de clases. Por
ejemplo, la clase ErrorMsg mostrada a continuacin se puede usar para informar de
errores. Su mtodo get ErrorMsg () devuelve un objeto Str ing que contiene una
descripcin de un error basada en el cdigo de error pasado.
// Devolver un objeto String.
class ErrorMsg {
String msgs[] = {
"Output Error " ,
" Input Error " ,
" Disk Full " ,
" Index Out - Of - Bounds "
} ;
// Devolver el mensaje de error .
String getErrorMsg(int i) { / / Devolver un objeto de tipo String.
if(i >=O & i < msgs.length)
return msgs[i) ;
el se
return " Invalid Error Code ";
class ErrMsg {
public static void main(String args[]) {
ErrorMsg err = new ErrorMsg();
System. out.println(err.getErrorMsg(2));
System. out . println(err.getErrorMsg(19)) ;
El resultado generado es el siguiente:
Disk Full
Invalid Error Code
Evidentemente, tambin puede devolver objetos de las clases que cree. Por ejemplo, la
siguiente versin del programa anterior crea dos clases de error. Una es Err y contiene
un mensaje de error junto a un cdigo de gravedad. La segunda, Er r o rinf o, define un
mtodo g etError i n f o (),que devuelve un objeto Er r:
11 Devolver un objeto definido por el programador
class Err {
String msg ; // mensaje de error
int severity; // cdigo de la g ~ v e d d del error
fjlel 6. Anlisis detallado de mtodos y clases
Err(String m, int s) {
msg = m;
severity = s;
class Errorinfo {
String msgs[J =
"Output Error",
" Input Error",
" Disk Full ",
" Index Out - Of - Bounds "
} ;
int howbad [) = { 3 , 3 , 2 , 4 } ;
Err getErrorinfo (int i) { //Devolver un objeto de tipo Err.
if(i >= O & i < msgs.length)
return new Err(msgs[i) , howbad[i));
el se
return new Err( " Invalid Error Code ", 0) ;
class Errinf o {
public static void main(String args [ ))
Errorinfo err = new Errorinfo();
Err e ;
e= err . getErrorinfo(2) ;
System. out . println(e.msg + " severity: "
e = err . getErrorinfo(19);
e . severity);
System. out.println(e.msg + " severity : " + e . severity);
El resultado generado es el siguiente:
Disk Full severity: 2
Invalid Error Code severity: O
Cada vez que se invoca getErrorinfo (),se crea un nuevo objeto Err y se devuelve
una referencia al mismo a la rutina de invocacin. Este objeto se usa en main () para
mostrar el mensaje de error y el cdigo de gravedad.
Cuando un mtodo devuelve un objeto, se conserva hasta que no haya ms referencias
al mismo. Despus, se puede eliminar. Por tanto, un objeto no se destruye slo porque
finalice el mtodo que lo ha creado.
Sobrecargar mtodos
En este apartado abordaremos una de las caractersticas ms interesantes de Java:
sobrecarga de mtodos. En Java, dos o ms mtodos de la misma clase pueden comparti!-
el mismo nombre, mientras sus declaraciones de parmetros sean diferentes. En
Java 7 Jll
caso, se dice que los mtodos estn sobrecargados y el proceso se denomina sobrecarga
de mtodos. La sobrecarga de mtodos es una de las tcnicas de Java para implementar
el polimorfismo.
Por lo general, para sobrecargar un mtodo, basta con declarar distintas versiones
del mismo. El compilador se encarga del resto. Debe tener en cuenta un restriccin
importante: el tipo y/ o nmero de parmetros de cada mtodo sobrecargado debe ser
diferente. No basta con que dos mtodos difieran slo en los tipos devueltos, que no
ofrecen informacin suficiente para que Java decida qu mtodo usar. Evidentemente,
los mtodos sobrecargados tambin pueden diferir en sus tipos devueltos. Al invocar
un mtodo sobrecargado, se ejecuta la versin del mtodo cuyos parmetros coincidan
con los argumentos.
El siguiente ejemplo muestra la sobrecarga de mtodos:
11 Sobrecarga de mtodos .
class Overload {
void ovlDemo() { //Primera version
System. out . println( " No parameters" ) ;
// Sobrecargar ovlDemo para un parmetro entero.
void ovlDemo (int a) { //Segunda ver sion
System. out . p r intln( " One parameter : " +a);
11 Sobrecargar ovlDemo para dos par metros enteros .
int ovlDemo(int a , int b) { //Tercera version
System. out . println (" Two parameters: " +a+ " " + b );
return a + b ;
// Sobrecargar ovlDemo para dos parmetros double .
double ovlDemo(double a , double b) { //Cuarta version
System. out . println (" Two double parameters: " + a + " " + b) ;
return a + b ;
class OverloadDemo {
public static void main (String args[))
Overload ob = new Overload() ;
int resI ;
double resD;
11 invocar todas las versiones de ovlDemo()
ob . ovlDemo( );
System. out . println ( ) ;
ob . ov1Demo(2 );
System. out . println ( ) ;
resI = ob . ov1Demo(4 , 6) ;
System. out . println (" Result of ob . ov1Demo(4 , 6) : " + resI) ;
System. out . println ( ) ;
fJtl
6. Anlisis detallado de mtodos y clases
resD = ob . ovlDemo ( l . 1, 2 . 32) ;
System. out . println( " Result of ob . ovlDemo(l . 1 , 2 . 32) : " + resD) ;
Este programa genera el resultado mostrado en la figura 6.2.
Figura 6.2. Ejemplo de sobrecarga de mtodos.
Como puede apreciar, ovlDerno () se sobrecarga cuatro veces. La primera versin no
acepta parmetros, la segunda adopta un parmetro entero, la tercera acepta dos par-
metros enteros y la cuarta acepta dos parmetros double. Las dos primeras versiones de
ovlDerno () devuelven void y las dos segundas devuelven un valor. Es perfectamente
vlido pero como hemos indicado, la sobrecarga no se ve afectada por el tipo devuelto
de un mtodo. Por ello, si intenta usar las dos siguientes versiones de ovlDerno () se
genera un error:
11 Un ovlDemo(int ) es correcto.
void ovlDemo(int a) { //Los tipos devueltoE no se pueden usar para diferenciar
mtodos sobrecargacos.
System. out . println( " One parameter : " + a);
/* Error! Dos ovlDemo(int) no son correctos aunque
los tipos devueltos difieran .
*/
int ovlDemo(int a) { //Los tipos devueltos no se pueden usar para diferenciar
mtodos sobrecargados .
System.out . println( " One parameter : " +a) ;
return a * a ;
Como indican los comentarios, la diferencia en los tipos devueltos no basta p
realizar la sobrecarga.
Como recordar del captulo 2, Java cuenta con determinas conversiones de ti
automticas que tambin se aplican a los parmetros de mtodos sobrecargados. Fjese
en el siguiente ejemplo:
/* Las conversiones automticas de tipos pueden afectar
a la resolucin de mtodos sobrecargados .
*/
class Overload2 {
void f (int x) {
System.out . println(
11
Inside f(int): " + x);
void f(double x) {
System.out . println(
11
Inside f(double) :
11
+ x);
class TypeConv {
public static void main(String args[])
Overload2 ob = new Overload2();
int i = 10;
double d = 10 . 1;
byte b 99 ;
short s 10 ;
float f = 11 . SF;
ob. f (i); 11 invoca
ob . f (d); 11 invoca
ob . f (b); 11 invoca
ob. f ( S) ; 11 invoca
ob.f(int)
ob . f(double)
ob.f(int) - conversin
ob . f (int) - conversin
de
de
ob . f (f); 11 invoca ob . f(double) - conversin
Este programa genera el siguiente resultado:
Inside f (int) : 10
Inside f (double): 10 . 1
Inside f (int): 99
Inside f (int): 10
Inside f (double) : 11. 5
Java 7 fjll
tipos
tipos
de tipos
En este ejemplo, slo se definen dos versiones de f ():una con un parmetro int y
otra con un parmetro double. Sin embargo, se pueden pasar valores byte, short o
f loat a f ().En el caso de byte y short, Java los convierte automticamente a int.
Por tanto, se invoca f (int). En el caso de float, el valor se convierte a double y se
invoca f (double).
Debe recordar que las conversiones automticas slo se aplican si no hay una coin-
cidencia directa entre un parmetro y un argumento. A continuacin se muestra el
programa anterior con una versin de f () que especifica un parmetro byte:
11 Aadir f(byte) .
class Overload2 {
void f(byte x) {
System.out.println("Inside f(byte): " + x);
void f (int x) {
System.out . println(
11
Inside f(int) :
11
+ x);
fJll 6. Anlisis detallado de mtodos y clases
void f(double x) {
System. out . println( " Inside f(double) : " + x);
class TypeConv {
public static void main(String args[])
Overload2 ob = new Overload2();
int i = 10 ;
double d = 10.1;
byte b = 99 ;
short s 10;
float f = 11 . SF;
ob . f (i); 11 invoca
ob . f (d); // invoca
ob . f(int)
ob . f (double)
ob.f(b); 11 invoca ob . f (byte) - no hay :::onversin de
ob . f (s) ; 11 invoca ob . f (int) - conversin de tipos
ob. f (f); 11 invoca ob.f (double) - conversin de tipos
Al ejecutar el programa, se genera el siguiente resultado:
Inside f(int) : 10
Inside f(double) : 10 . 1
Inside f(byte) : 99
Inside f(int) : 10
Inside f(double): 11.5
tipos
En esta versin, como existe una versin de f () que acepta un argumento byte, CL
invocar f () con un argumento byte, se invoca f (byte) y no se produce la conversir.
automtica a int.
La sobrecarga de mtodos admite el polimorfismo ya que es una forma de Java para
implementar el paradigma de una interfaz, varios mtodos. Para entenderlo, fjese er.:
lo siguiente. En lenguajes que no admiten la sobrecarga de mtodos, cada mtodo debe
tener un nombre exclusivo. Sin embargo, en ocasiones querr implementar el mismo
mtodo para distintos tipos de datos. Piense en la funcin de valor absoluto. En lenguajes
que no admiten la sobrecarga, suele haber tres o ms versiones de esta funcin, cada Ul\i!
con un nombre ligeramente distinto. Por ejemplo, en C, la funcin abs () devuelve
valor absoluto de un entero, labs () el valor absoluto de un entero long y fabs ()
valor absoluto de un valor de coma flotante. Como C no admite sobrecarga, cada funcir.
necesita un nombre propio, aunque las tres funciones hagan bsicamente lo mismo. P
este motivo, resulta ms complejo, conceptualmente hablando, de lo que es en realidad..
Aunque el concepto subyacente de cada funcin es el mismo, tendr que recordar tres
nombres, algo que no sucede en Java, ya que cada mtodo de valor absoluto puede usar
el mismo nombre. En realidad, la biblioteca de clases estndar de Java incluye un mtod
Java 7 fJlej
de valor absoluto, abs (), que la clase Ma th de Java sobrecarga para trabajar con todos
los tipos numricos. Java determina qu versin de abs () invocar en funcin del tipo
de argumento.
La sobrecarga permite acceder a mtodos relacionados a travs de un nombre comn.
Por tanto, el nombre abs representa la accin general realizada. El compilador se encarga
de seleccionar la versin especfica en cada caso concreto. El programador slo tiene
que recordar la operacin general realizada. Mediante la aplicacin de polimorfismo, se
pueden reducir varios nombres a uno. Aunque ste sea un ejemplo sencillo, si ampla el
concepto, comprobar que la sobrecarga le permite afrontar otros ms complejos.
Al sobrecargar un mtodo, cada versin del mismo puede realizar una actividad
distinta. No hay reglas sobre la relacin entre los mtodos sobrecargados. Sin embargo,
desde un punto de vista estilstico, la sobrecarga de mtodos implica una relacin. Por
tanto, aunque puede usar el mismo nombre para sobrecargar mtodos sin relacin,
no debera hacerlo. Por ejemplo, podra usar el nombre sqr para crear mtodos que
devuelvan el cuadrado de un entero y la raz cuadrada de un valor de coma flotante.
Pero son dos operaciones diferentes. La aplicacin de la sobrecarga de mtodos de esta
forma es contraria a su naturaleza original. En la prctica, slo debera sobrecargar
operaciones relacionadas.
Preguntas al experto
P: He escuchado que los programadores de Java usan el trmino firma. Qu signi-
fica?
R: En Java, una firma es el nombre de un mtodo ms su lista de parmetros. Por
tanto, en lo que a sobrecarga se refiere, dos mtodos de la misma clase no pueden
tener la misma firma. La firma no incluye el tipo devuelto, ya que Java no lo utiliza
para la resolucin de sobrecarga.
Sobrecargar constructores
Al igual que los mtodos, los constructores tambin se pueden sobrecargar. De este
modo puede crear objetos de distintas formas. Fjese en el siguiente programa:
11 Ejemplo de constructor sobrecargado .
class MyClass
int x ;
MyClass() { //Crear objetos de 1istintas formas .
System.out.println ("I nside MyClass() ." ) ;
X = O;
MyClass(int i) { //Crear objetos de distintas formas.
System.out.println ("Inside MyClass (int) . " );
fjkl
6. Anlisis detallado de mtodos y clases
X = i ;
MyClass(double d) { //Crear objetos de distintas formas .
System. out.println( " Inside MyClass(double) ." ) ;
X = (int) d ;
MyClass(int i , int j) { //Crear objetos de distintas formas .
System. out . println ( " Inside MyClass (int, ir.t). " ) ;
X= i * j;
class OverloadConsDemo {
public static void main(String args[J)
MyClass tl new MyClass() ;
MyClass t2 new MyClass(88 );
MyClass t3 new MyClass(l7.23) ;
MyClass t4 new MyClass(2 , 4) ;
System.out.println( " tl.x: "+ tl.x);
System.out.println( " t2 . x : "+ t2 . x);
System. out.println( " t3.x: "+ t3 . x);
System. out . println( " t4 . x : " + t4 . x) ;
En la figura 6.3 puede ver el resultado generado por este.
Figura 6.3. Ejemplo de constructor sobrecargado.
MyClas s () se sobrecarga de cuatro formas y en cada una se crea un objeto diferen
Se invoca el constructor adecuado en funcin de los parmetros especificados al ejecu
new. Al sobrecargar el constructor de una clase, el usuario de la misma dispone de gra::
flexibilidad para crear objetos. Uno de los motivos ms habituales de la sobrecarga
constructores es para que un objeto pueda inicializar otro. El siguiente programa usa
clase Summa tion para calcular la suma de un valor entero:
11 Inicializar un objeto con otro.
class Summation
int sum;
11 Crear a partir de un int.
Sumrna tion ( int num) {
sum = O;
for ( int i=l ; i <= num; i++)
sum += i ;
11 Crear a partir de otro objeto.
Sumrnation(Sumrnation ob ) {
sum = ob . sum;
class SumDemo {
public static void main(String args())
Sumrnation sl new Sumrnation ( 5);
Sumrnation s2 = new Sumrnation(sl );
System.out . println( " sl . sum: " + sl . sum);
System. out . println( " s2 . sum: " + s2.sum);
Este programa genera el siguiente resultado:
sl . sum: 15
s2.sum: 15
Java 7 fjfl
Corno muestra el ejemplo, una de las ventajas de proporcionar un constructor que
use un objeto para inicializar otro es la eficacia. En este caso, al crear s2, no es necesario
volver a calcular la suma. Evidentemente, en casos en los que la eficacia no es problema,
resulta muy til proporcionar un constructor que cree una copia de un objeto.
Ejercicio 6.2. Sobrecargar el constructor Queue
En este ejercicio mejoraremos la clase Qu eue y le asignaremos dos constructores
iniciales. El primero crea una nueva cola a partir de otra. El segundo crea una cola y le
asigna valores iniciales. Corno comprobar, estos constructores aumentan considerable-
mente la capacidad de uso de Queue.
l. Cree un archivo con el nombre QDemo2 . j ava y copie en su interior la clase Que u e
actualizada del ejercicio 6.1.
2. En primer lugar, aada el siguiente constructor, para crear una cola a partir de
otra:
11 Crear una cola a partir de una cola.
Queue(Queue ob) {
putloc = ob.putloc;
getloc = ob . getloc ;
q = new char[ob . q.length];
11 copiar elementos
fjl:I 6. Anlisis detallado de mtodos y clases
for(int i=getloc+l ; i <= putloc; i++)
q [ i J = ob . q [ i J ;
Fjese atentamente en el constructor. Inicializa putloc y getloc en los valores
incluidos en el parmetro ob. Tras ello, asigna una nueva matriz para alma-
cenar la cola y copia los elementos de ob a la matriz. Una vez creada, la nueva
cola ser una copia idntica a la original pero sern dos objetos indepen-
dientes.
3. Aada el constructor que inicializa la cola a partir de una matriz de caracteres:
11 Crear una cola con valores iniciales .
Queue(char a[])
putloc = O;
getloc = O;
q = new char[a . length+l];
for(int i =O; i < a.length; i++) put(a[i)) ;
Este constructor crea una cola con tamao suficiente para almacenar los caracteres
de a y despus los almacena en la cola. Debido al funcionamiento del algoritmo
de colas, la longitud de la cola debe ser igual que la de la matriz ms uno.
4. A continuacin se muestra la clase Queue actualizada completa junto a la clase
QDerno2:
11 Una clase de cola para caracteres.
class Queue {
private char q[]; //matriz que contiene la cola
private int putloc, getloc; // los ndices put y get
11 Crear una cola vaca dado su tamao.
Queue (int size) {
q = new char[size+l) ; //asignar memoria a la cola
putloc = getloc = O;
// Crear una cola a partir de una cola.
Queue (Queue ob) {
putloc = ob.putloc;
getloc = ob . getloc;
q = new char[ob.q.length);
// copiar elementos
for(int i=getloc+l; i <= putloc; i++)
q [ i) = ob . q [ i J ;
// Crear una cola con valores iniciales.
Queue(char a[])
putloc = O;
getloc = O;
q = new c har[a.length+l];
for(int i
O; i < a.length; i++) put(a[i]);
11 Aadir un carcter a la cola.
void put(char ch)
if(putloc==q. length-1)
System. out .println(" - Queue is full.");
return;
putloc++;
q [putloc] ch;
11 Obtener un carcter de la cola .
char get()
if(getloc == putloc)
System. out.println(" - Queue is empty ." );
return (char) O;
getloc++;
return q[getloc];
11 La clase Queue .
class QDemo2 {
public static void main(String args[])
11 crear una cola vaca de 10 elementos
Queue ql = new Queue(lO);
char name [] = { 'T' , ' o ' , ' m' } ;
11 crear cola a partir de l a matriz
Queue q2 = new Queue(name);
char ch;
int i ;
11 aadir caracteres a ql
for (i =O; i < 10 ; i++)
ql.put((char) ('A'+ i));
11 crear una cola a partir de otra
Queue q3 new Queue(ql);
11 Mostrar las colas .
System. out . print ( " Contents of ql: ") ;
for(i=O; i < 10 ; i++ ) {
ch = ql . get () ;
System. out . print( ch);
System. out .println( " \n");
System. out . pri nt ( " Contents of q2: ") ;
Java 7 fJiji
6. Anlisis detallado de mtodos y clases
for(i=O; i < 3 ; i++) {
ch= q2 . get();
System. out .print(ch) ;
System. out . println( " \n" ) ;
System. out . print( " Contents of q3 : " );
for (i=O; i < 10; i++) {
ch = q3. get () ;
System.out.print (ch) ;
Este programa genera el siguiente resultado:
Contents of ql : ABCDEFGHIJ
Contents of q2 : Tom
Contents of q3 : ABCDEFGHIJ
Recursin
En Java, un mtodo se puede invocar a s mismo. Este proceso se denomina recursin
y un mtodo que se invoque a s mismo se denomina mtodo recursivo. Por lo general
la recursin es el proceso de definir algo en trminos de s mismo y es similar a una defi-
nicin circular. El principal componente de un mtodo recursivo es una instruccin que
ejecute una invocacin a s mismo. La recursin es un potente mecanismo de control.
El ejemplo clsico de recursin es el clculo del factorial de un nmero. El factorial
de un nmero N es el producto de todos los nmeros enteros entre 1 y N. Por ejemplo.
factorial 3 es 1x2 x 3, 6. El siguiente programa muestra una forma recursiva de calcular
el factorial de un nmero. Por motivos de comparacin, tambin se incluye su equiva-
lente sin recursin.
11 Sencillo ejemplo de recursin.
class Factorial {
// Una funcin recursiva .
int factR(int n )
int result;
if(n==l) return 1;
result = factR(n-1) * n; //Ejecutar la invocacin recursiva de factR().
return result;
11 Un equivalente iterativo.
int factI (int n) {
int t, result;
resul t = l ;
for (t=l ; t <= n ; t++) result += t ;
return r esult ;
class Recursion {
public static void main(String args[])
Factorial f = new Factorial() ;
System. out . println( " Factorials using recursive method." ) ;
System. out . println ("Factorial of 3 is " + f. factR (3)) ;
System. out.printl n (" Factorial of 4 is " + f.factR(4)) ;
System. out . pri ntln( " Factorial of 5 is " + f.factR(S)) ;
System. out.println( );
System. out . println( " Factorials using i terative method ." ) ;
System. out.println( " Factorial of 3 is " + f . factI(3)) ;
System. out . println( " Factorial of 4 is " + f.factI(4)) ;
System.out.println( " Factorial of 5 i s " + f .factI(S)) ;
Este programa genera el resultado que reproducimos en la figura 6.4.
Figura 6.4. Sencillo ejemplo de recursin.
Java 7 fjji
El funcionamiento del mtodo no recursivo fac tI () debe ser evidente. Usa un
bucle comenzando en 1 y multiplica progresivamente cada nmero por el producto
resultante.
El funcionamiento de fact R () es ms complejo. Al invocar factR () con el argumento
1, el mtodo devuelve 1; en caso contrario, devuelve el producto de factR (n- 1 ) *n.
Para evaluar esta expresin, se invoca factR () con n-1. Este proceso se repite hasta
que n equivale a 1 y se empiezan a devolver las invocaciones del mtodo. Por ejemplo,
cuando se calcula el factorial de 2, la primera invocacin de f actR () provoca una
segunda invocacin con el argumento l. Esta invocacin devuelve 1, que se multiplica
por 2 (el valor original de n). Por tanto, la respuesta es 2. Puede aadir instrucciones
println () en factR () para mostrar en qu nivel se encuentra cada invocacin y cul
es el resultado intermedio.
Cuando un mtodo se invoca a s mismo, se asigna almacenamiento en la pila a nuevas
variables locales y parmetros, y el cdigo del mtodo se ejecuta con dichas variables
desde el principio. Una invocacin recursiva no crea una nueva copia del mtodo. Slo
f }}I 6. Anlisis detallado de mtodos y clases
los argumentos son nuevos. Al regresar cada invocacin recursiva, se eliminan de la pila
las variables locales y los parmetros antiguos, y la ejecucin se reanuda desde la invo-
cacin dentro del mtodo. Los mtodos recursivos se podran denominar telescpicos.
Las versiones recursivas de muchas rutinas pueden ejecutarse ms lentamente que sus
equivalentes iterativas debido a la sobrecarga aadida de las invocaciones de mtodos
adicionales. Un exceso de invocaciones recursivas a un mtodo puede provocar el
desbordamiento de la pila. Como el almacenamiento de parmetros y variables locales
se encuentra en la pila y en cada invocacin se crea una nueva copia de estas variables,
la pila puede llegar a agotarse. En ese caso, el sistema de tiempo de ejecucin de Java
genera una excepcin. Sin embargo, seguramente no tenga que preocuparle a menos que
una rutina recursiva se descontrole.
La principal ventaja de la recursin es que algunos tipos de algoritmos se pueden
implementar de forma ms clara y sencilla en formato recursivo que iterativo. Por
ejemplo, el algoritmo de ordenamiento rpido es bastante complejo de implementar de
forma iterativa.
Adems, determinados problemas, como los relacionados con IA, suelen solventarse
con soluciones recursivas. Al crear mtodos recursivos, debe incluir una instruccin
condicional, como i f, para que el mtodo regrese sin que se ejecute la invocacin recur-
siva. Si no lo hace, al invocar el mtodo nunca regresar. Es un error muy habitual al
trabajar con la recursin. U se instrucciones p r in t l n ( ) para saber qu sucede y cancele
la ejecucin si detecta algn error.
Comprender static
En ocasiones tendr que definir un miembro de clase que se use independientemente
de los objetos de esa clase. Por lo general se accede a un miembro de clase a travs de
un objeto de su clase pero se puede crear un miembro que usar de forma independiente,
sin referencias a una instancia concreta. Para crear este tipo de miembro, debe preceder
su declaracin con la palabra clave static. Al declarar un miembro como static,
se puede acceder al mismo antes de crear objetos de su clase y sin referencias a ningn
objeto.
Puede declarar como static tanto mtodos como variables. El ejemplo ms habi-
tual de miembro static es rnain (),ya que debe invocarse por la MVJ al iniciar un
programa. Fuera de la clase, para usar un miembro static, basta con especificar el
nombre de su clase seguido del operado r de punto. No es necesario crear objetos. Por
ejemplo, si desea asignar el valor 10 a una variable s tatic count que forma parte de
la clase T irne r, use esta lnea:
Timer.count = 10 ;
Es un formato similar al utilizado para acceder a variables de instancia normales a
travs de un objeto, con la excepcin de que se usa el nombre de la clase. Un mtodo
static se puede invocar de la misma forma, por medio del operador de punto en el
nombre de la clase.
Las variables declaradas como s ta ti e son, bsicamente, variables globales. Al
declarar un objeto, no se crean copias de una variable stat ic. En su lugar, todas las
instancias de la clase comparten la misma variable s tat i c . Veamos un ejemplo que
ilustra las diferencias entre una variable s t a t i c y una variable de instancia:
Usar una variable esttica .
class StaticDemo
int x ; // una variable de instancia normal
static int y ; // una variable esttica
// Devolver la suma de la variable de instancia x
// y la variable esttica y .
int sum() {
ret urn x + y ;
class SDemo {
public static void main(String args[])
StaticDemo obl new StaticDemo();
StaticDemo ob2 = new StaticJemo() ;
// Cada objeto tiene su copia de una var iable de instancia .
obl. x = 10;
ob2.x = 20 ;
System. out.println( " Of course, obl . x and ob2 . x " + " are independent ." );
System. out. printl n( " obl.x: " + obl.x + " \nob2 . x: " + ob2 . x);
System. out . println() ;
// Cada objeto comparte una copia de una variable esttica .
System. out . println( " The static variable y is shared." ) ;
StaticDemo. y = 19;
System. out . println( " Set StaticDemo . y to 19. " );
System.out . println( " obl.sum(): " + obl.sum());
System. out . println( " ob2 . sum() : " + ob2 . sum());
System. out . println() ;
StaticDemo . y = 100;
System. out . println( " Change StaticDemo.y to 100" );
System. out . println( " obl . sum() : " + obl . sum() ) ;
System. out . println( " ob2.sum(): "+ ob2.sum() ) ;
System. out . println() ; }
Este programa genera el resultado mostrado en la figura 6.5.
Como puede apreciar, la variable static y se comparte en obl y ob2. Al cambiarla,
toda la clase se ve afectada, no slo una instancia.
La diferencia entre un mtodo static y uno normal es que el mtodo static se
invoca a travs de su nombre de clase, sin crear objetos de dicha clase. Ya hemos visto
un ejemplo, el mtodo sqrt () , un mtodo static dentro de la clase Math de Java.
Veamos un ejemplo que crea un mtodo static:
fil
6. Anlisis detallado de mtodos y clases
// Usar un mtodo esttico .
class StaticMeth {
static int val = 1024 ; // una variable esttica
11 un mtodo esttico
static int va1Di v2()
return val/2 ;
class SDemo2 {
publ ic static void main (String args []) {
System.out . println( " val is " + StaticMeth . val) ;
System.out . println ( " StaticMeth.valDiv2 (): " + StaticMeth . valDiv2 ()) ;
StaticMeth. val = 4 ;
System.out . println( " val is " + StaticMeth.val);
System.out . println( '' StaticMeth . valDiv2(): " + StaticMeth . valDiv2()) ;
D Slmbolo del sistema
Figura 6.5. Programa para ilustrar el uso de variables estticas.
Este programa genera el siguiente resultado:
val is 1024
StaticMeth . valDiv2( ): 512
val is 4
StaticMeth . valDiv2( ): 2
Los mtodos declarados corno static tienen ciertas restricciones:
Slo pueden invocar directamente otros mtodos static.
Slo pueden acceder directamente a datos static.
Carecen de una referencia t h is.
En la siguiente clase, el mtodo esttico valDi vDenom ( ) no es vlido:
class StaticError {
int denom = 3 ; // una variable de instancia normal
static int val = 1024 ; // una variable est:ica
/* Error ! No se puede acceder a una vari able no esttica
desde un mtodo esttico . */
static int valDivDenom( ) {
return val/denom; // no se compilar
Java 7 fj_;j
Aqu, denom es una variable de instancia normal a la que no se puede acceder con
un mtodo static.
Bloques estticos
En ocasiones, una clase requiere cierta inicializacin antes de que pueda crear objetos.
Por ejemplo, puede necesitar establecer una conexin a un sitio remoto. O inicializar
determinadas variables static antes de poder usar los mtodos stati c de la clase.
En estos casos, Java le permite declarar un bloque sta t i c, un bloque que se ejecuta al
cargar la clase por primera vez. Por tanto, se ejecuta antes de poder usar la clase con
otros fines. Veamos un ejemplo de bloque sta tic:
11 Usar un bloque esttico
class StaticBlock {
static double root0f2 ;
static double root0f3;
static { //Este bloque se ejecuta al cargar la clase.
System. out . println( " Inside static block. " );
root0f2 Math.sqrt(2 . 0) ;
root0f3 = Math . sqrt(3 . 0) ;
StaticBlock(String msg) {
System. out . println(msg) ;
class SDemo3 {
publ ic static void main(String args[]) {
StaticBlock ob = new StaticBlock( " Inside Constructor" );
System.out . println (" Square root of 2 is '' + StaticBlock . rootOf2);
System.out.println (" Square root of 3 is " + StaticBlock. rootOf3);
Se genera el siguiente resultado:
Inside static block .
Inside Constructor
Square root of 2 is 1 . 4142135623730951
Square root of 3 is 1 . 7320508075688772
Como puede apreciar, el bloque sta tic se ejecuta antes de crear ningn obj
f j.l;j 6. Anlisis detallado de mtodos y clases
Ejercicio 6.3. Ordenamiento rpido
En el captulo anterior vimos un sencillo mtodo de ordenamiento llamado
Ordenamiento de burbuja. Mencionamos que existen otros mtodos ms eficaces. A
continuacin, desarrollaremos una versin de uno de los mejores: ordenamiento rpido.
Este mtodo, inventado por C.A.R. Hoare, es el mejor algoritmo general de ordenamiento
que existe en la actualidad. No lo mostramos en el captulo anterior porque depende de
la recursin. La versin que desarrollaremos ordena una matriz de caracteres pero la
lgica se puede adaptar a cualquier objeto que desee.
El ordenamiento rpido se basa en el concepto de particiones. El procedimiento general
consiste en seleccionar un valor denominado comparando y despus dividir la matriz
en dos secciones. Todos los elementos mayores o iguales que el valor de particin se
desplazan a un lado y el resto al otro. El proceso se repite en todas las secciones restantes
hasta que la matriz se ordena. Por ejemplo, dada la matriz f edacb y con el valor d como
comparando, la primera pasada de ordenamiento rpido la ordenara de esta forma:
Inicial fedacb
Primera pasada bcadef
El proceso se repite en cada seccin, es decir, b ca y d e f. Como puede apreciar, es
un proceso esencialmente recursivo y, en realidad, la implementacin ms limpia de
ordenamiento rpido es recursiva.
Hay dos formas de seleccionar el valor de comparando. Puede elegirlo aleatoriamente
o calcular la media de un pequeo conjunto de valores obtenidos de la matriz. Para que
el orden sea ptimo, debe seleccionar un valor que sea el centro preciso del interv
de valores. Sin embargo, no resulta sencillo en muchos conjuntos de datos. En el peor
de los casos, el valor seleccionado se encuentra en uno de los extremos. Incluso as,
ordenamiento rpido se ejecutar correctamente. La versin de ordenamiento rpido que
desarrollaremos selecciona el elemento central de la matriz como comparando.
l. Cree un archivo con el nombre QSDerno . java.
2. En primer lugar, cree la clase Quicks ort:
11 Ejercicio 6.3 : Una sencil l a versin de rpido
class Quicksort {
11 Configurar la invocacin del mtodo Quicksort .
static void qsort(char items[ ] ) {
qs (items , O, items.length-1);
11 Una versin recursiva de Quicksort para caracteres .
private static void qs (char items[] , int left , int right)
{
i nt i , j ;
c har x , y ;
i left ; j = right;
x = items[(left+right)/2);
do
while ( (items[i) < x) && (i < right)) i++;
while((x < items[j)) && (j > left)) j --;
if(i <= j)
y= items[i);
items[i) = iterrs[j) ;
items[j) =y;
i++ ; j --;
while(i <= j ) ;
if ( left < j) qs(items, left , j );
if(i < right ) qs(items , i, right) ;
Java 7 flJI
Para simplificar la interfaz de ordenamiento rpido, la clase Quicksort cuenta
con el mtodo qso rt ( ),que define la invocacin del mtodo, q s () .Esto permite
invocar Quic ksor t con el nombre de la matriz que ordenar, sin necesidad de
indicar una particin inicial. Como q s () slo se usa internamente, se especifica
como priva te.
3. Para usar Quicks or t , basta con invocar Quicksort . qsort ( ) .Como qsort ()
se especifica como static, se puede invocar a travs de su clase en vez de un
objeto. Por tanto, no es necesario crear un objeto Quicksor t . Tras la invocacin,
la matriz se ordena. Recuerde que esta versin slo funciona con matrices de
caracteres pero puede adaptarla a otros tipos distintos.
4. Veamos un programa que ilustra el uso de Qui cks ort:
// Ejercicio 6 . 3: Una sencilla versin de ordenamiento rpido.
class Quicksort {
// Configurar la invocacin del mtodo Quicksort.
static void qsort(char items[)) {
qs(items , O, items.length-1) ;
// Una versin recursiva de Quicksort para caracteres.
private static void qs(char items[) , int left , int right)
int i , j;
char x , y;
i left ; j = right ;
x = items[ ( left+right)/2);
do
while((items[i) < x) && (i < right)) i++;
while((x < items[ j )) && (j > left)) j --;
f f ..J:I 6. Anlisis detallado de mtodos y clases
if(i <= j) {
y = items [i];
items[i] = items[j] ;
items[j] =y;
i++; j --;
while (i <= j) ;
if(left < j) qs(items , left, j) ;
if(i < right) qs(items, i, right) ;
class QSDemo {
public static void main(String args[]) {
c har a [ l = { ' d ', 'x', ' a', ' r ', ' p' , ' j ', ' i ' l ;
int i;
System. out .print("Original array: " ) ;
for(i=O; i < a . length; i++)
System. out . print(a[i]);
System. out . println() ;
11 ordenar la matriz
Quicksort.qsort(a) ;
System.out . print( " Sorted array: " );
for(i=O; i < a . length; i++)
System. out . print(a[i]);
Clases anidadas e internas
En Java, puede definir una clase anidada, una clase que se declara dentro de otra.
En realidad, es un terna avanzado. De hecho, las clases anidadas no se permitan en la
primera versin de Java. No se aadieron hasta Java 1.1. No obstante, conviene que las
conozca y su mecanismo de uso, ya que desempean un importante papel en las apli-
caciones reales.
Una clase anidada no existe independientemente de su clase contenedora. Por tanto.
el mbito de una clase anidada lo define su clase exterior. Una clase anidada que se
declara directamente en el mbito de su clase contenedora es miembro de esta. Tambin
se puede declarar una clase anidada que sea local de un bloque.
Hay dos tipos generales de clases anidadas: las precedidas del modificador stac .. :::
y las que no lo estn. El nico tipo que veremos en el libro es la variante no esttica. Es
tipo de clase anidada tambin se denomina clase interna. Tiene acceso a todas las varia-
bles y mtodos de su clase externa y puede hacer referencia a los mismos directamen!:e
corno hacen otros miembros no estticos de la clase externa.
fava7 fjD
En ocasiones se usan clases internas para ofrecer una serie de servicios que slo se
:isan en su clase contenedora. El siguiente ejemplo usa una clase interna para calcular
distintos valores para su clase contenedora:
Usar una clase interna.
:::ass Outer {
int nums[ );
Outer (int n[))
nums = n ;
void analyze () {
Inner inOb = new Inner() ;
System.out . println( "Minimum: " + inOb . min()) ;
System. out . println("Maximum: " + inOb.max() ) ;
System. out.println( "Average : " + inOb.avg());
11 Una clase interna .
class Inner {
int min () {
int m = nums[O] ;
for(int i=l; i < nums .length; i++)
if(nums[i) < m) m = nums[i) ;
return m;
int max () {
int m = nums[O];
for(int i=l; i < nums . length; i ++ )
if(nums[i) > m) m = nums[i) ;
return m;
int avg () {
int a = O;
for(int i=O; i < nums . length; i++)
a+= nums[i);
return a / nums.length;
class NestedClassDemo {
public static void main(String args[))
int x[ ) = { 3, 2 , 1 , 5, 6, 9 , 7 , 8 } ;
Outer outOb = new Outer(x);
outb.analyze();
filI
6. Anlisis detallado de mtodos y clases
Este programa genera el siguiente resultado:
Minimum: 1
Maximum: 9
Aver age : 5
En este ejemplo, la clase interna Inner calcula distintos valores de la matriz nurns,
que es miembro de Outer. Como hemos indicado, una clase interna tiene acceso a los
miembros de su clase contenedora, por lo que I nne r puede acceder directamente a la
matriz nurns. Evidentemente, lo contrario no es posible. Por ejemplo, a nal y ze ( ) no
podra invocar directamente el mtodo rnin () sin crear un objeto Inner.
Se puede anidar una clase en el mbito de un bloque. De esta forma se crea una clase
localizada desconocida en el exterior del bloque. El siguiente ejemplo adapta la clase
ShowBi ts del ejercicio 5.3 para usarla como clase local:
11 Usar ShowBits como clase local .
c l ass LocalClassDemo {
public static void main(String args[])
// Una versin de clase interna de ShowBi ts .
class ShowBits { //Una clase local anidada e n un mtodo .
i nt numbits ;
ShowBits ( i n t n )
numbits = n ;
void show( l ong val)
long mask = l ;
11 desplazamiento izquierdo de u n 1 a su posicin correct a
mask <<= numbits-1 ;
int spacer = O;
for (; mask != O; mask >>>= 1 ) {
if ( (val & mask) ! = 0) System. out . p.::int (" l " ) ;
else System. out.print( " O");
spacer++;
if ( (spacer % 8 ) == 0)
System. out.print (" " ) ;
spacer = O;
System. out . println( ) ;
for (byte b = O; b < 10; b++) {
ShowBits byteval = new ShowBits(8 );
System. out.print(b + " in binary: ") ;
byteval . show (b ) ;
Java 7 fJll
Este programa genera el resultado mostrado en la figura 6.6.
C\.
'
me x .
G:'-Cod g-o J V<'t 7),j<tV<l LoL <.\ le
0 in b lil00000fl
l in h fl<.1.J'lj
2 .in h n<H'Y
J in h ll<\ry 01-nmm111
4 in h 0<1 0mm01m1
in h ndry 01'.10fll0!
6 in h fl<\ry rn
'/ in
"
n,\ry m1mw1111
8 in h n,_,ry
9 in h n.-;..1y f11'.I0.10f1.1
Figura 6.6. Ejemplo de uso de una clase local.
En este ejemplo, la clase ShowBi ts no se conoce fuera de main () y todo intento de
acceder a la misma por otro mtodo que no sea main () generar un error.
Por ltimo, puede crear una clase interna sin nombre, denominada clase interna
annima. Se crea una instancia de un objeto de una clase interna annima al declarar la
clase, por medio de new. Encontrar ms informacin al respecto en el captulo 15.
Preguntas al experto
P: Qu diferencia existe entre una clase anidada s ta ti e y otra que no lo sea?
R: Una clase anidada static tiene un modificador static aplicado. Al ser esttica,
slo puede acceder directamente a otros miembros s ta ti e de la clase contenedora.
Para acceder a otros miembros de la clase externa debe usar una referencia de
objeto.
Argumentos de longitud variable: varargs
En ocasiones, tendr que crear un mtodo que acepte un nmero variable de argu-
mentos, en funcin de su uso preciso. Por ejemplo, un mtodo que abra una conexin
a Internet puede aceptar el nombre de un usuario, la contrasea, nombre de archivo,
protocolo y dems, pero proporcionar valores predeterminados si parte de esta infor-
macin no se proporciona. En este caso, sera recomendable pasar slo los argumentos a
los que no se apliquen los valores predeterminados. Para crear dicho mtodo se necesita
una forma de crear una lista de argumentos de longitud variable, no fija.
En el pasado, los mtodos que requeran una lista de argumentos de longitud variable
se podan controlar de dos formas, ninguna especialmente atractiva. Por un lado, si el
nmero mximo de argumentos era reducido y conocido, se podan crear versiones sobre-
cargadas del mtodo, una para cada forma de invocar el mtodo. Aunque esto funcione y
resulte adecuado en determinados casos, slo se aplica a situaciones concretas. En casos
en los que el nmero mximo de posibles argumentos es mayor o desconocido, se usaba
f Jtj 6. Anlisis detallado de mtodos y clases
un segundo enfoque en el que los argumentos se incluan en una matriz, que despus
se pasaba al mtodo. Francamente, ambos enfoques no resultaban elegantes y se hizo
evidente la necesidad de un enfoque ms adecuado.
Desde el JDK 5, se abord esta necesidad con la inclusin de una funcin que simpli-
ficaba la creacin de mtodos que requeran un nmero variable de argumentos. Esta
funcin es varags, abreviatura de argumentos de longitud variable. Un mtodo que
acepta un nmero variable de argumentos se denomina mtodo varargs. La lista de
parmetros de un mtodo varargs no es fija sino de longitud variable. Por tanto, un
mtodo varargs puede aceptar un nmero variable de argumentos.
Fundamentos de varargs
Un argumento de longitud variable se especifica por medio de tres puntos ( ... ). El
siguiente ejemplo crea el mtodo va Test () que acepta un nmero variable de argu-
mentos:
11 vaTest() usa un argumento de longitud variable.
static void vaTest(int ... v) //Declarar una lista de argumentos de longitud
variable.
System. out . println( "Number of args: " + v . length);
System. out.println( "Contents : " );
for(int i=O; i < v . length; i++)
System.out . println(
11
arg " + i +
11

11
+ v[i]);
System. out . println();
La declaracin de v es la siguiente:
int ... V
Esta sintaxis indica al compilador que va Test () se puede invocar con cero o ms
argumentos. Es ms, hace que v se declare implcitamente como matriz de tipo int [].
Por tanto, dentro de va Test (),se accede a v por medio de la sintaxis estndar para
matrices.
Veamos un programa completo que ilustra va Test () :
11 Ejemplo de argumentos de longitud variable .
class VarArgs {
11 vaTest() usa un argumento de longitud variable .
static void vaTest(int ... v) {
System. out . println(
11
Number of args: " + v . length);
System.out.println(
11
Contents: " );
for(int i=O; i < v.length; i++)
System.out . println(
11
arg " + i + "
11
+ v[i]);
System. out . println();
public static void main(String args[])
{
11 Se puede invocar vaTest() con un
11 nmero variable de argumentos .
vaTest(lO) ; 11 1 argumento
vaTest(l , 2 , 3) ; 11 3 argumentos
vaTest() ; //sin argumentos
Este programa genera el siguiente resultado:
Number of args : 1
Contents:
arg O: 10
Number of args : 3
Contents:
arg o: 1
arg 1: 2
arg 2 : 3
Number of args: o
Contents:
Java7 fJd
Dos aspectos importantes sobre este programa. Por un lado, dentro de va Test (),
se utiliza v como si fuera una matriz. Se debe a que v es una matriz. La sintaxis ...
simplemente indica al compilador que se usar un nmero variable de argumentos y que
stos se almacenarn en la matriz a la que hace referencia v. Por otra parte, en rna i n (),
se invoca va Tes t ( ) con diferente nmero de argumentos, incluso sin ellos. Los argu-
mentos se aaden automticamente a una matriz y se pasan a v. En el caso de no utilizar
argumentos, la longitud de la matriz es cero.
Un mtodo puede tener parmetros normales adems de un parmetro de longitud
variable. Sin embargo, el parmetro de longitud variable debe ser el ltimo declarado
por el mtodo. Por ejemplo, esta declaracin de mtodo es perfectamente vlida:
int doit(int a, int b , doubl e c, int .. . vals) {
En este caso, los tres primeros argumentos usados en la invocacin de do I t ( ) coin-
ciden con los tres primeros parmetros. Tras ello, se asume que los argumentos restantes
pertenecen a va ls.
Veamos una versin modificada del mtodo va Tes t () que acepta un argumento
convencional y otro de longitud variable:
// Usar varargs con argumentos estndar .
class VarArgs2 {
11 Aqu , msg es un parmetro normal y ves
11 un parmetro varargs .
static void vaTest(String msg, int .. . v) {
System. out.println(msg + v . length);
fi!I
6. Anlisis detallado de mtodos y clases
System. out . println( " Contents : " ) ;
for(int i=O ; i < v . length; i++)
System. out . println( " arg " + i + " " + v[i]) ;
System.out . println() ;
public static void main (String args[])
{
vaTest( "One vararg: ", 10) ;
vaTest( "Threevarargs: ", 1, 2 , 3) ;
vaTest( " No varargs : ");
Este programa genera el siguiente resultado:
One vararg : 1
Contents :
arg O: 10
Three varargs:
Contents :
arg o: 1
arg 1 : 2
arg 2 : 3
No varargs: O
Contents :
3
Recuerde que el parmetro v arargs debe ser el ltimo. Por ejemplo, la siguiente
declaracin no es correcta:
int doit(int a , int b, double e, int .. . vals, boolean stopFlag) { // Error!
Aqu, se intenta declarar un parmetro normal tras el parmetro varargs, algo
rrecto. Existe otra restriccin, slo debe haber un parmetro var args. Por ejemplo, esta
declaracin tampoco es vlida:
int doit (int a , int b , double e, int .. . vals , double . .. morevals) { // Error!
El intento de declarar el segundo parmetro var args no es vlido.
Sobrecargar mtodos varargs
Puede sobrecargar un mtodo que acepte un argumento de longitud
ejemplo, el siguiente programa sobrecarga va Tes t () tres veces:
//Argumentos de longitud variable y sobrecarga .
class VarArgs3 {
static void vaTest(int . . . v) { // Primera versin de vaTest()
Java 7 WJJOj
System. out . println("vaTest(int ... ) : " + " Number of args : " + v.length);
System. out.println( "Contents: " ) ;
for(int i=O; i < v.length; i++)
System.out . println( " arg " + i + " "+ v(i]);
System. out . println() ;
static void vaTest(boolean ... v) { //Segunda versin de vaTest()
System. out.println( "vaTest(boolean . .. ) : " + "Number of args : "+ v.length) ;
System. out . println ( "Contents: ");
for(int i=O; i < v.length; i++)
System. out . println( " arg " + i + " " + v[i]);
System. out . println();
static void vaTest(String msg, int .. . v) { // Tercera versin de vaTest()
System.out . println( "vaTest (String, int ... ) : " + msg + v . length);
System. out.println("Contents: " );
for(int i=O; i < v . length; i++)
System. out . println( " arg " + i + " " + v[i]);
System.out . println() ;
public static void main(String args[])
{
vaTest(l , 2 , 3);
vaTest( "Testing: " 10, 20);
vaTest(true, false , false);
En la figura 6.7 puede ver un ejemplo de ejecucin.
Figura 6.7. Argumentos de longitud variable y sobrecarga
f.111 6. Anlisis detallado de mtodos y clases
El programa ilustra las dos formas de sobrecargar un mtodo varargs. En primer
lugar, los tipos de su parmetro varargs pueden diferir. Es el caso de va Test (int ... )
y va Test (boolean ... ) . Recuerde que ... hace que el parmetro se procese como
una matriz del tipo especificado. Por tanto, al igual que puede sobrecargar mtodos
con distintos tipos de parmetros de matriz, puede sobrecargar mtodos varargs con
distintos tipos de varargs. En este caso, Java usa la diferencia de tipos para determinar
qu mtodo sobrecargado invocar.
La segunda forma de sobrecargar un mtodo vara rg s consiste en aadir uno o varios
parmetros normales, como en vaTest (Stri ng, int ... ) . En este caso, Java usa tanto
el nmero de argumentos como el tipo para determinar el mtodo que invocar.
Varargs y la ambigedad
Se pueden producir errores inesperados al sobrecargar un mtodo que acepte un
argumento de longitud variable. Estos errores implican ambigedad, ya que se puede
crear una invocacin ambigua a un mtodo vara rg s sobrecargado. Fjese en el siguiente
programa:
11 Argumentos de longitud variable , y ambigedad.
11
11 Este programa contiene un error y
// no se compilar .
class VarArgs4 {
11 Usar un parmetro vararg int .
static. void vaTest (i nt ... v) {
/ / ...
11 Usar un parmetro vararg boolean .
static void vaTest(boolean . .. v) {
/ / . ..
public static void main (String args[])
{
vaTest (1 , 2 , 3) ; // Correcto
vaTest(true , false, false); 11 Correcto
vaTest() ; // Error: ambiguo!
En este programa, la sobrecarga de va Test () es totalmente vlida. Sin embargo, el
programa no se compilar debido a la siguiente invocacin:
vaTest() ; // Error: ambiguo!
Como el parmetro vararg puede estar vaco, esta invocacin podra traducirse
unainvocacinde vaTest (int ... ) ode vaTest (boolean .. . ) . Ambas son vlidas...
Por tanto, la invocacin es inherentemente ambigua.
Java 7 fJfl
Veamos otro ejemplo de ambigedad. Las siguientes versiones sobrecargadas de
va Test () son inherentemente ambiguas aunque una acepte un parmetro normal:
static void vaTest (int ... v) { J /
static void vaTest (int n , int . .. v) { // ...
Aunque las listas de parmetros de v a Test () difieran, el compilador no puede
resolver la siguiente invocacin:
vaTest(l)
Seguramente se pregunte si se traduce en una invocacin de v a Test (int ... ) con
un argumento varargs o en una invocacin de v aTest (int ... ) sin argumentos. El
compilador no puede saberlo, por lo que la situacin es ambigua.
Debido a estos errores de ambigedad, en ocasiones tendr que olvidarse de la sobre-
carga y usar dos nombres de mtodo distintos. Adems, en algunos casos, los errores de
ambigedad muestran un fallo conceptual del cdigo, que puede remediar sin disea
la solucin adecuada.
Evaluacin de conocimientos
1. Dado este fragmento de cdigo:
class X {
private int count ;
el siguiente fragmento es correcto?
class Y {
public static void main(String args[])
X ob = new X() ;
ob.count = 10;
2. Un modificador de acceso debe _______ la declaracin de un mtodo.
3. El complemento de una cola es una pila. Usa acceso FILO y suele compararse
con una pila de platos. El primer plato colocado en la mesa es el ltimo en utili-
zarse. Cree la clase de pila Stack para almacenar caracteres. Asigne los nombres
pus h () y pop () a los mtodos que accedan a la pila. Permita que el usuario
pueda especificar el tamao de la pila al crearla. Los dems miembros de la clase
Stack deben ser private. (Truco: Puede usar la clase Queue como modelo; basta
con cambiar el acceso a los datos.)
4. Dada la siguiente clase:
class Test {
int a;
Test (int i) { a i; )
f Ji:I 6. Anlisis detallado de mtodos y clases
cree un mtodo swap () que intercambie los contenidos de los objetos a los que
hacen referencia dos referencias de objetos Test.
5. Es correcto el siguiente fragmento de cdigo?:
class X {
int meth (int a, int b) { . . . }
String meth (int a , int b) { . . .
6. Cree un mtodo recursivo que muestre los contenidos de una cadena a la
inversa.
7. Si todos los objetos de una clase deben compartir la misma variable, cmo debe
declarar dicha variable?
8. Para qu utilizara un bloque s t a t ic?
9. Qu es una clase interna?
10. Para que un miembro sea accesible para los dems miembros de su clase, qu
modificador de acceso debe utilizar?
11. El nombre de un mtodo ms su lista de parmetros constituyen el ____ _
del mtodo.
12. Un argumento in t se pasa a un mtodo mediante una invocacin por
13. Cree un mtodo varargs con el nombre surn () que sume el valor int pasado a_
mismo. Debe devolver el resultado. Demuestre su uso.
14. Se puede sobrecargar un mtodo varargs?
15. Indique un ejemplo de mtodo varargs sobrecargado que sea ambiguo.

S-ar putea să vă placă și