Mostrando entradas con la etiqueta Unidad 2. Mostrar todas las entradas
Mostrando entradas con la etiqueta Unidad 2. Mostrar todas las entradas

miércoles, 22 de febrero de 2012

2.7 Sobrecarga de operadores

Para descargar el documento de este tema, haz clic aquí
 
Los operadores de Java son muy parecidos en estilo y funcionamiento a los de C. Tanto C, como C++, como Java, proporcionan un conjunto de operadores para poder realizar acciones sobre uno o dos operandos. Un operador que actúa sobre un solo operando es un operador unario, y un operador que actúa sobre dos operandos es un operador binario.
Algunos operadores pueden funcionar como unarios y como binarios, el ejemplo más claro es el operador - (signo menos). Como operador binario, el signo menos, el cual hace que el operando de la derecha sea sustraido al operando de la izquierda; como operador unario hace que el signo algebraico del operando que se encuentre a su derecha sea cambiado.
En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden de precedencia:
  .       []      ()
  ++      --
  !       ~       instanceof
  *       /       %
  +       -
  <<      >>      >>>
  <       >       <=       >=      ==      !=
  &       ^       |
  &&      ||
  ?  :
  =       op=     (*=      /=      %=     +=    -=   etc.) ,
Los operadores numéricos se comportan como esperamos:
    int + int = int
Los operadores relacionales devuelven un valor booleano.
Para las cadenas, se pueden utilizar los operadores relacionales para comparaciones además de + y += para la concatenación:
    String nombre = "nombre" + "Apellido";
El operador = siempre hace copias de objetos, marcando los antiguos para borrarlos, y ya se encargará el garbage collector de devolver al sistema la memoria ocupada por el objeto eliminado.
Java, a diferencia de C++, no soporta la sobrecarga de operadores. Esto significa que no es posible redefinir el entorno en el que actúa un operador con respecto a los objetos de un nuevo tipo que el programador haya definido como propios.
Un caso interesante, que se sale de la afirmación anterior, es el operador + (signo más), que se puede utilizar para realizar una suma aritmética o para concatenar cadenas (¡Java lo sobrecarga internamente!). Cuando el signo más se utiliza en esta última forma, el operando de la derecha se convierte automáticamente en una cadena de caracteres antes de ser concatenada con el operando que se encuentra a la izquierda del operador +. Esto asume que el compilador sabe que el operando de la derecha es capaz de soportar la conversión. Y ese conocimiento lo tiene porque comprueba todos los tipos primitivos y muchos de los tipos internos que se utilizan. Obviamente, el compilador no sabe absolutamente nada de los tipos que haya definido el programador.
A continuación se detallan algunos de los operadores que admite Java y que se suelen agrupar en operadores aritméticos, relacionales y condicionales, operadores lógicos y que actúan sobre bits y, finalmente, operadores de asignación.
Operadores Aritméticos
Java soporta varios operadores aritméticos que actúan sobre números enteros y números en coma flotante. Los operadores binarios soportados por Java son:
+
suma los operandos
-
resta el operando de la derecha al de la izquierda
*
multiplica los operandos
/
divide el operando de la izquierda entre el de la derecha
%
resto de la división del operando izquierdo entre el derecho
Como se ha indicado anteriormente, el operador más (+), se puede utilizar para concatenar cadenas, como se observa en el ejemplo siguiente:
"miVariable tiene el valor " + miVariable + " en este programa"
Esta operación hace que se tome la representación como cadena del valor de miVariable para su uso exclusivo en la expresión. De ninguna forma se altera el valor que contiene la variable.
En C++ no se soporta la concatenación de cadenas con el operador más (+); hay que utilizar la función strcat() para poder concatenar dos cadenas y el uso de esta función puede resultar peligroso porque no hay protección contra el traspaso de los límites del array que se utiliza como destino de la cadena concatenada.
El operador módulo (%), que devuelve el resto de una división, a diferencia de C++, en Java funciona con tipos en coma flotante además de con tipos enteros. Cuando se ejecuta el programa que se muestra en el ejemplo java401.java.
La salida que se obtiene por pantalla tras la ejecución del ejemplo es la que se reproduce a continuación:
%java java401
x mod 10 = 3
y mod 10 = 3.299999999999997
Los operadores unarios que soporta Java son:
+
indica un valor positivo
-
negativo, o cambia el signo algebraico
++
suma 1 al operando, como prefijo o sufijo
--
resta 1 al operando, como prefijo o sufijo
En los operadores de incremento (++) y decremento (--), en la versión prefijo, el operando aparece a la derecha del operador, ++x; mientras que en la versión sufijo, el operando aparece a la izquierda del operador, x++. La diferencia entre estas versiones es el momento en el tiempo en que se realiza la operación representada por el operador si éste y su operando aparecen en una expresión larga. Con la versión prefijo, la variable se incrementa (o decrementa) antes de que sea utilizada para evaluar la expresión en que se encuentre, mientras que en la versión sufijo, se utiliza la variable para realizar la evaluación de la expresión y luego se incrementa (o decrementa) en una unidad su valor.
Operadores Relacionales y Condicionales
Aunque Java y C++ soportan los mismos operadores relacionales, devuelven valores diferentes. Los operadores relacionales en Java devuelven un tipo booleano, true o false; mientras que en C++ devuelven un valor entero, en donde se puede considerar al valor cero como false y a un valor no-cero como true.
> 
el operando izquierdo es mayor que el derecho
>=
el operando izquierdo es mayor o igual que el derecho
< 
el operando izquierdo es menor que el derecho
<=
el operando izquierdo es menor o igual que el derecho
==
el operando izquierdo es igual que el derecho
!=
el operando izquierdo es distinto del derecho
Los operadores relacionales combinados con los operadores condicionales (o lógicos en C++), se utilizan para obtener expresiones más complejas. Los operadores condicionales que soporta Java son:
&&
expresiones izquierda y derecha son true
||
o la expresión izquierda o al expresión de la derecha son true
!
la expresión de la derecha es false
Los programadores C++ se encontrarán con que Java restringe el uso de estos operadores, porque en C++ la representación de true y false es muy diferente a Java. En Java, los operandos de estos operadores deben ser tipos booleanos, o expresiones que devuelvan un tipo booleano, mientras que en C++ puede ser cualquier tipo entero o expresión que devuelva un tipo entero, sin restricciones.
Una característica importante del funcionamiento de los operadores && y ||, tanto en Java como en C++ (y que es pasado a veces por alto, incluso por programadores experimentados) es que las expresiones se evalúan de izquierda a derecha y que la evaluación de la expresión finaliza tan pronto como se pueda determinar el valor de la expresión. Por ejemplo, en la expresión siguiente:
    ( a < b ) || ( c < d )
si la variable a es menor que la variable b, no hay necesidad de evaluar el operando izquierdo del operador || para determinar el valor de la expresión entera. En casos de complejas y largas expresiones, el orden en que se realizan estas comprobaciones puede ser fundamental, y cualquier error en la colocación de los operandos puede dar al traste con la evaluación que se desea realizar y, estos errores son harto difíciles de detectar, ya que se debe estudiar concienzudamente el resultado de las expresiones en tiempo de ejecución para poder detectar el problema.
Operadores a Nivel de Bits
Java y C, C++ comparten un conjunto de operadores que realizan operaciones sobre un solo bit cada vez. Java también soporta el operador >>>, que no existe en C o C++ y, además, el entorno en el que se realizan algunas de las operaciones no siempre es el mismo en Java que en los otros lenguajes.
Los operadores de bits que soporta Java son los que aparecen en la siguiente tablita:
Operador
Uso
Operación
>> 
Operando >> Despl
Desplaza bits del operando hacia la derecha las posiciones indicadas (con signo)
<< 
Operando << Despl
Desplaza bits del operando hacia la izquierda las posiciones indicadas
>>> 
Operando >>> Despl
Desplaza bits del operando hacia la derecha las posiciones indicadas (sin signo)
&
Operando & Operando
Realiza una operación AND lógiga entre los dos operandos
|
Operando | Operando
Realiza una operación OR lógica entre los dos operandos
^
Operando ^ Operando
Realiza una operación lógica OR Exclusiva entre los dos operandos
~
~Operando
Complementario del operando (unario)
En Java, el operador de desplazamiento hacia la derecha sin signo, rellena los bits que pueden quedar vacíos con ceros. Los bits que son desplazados fuera del entorno se pierden.
En el desplazamiento a la izquierda, hay que ser precavidos cuando se trata de desplazar enteros positivos pequeños, porque el desplazamiento a la izquierda tiene el efecto de multiplicar por 2 para cada posición de bit que se desplace; y esto es peligroso porque si se desplaza un bit 1 a la posición más alta, el bit 31 o el 63, el valor se convertirá en negativo.
Operadores de Asignación
El operador = es un operador binario de asignación de valores. El valor almacenado en la memoria y representado por el operando situado a la derecha del operador es copiado en la memoria indicada por el operando de la izquierda. Al contrario que en C++, el operador de asignación (ni ningún otro) no se puede sobrecargar en Java.
Java soporta toda la panoplia de operadores de asignación que se componen con otros operadores para realizar la operación que indique ese operador y luego asignar el valor obtenido al operando situado a la izquierda del operador de asignación. De este modo se pueden realizar dos operaciones con un solo operador.
+=   -=   *=   /=   %=   &=   |=   ^=   <<=
>>=  >>>=
Por ejemplo, las dos sentencias que siguen realizan la misma función:
    x += y;
    x = x + y;
Y las otras comprobaciones siguen el mismo patrón. C++ no soporta el operador >>>= porque tampoco soporta el operador a nivel de bits de desplazamiento sin signo (>>>).
Operadores Ternario if-then-else
Java, lo mismo que C y C++, soporta este operador ternario. No obstante, la construcción utilizada por este operador es lago confusa, aunque se puede llegar a entender perfectamente cuando uno lee en el pensamiento lo que está escrito en el código. La forma general del operador es:
    expresion ? sentencia1 : sentencia2
en donde expresion puede ser cualquier expresión de la que se obtenga como resultado un valor booleano, porque en Java todas las expresiones condicionales se evalúan a booleano; y si es true entonces se ejecuta la sentencia1 y en caso contrario se ejecuta la sentencia2. La limitación que impone el operador es que sentencia1 y sentencia2 deben devolver el mismo tipo, y éste no puede ser void.
Puede resultar útil para evaluar algún valor que seleccione una expresión a utilizar, como en la siguiente sentencia:
    cociente = denominador == 0 ? 0 : numerador / denominador
Cuando Java evalúa la asignación, primero mira la expresión que está a la izquierda del interrogante. Si denominador es cero, entonces evalúa la expresión que está entre el interrogante y los dos puntos, y se utiliza como valor de la expresión completa. Si denominador no es cero, entonces evalúa la expresión que está después de los dos puntos y se utiliza el resultado como valor de la expresión completa, que se asigna a la variable que está a la izquierda del operador de asignación, cociente.
En el ejemplo java402.java se utiliza este operador para comprobar que el denominador no es cero antes de evaluar la expresión que divide por él, devolviendo un cero en caso contrario. Hay dos expresiones, una que tiene un denominador cero y otra que no.
class java402 {
    public static void main( String args[] ) {
        int a = 28;
        int b = 4;
        int c = 45;
        int d = 0;
   
        // Utilizamos el operador ternario para asignar valores a
        // las dos variables e y f, que son resultado de la
        // evaluación realizada por el operador
        int e = (b == 0) ? 0 : (a / b);
        int f = (d == 0) ? 0 : (c / d);
        // int f = c / d;
   
        System.out.println( "a = " + a );
        System.out.println( "b = " + b );
        System.out.println( "c = " + c );
        System.out.println( "d = " + d );
        System.out.println();
        System.out.println( "a / b = " + e );
        System.out.println( "c / d = " + f );
        }
    }
El programa se ejecuta sin errores, y la salida que genera por pantalla es la que se reproduce:
%java java402
a = 28
b = 4
c = 45
d = 0
   
a / b = 7
c / d = 0
Si ahora cambiamos la línea que asigna un valor a la variable f, y quitamos este operador, dejándola como:
    int f = c / d;
se producirá una excepción en tiempo de ejecución de división por cero, deteniéndose la ejecución del programa en esa línea.
%java java402
java.lang.ArithmeticException: / by zero at java402.main(java402.java:40)
Errores comunes en el uso de Operadores
Uno de los errores más comunes que se cometen cuando se utilizan operadores es el uso incorrecto, o no uso, de los paréntesis cuando la expresión que se va a evaluar es compleja. Y esto sigue siendo cierto en Java.
Cuando se programa en C/C++, el error más común es el siguiente:
while( x = y ) {
    // . . .
    }
EL programador intenta realmente hacer una equivalencia (==) en vez de una asignación. En C/C++ el resultado de esta asignación siempre será true si y no es cero, y lo más seguro es que esto se convierta en un bucle infinito. En Java, el resultado de esta expresión no es un booleano, y el compilador espera un booleano, que no puede conseguir a partir de un entero, así que aparecerá un error en tiempo de compilación que evitará que el programa se caiga al ejecutarlo. Es decir, este error tan común, para que se dé en Java tienen que ser x e y booleanos, lo cual no es lo más usual.
Otro error muy difícil de detectar en C++ es cuando se utiliza el operador unario & cuando la lógica requiere un operador &&; o utilizar el operador unario |, en lugar del operador ||. Este es otro problema difícil de detectar porque el resultado es casi siempre el mismo, aunque sólo por accidente. En Java, no ocurre esto y se puede utilizar perfectamente el operador unario & como sinónimo del operador &&, o el operador | como sinónimo de ||; pero si se hace así, la evaluación de la expresión no concluirá hasta que todos los operandos hayan sido evaluados, perdiendo de esta forma las posibles ventajas que podría reportar la evaluación de izquierda a derecha.
Moldeo de Operadores
Es lo que se conoce como casting, refiriéndose a "colocar un molde". Java automáticamente cambia el tipo de un dato a otro cuando es pertinente. Por ejemplo, si se asigna un valor entero a una variable declarada como flotante, el compilador convierte automáticamente el int a float. El casting, o moldeo, permite hacer esto explícitamente, o forzarlo cuando normalmente no se haría.
Para realizar un moldeo, se coloca el tipo de dato que se desea (incluyendo todos los modificadores) dentro de paréntesis a la izquierda del valor. Por ejemplo:
void moldeos() {
    int i = 100;
    long l = (long)i;
    long l2 = (long)200;
    }
Como se puede ver, es posible realizar el moldeo de un valor numérico del mismo modo que el de una variable. No obstante, en el ejemplo el moldeo es superfluo porque el compilador ya promociona los enteros a flotantes cuando es necesario automáticamente, sin necesidad de que se le indique. Aunque hay otras ocasiones en que es imprescindible colocar el moldeo para que el código compile.
En C/C++ el moldeo puede causar auténticos quebraderos de cabeza. En Java, el moldeo es seguro, con la excepción de cuando se realiza una conversión estrecha, es decir, cuando se quiere pasar de un dato que contiene mucha información a otro que no puede contener tanta, en donde se corre el riesgo de perder información. Aquí es donde el compilador fuerza a que se coloque un moldeo expreso, indicando al programador que "esto puede ser algo peligroso, así que si quieres que lo haga, dímelo expresamente". Cuando se trata de una conversión ancha, no es necesario el moldeo explícito, ya que el nuevo tipo podrá contener más información que la que contiene el tipo original, no ocasionándose ninguna pérdida de información,

2.6 Sobrecarga de métodos

Descarga el documento de este tema aquí

Sobrecarga de Métodos.


En Java es posible definir dos o más métodos dentro de la misma clase que comparten el mismo  nombre, siempre y cuando sus declaraciones de parámetro son diferentes. Cuando esto sucede, los métodos se dicen a sobrecargarse, y el proceso se conoce como la sobrecarga de métodos. Sobrecarga
de métodos es una de las maneras que Java implementa el polimorfismo. Si nunca ha utilizado un lenguaje que permite la sobrecarga de métodos y, a continuación, el concepto puede parecer extraño al principio. Pero como puede ver, sobrecarga de métodos es una de las características más interesantes y útiles de Java. Cuando se invoca un método sobrecargado, Java utiliza el tipo o el número de argumentos como su guía para determinar la versión del método sobrecargado realmente llamar. Por lo tanto, los métodos sobrecargados deben ser diferente en el tipo o el número de sus parámetros. Aunque los métodos sobrecargados pueden tener diferentes tipos de devolución, el tipo devuelto por
sí sola no basta distinguir dos versiones de un método. Cuando Java encuentra una llamada a un método sobrecargado, simplemente ejecuta la versión del método cuyos parámetros coincidan con los argumentos utilizados en la llamada. Aquí está un ejemplo sencillo que ilustra la sobrecarga de métodos:


// Demostrando la sobrecarga de métodos.

class
OverloadDemo {


void test()
{


System.out.println("No
parameters");


}


// Overload
test for one integer parameter.


void
test(int a) {


System.out.println("a:
" + a);


}


// Overload
test for two integer parameters.


void
test(int a, int b) {


System.out.println("a
and b: " + a + " " + b);


}


// overload
test for a double parameter


double
test(double a) {


System.out.println("double
a: " + a);


return a*a;


}


}


class
Overload {


public
static void main(String args[]) {


OverloadDemo
ob = new OverloadDemo();


double
result;


// call all
versions of test()


ob.test();


ob.test(10);


ob.test(10,
20);


result =
ob.test(123.25);


System.out.println("Result
of ob.test(123.25): " + result);


}


}


Este programa genera el siguiente resultado:


No
parameters


a: 10


a and b: 10
20


double a:
123.25


Result of
ob.test(123.25): 15190.5625


Como puede ver, (de prueba) está sobrecargado cuatro veces.
La primera versión no toma parámetros, la segunda toma un parámetro de número
entero, la tercera toma dos parámetros de enteros, y la cuarta toma un
parámetro doble. El hecho de que la cuarta versión de prueba () también
devuelve un valor es sin consecuencias respecto a la sobrecarga, ya que tipos
de devolución no desempeñar un papel en la resolución de sobrecarga. Cuando se
llama a un método sobrecargado, Java busca una coincidencia entre los
argumentos utilizados para llamar el método y los parámetros del método. Sin
embargo, este partido no siempre necesita ser exacto. En algunos casos las
conversiones de tipo automático de Java pueden desempeñar un papel en la
resolución de sobrecarga. Por
ejemplo, considere el siguiente programa:


//
Automatic type conversions apply to overloading.


class
OverloadDemo {


void test()
{


}


// Overload
test for two integer parameters.


void
test(int a, int b) {


System.out.println("a
and b: " + a + " " + b);


}


// overload
test for a double parameter


void
test(double a) {


System.out.println("Inside
test(double) a: " + a);


}


}


class
Overload {


public
static void main(String args[]) {


OverloadDemo
ob = new OverloadDemo();


int i = 88;


ob.test();


ob.test(10,
20);


ob.test(i);
// this will invoke test(double)


ob.test(123.2);
// this will invoke test(double)


}


}


Este programa genera el siguiente resultado:


No
parameters


a and b: 10
20


Inside
test(double) a: 88


Inside
test(double) a: 123.2


Como puede ver, esta versión de OverloadDemo no define
test(int). Por lo tanto, cuando se llama a prueba () con un argumento entero
dentro de sobrecarga, no se encuentra ningún método coincidente. Sin embargo,
Java puede convertir automáticamente un número entero en un doble, y esta
conversión puede utilizarse para resolver la llamada. Por lo tanto, después de
que no se encuentra test(int), Java me eleva a doble y, a continuación, llama
test(double). Por supuesto, si test(int) se había definido, se habría llamado
en su lugar. Java empleará sus conversiones de tipo automático sólo si no se
encuentra ninguna coincidencia exacta. Sobrecarga de métodos soporta
polimorfismo porque es una manera de que Java implementa el paradigma de
"una interfaz, varios métodos". Para entender cómo, considere lo
siguiente. En lenguajes que no admiten la sobrecarga de métodos, debe darse un
nombre único a cada método. Sin embargo, con frecuencia se desea implementar
esencialmente el mismo método para diferentes tipos de datos. Considérese la
función valor absoluto. En lenguajes que no admiten la sobrecarga, generalmente
hay tres o más versiones de esta función, cada uno con un nombre ligeramente
diferente. Por ejemplo, en el C, el abs de función () devuelve el valor
absoluto de un número entero, laboratorios () devuelve el valor absoluto de un
entero largo y fabulosos () devuelve el valor absoluto de un valor de coma
flotante. Ya c no admite la sobrecarga, cada función debe tener su propio nombre,
a pesar de todas las funciones de tres es esencialmente la misma cosa.


Esto hace la situación más compleja, conceptualmente, lo que
realmente es. Aunque el concepto subyacente de cada función es el mismo,
todavía tiene tres nombres para recordar. Esta situación se produce en Java,
porque cada método de valor absoluto puede utilizar el mismo nombre. De hecho,
la biblioteca de clase estándar de Java incluye un método de valor absoluto,
llamado abs (). Este método está sobrecargado por la clase de matemáticas de
Java para manejar todos los tipos numéricos. Java determina qué versión de abs
() para llamar a basada en el tipo de argumento. El valor de la sobrecarga es
que permite que los métodos relacionados a ser visitada por el uso de un nombre
común. Así, el nombre abs representa la acción de carácter general que se está
realizando. Se deja al compilador para elegir la versión derecho específica
para una circunstancia particular. Usted, el programador, necesita sólo
recordar la operación general que se realiza. A través de la aplicación de
polimorfismo, varios nombres han sido reducidos a uno. Aunque este ejemplo es
bastante simple, que si expande el concepto, puede ver cómo puede ayudar la
sobrecarga, administrar la mayor complejidad.


 Cuando usted sobrecarga
un método, cada versión de dicho método puede realizar cualquier actividad que
desee. No hay regla que indica que los métodos sobrecargados deben relacionar
entre sí. Sin embargo, desde un punto de vista estilístico, la sobrecarga de
métodos implica una relación. Así, mientras que se puede utilizar el mismo
nombre para sobrecargar métodos no relacionados, usted no debe. Por ejemplo, se
podría utilizar el nombre sqr para crear métodos que devuelven el cuadrado de
un número entero y la raíz cuadrada de un valor de coma flotante. Pero estas
dos operaciones son fundamentalmente diferentes. Aplicar el método de la
sobrecarga en derrotas de esta manera su propósito original. En la práctica,
sólo se deben sobrecargar las operaciones estrechamente relacionadas.


Sobrecarga de Constructors.


Además de la sobrecarga de métodos normales, también puede
sobrecargar métodos de constructor. De hecho, para la mayoría las clases reales
que cree, constructores sobrecargados será la norma, no la excepción. Para
entender por qué, volvamos a la clase de cuadro desarrollada en el capítulo
anterior. Lo que sigue es la última versión del cuadro:


class Box {


double
width;


double
height;


double
depth;


// This is
the constructor for Box.


Box(double
w, double h, double d) {


width = w;


height = h;


depth = d;


}


// compute
and return volume


double
volume() {


return
width * height * depth;


}


}


Como se puede ver, el Box( ) constructor requiere tres
parámetros. Esto significa que todas las declaraciones de un cuadro de objetos
deben pasar tres argumentos para el constructor de box (). Por ejemplo, la
siguiente declaración es actualmente no válida:





Box ob =
new Box();


Box( ) requiere tres argumentos, es un error llamarlo sin
ellos. Esto plantea algunas cuestiones importantes. ¿Qué sucede si simplemente
quería un cuadro y no cuidado (o saber) lo que fueron sus dimensiones
iniciales? O, ¿qué sucede si desea poder inicializar un cubo mediante la
especificación de un único valor que se utilizaría para las tres dimensiones?
Como la clase de cuadro actualmente está escrita, estas otras opciones no están
disponibles para usted. Afortunadamente, la solución a estos problemas es muy fácil:
simplemente de sobrecarga del constructor de cuadro para que maneja las
situaciones que se acaba de describir. Aquí es un programa que contiene una
versión mejorada del cuadro que hace exactamente eso:





/* Here,
Box defines three constructors to initialize


the
dimensions of a box various ways.


*/


class Box {


double
width;


double
height;


double
depth;


//
constructor used when all dimensions specified


Box(double
w, double h, double d) {


width = w;


height = h;


depth = d;


}


//
constructor used when no dimensions specified


Box() {


width =
-1;  // use -1 to indicate


height =
-1; // an uninitialized


depth =
-1;  // box


}


//
constructor used when cube is created


Box(double
len) {


width =
height = depth = len;


}


// compute
and return volume


double
volume() {


return
width * height * depth;


}


}


class
OverloadCons {


public
static void main(String args[]) {


// create
boxes using the various constructors


Box mybox1
= new Box(10, 20, 15);


Box mybox2
= new Box();


Box mycube
= new Box(7);


double vol;


// get
volume of first box


vol =
mybox1.volume();


System.out.println("Volume
of mybox1 is " + vol);


// get
volume of second box


vol =
mybox2.volume();


System.out.println("Volume
of mybox2 is " + vol);


// get
volume of cube


vol =
mycube.volume();


System.out.println("Volume
of mycube is " + vol);


}


}


Aquí se muestra la salida producida por este programa:


Volume of
mybox1 is 3000.0


Volume of
mybox2 is -1.0


Volume of
mycube is 343.0


Como puede ver, se llama al constructor sobrecargado
adecuado en función de los parámetros especificados cuando se ejecuta en nuevo.


Uso de objetos como parámetros


Hasta ahora hemos sólo se ha utilizando tipos simples como
parámetros a los métodos. Sin embargo, es correcto y común para pasar objetos a
métodos. Por ejemplo,
considere el siguiente programa corto:


// Objects
may be passed to methods.


class Test
{


int a, b;


Test(int i,
int j) {


a = i;


b = j;


}


// return
true if o is equal to the invoking object


boolean
equals(Test o) {


if(o.a == a
&& o.b == b) return true;


else return
false;


}


}


class
PassOb {


public
static void main(String args[]) {


Test ob1 =
new Test(100, 22);


Test ob2 =
new Test(100, 22);


Test ob3 =
new Test(-1, -1);


System.out.println("ob1
== ob2: " + ob1.equals(ob2));


System.out.println("ob1
== ob3: " + ob1.equals(ob3));


}


}


Este programa genera el siguiente resultado:


ob1 == ob2:
true


ob1 == ob3:
false




Como puede ver, el método equals de () dentro de prueba
compara dos objetos para la igualdad y devuelve el resultado. Es decir, compara
el objeto invocando con uno que se pasa. Si contienen los mismos valores, el
método devuelve true. De lo contrario, devuelve false. Observe que el parámetro
o es igual a () especifica la prueba como su tipo. Aunque la prueba es un tipo
de clase creado por el programa, se utiliza en sólo del mismo modo que los
tipos integrados de Java. Uno de los usos más comunes de los parámetros del
objeto implica constructores. Frecuentemente se desea construir un nuevo
objeto, por lo que inicialmente es el mismo que algún objeto existente. Para
ello, debe definir un constructor que toma un objeto de su clase como un
parámetro. Por ejemplo, la siguiente versión del cuadro de permite que un
objeto inicializar otro:


// Here,
Box allows one object to initialize another.


class Box {


double
width;


double
height;


double
depth;


//
construct clone of an object


Box(Box ob)
{ // pass object to constructor


width =
ob.width;


height =
ob.height;


depth =
ob.depth;


}


//
constructor used when all dimensions specified


Box(double
w, double h, double d) {


width = w;


height = h;


depth = d;


}


//
constructor used when no dimensions specified


Box() {


width =
-1;  // use -1 to indicate


height = -1;
// an uninitialized


depth =
-1;  // box


}


//
constructor used when cube is created


Box(double
len) {


width =
height = depth = len;


}


// compute
and return volume


double
volume() {


return
width * height * depth;


}


}


class
OverloadCons2 {


public
static void main(String args[]) {


// create
boxes using the various constructors


Box mybox1
= new Box(10, 20, 15);


Box mybox2
= new Box();


Box mycube
= new Box(7);


Box myclone
= new Box(mybox1);


double vol;


// get
volume of first box


vol =
mybox1.volume();


System.out.println("Volume
of mybox1 is " + vol);


// get
volume of second box


vol =
mybox2.volume();


System.out.println("Volume
of mybox2 is " + vol);


// get
volume of cube


vol =
mycube.volume();


System.out.println("Volume
of cube is " + vol);


// get volume
of clone


vol =
myclone.volume();


System.out.println("Volume
of clone is " + vol);


}


}

Como puede ver cuando comience a crear sus propias clases,
proporcionando muchas formas de métodos de constructor normalmente es necesario
para permitir que los objetos que se construirán en una manera conveniente y
eficiente.




Un vistazo a pasar  argumento.



En general, hay dos maneras de que un lenguaje de
programación puede pasar un argumento a una subrutina. La primera es llamada
por valor. Este método copia el valor de un argumento en el parámetro formal de
la subrutina. Por lo tanto, los cambios realizados en el parámetro de la
subrutina no tienen efecto sobre el argumento. La segunda manera que puede
pasar un argumento es llamada por referencia. En este método, se pasa una
referencia a un argumento (no el valor del argumento) para el parámetro. Dentro
de la subrutina, esta referencia se utiliza para acceder el argumento real
especificado en la llamada. Esto significa que los cambios realizados en el
parámetro afectará el argumento que se utiliza para llamar a la subrutina. Como
puede ver, Java utiliza ambos enfoques, en función de lo que se pasa. En Java,
al pasar un tipo simple a un método, se pasa por valor. Por lo tanto, lo que se
produce al parámetro que recibe el argumento no tiene efecto fuera del método. Por ejemplo, considere el siguiente
programa:


// Simple
types are passed by value.


class Test
{


void
meth(int i, int j) {


i *= 2;


j /= 2;


}


}


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.meth(a,
b);


System.out.println("a
and b after call: " +


a + "
" + b);


}


}


La salida de este programa se muestra aquí:


a and b
before call: 15 20


a and b
after call: 15 20


Como puede ver, las operaciones que se producen dentro de
meth() no tienen ningún efecto en los valores de un y b utilizado en la
llamada; sus valores aquí no cambió a 30 y 10. Cuando se pasa un objeto a un
método, la situación cambia drásticamente, debido a que los objetos se pasan
por referencia. Tenga en cuenta que cuando se crea una variable de un tipo de
clase, sólo se crea una referencia a un objeto. Así, cuando se pasa esta
referencia a un método, el parámetro que recibe se referirá al mismo objeto
como la que se refirió el argumento. Esto significa que los objetos se pasan a los
métodos por uso de llamada por referencia. Cambios en el objeto dentro del
método afectan al objeto que se utiliza como argumento. Por ejemplo, considere
el siguiente programa:


// Objects
are passed by reference.


class Test
{


int a, b;


Test(int i,
int j) {


a = i;


b = j;


}


// pass an
object


void
meth(Test o) {


o.a *=  2;


o.b /= 2;


}


}


class
CallByRef {


public
static void main(String args[]) {


Test ob =
new Test(15, 20);


System.out.println("ob.a
and ob.b before call: " +


ob.a +
" " + ob.b);


ob.meth(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: 30 10


Como puede ver, en este caso, las acciones () de
metanfetaminas han afectado el objeto utilizado como argumento. Como un punto
de interés, cuando una referencia de objeto se pasa a un método, la propia
referencia se pasa por el uso de la llamada por valor. Sin embargo, dado que el
valor que se pasa se refiere a un objeto, la copia de ese valor todavía se
referirá al mismo objeto que hace de su argumento correspondiente.


Returning Objects


Un método puede devolver cualquier tipo de datos, incluidos
los tipos de clase que se crean. Por ejemplo, el siguiente programa, el método
de incrByTen () devuelve un objeto en el que el valor de un diez es mayor que
en el objeto invocando.


// Returning
an object.


class Test
{


int a;


Test(int i)
{


a = i;


}


Test
incrByTen() {


Test temp =
new Test(a+10);


return
temp;


}


}


class RetOb
{


public
static void main(String args[]) {


Test ob1 =
new Test(2);


Test ob2;


ob2 =
ob1.incrByTen();


System.out.println("ob1.a:
" + ob1.a);


System.out.println("ob2.a:
" + ob2.a);


ob2 =
ob2.incrByTen();


System.out.println("ob2.a
after second increase: "


+ ob2.a);


}


}


Aquí se muestra la salida generada por este programa:


ob1.a: 2


ob2.a: 12


ob2.a after
second increase: 22


Como puede ver, se invoca cada () incrByTen de tiempo, se
crea un nuevo objeto y, a continuación, se devuelve una referencia a la misma a
la rutina de llamada. El programa anterior hace otro punto importante: ya que
todos los objetos se asignan dinámicamente utilizando de nuevo, no es necesario
preocuparse por un objeto que se va fuera de ámbito, porque termina el método
en el que fue creado. El objeto seguirá existiendo mientras hay una referencia
a él en alguna parte de su programa. Cuando no hay referencias a él, el objeto
se ser reclamado la próxima vez que lleva a cabo la recolección


Recursion.


 Java soporta la
recursividad. Recursión es el proceso de definir algo de sí mismo. Lo que se
refiere a la programación Java, recursión es el atributo que permite un método
para llamar a sí mismo. Un método que se llama a sí mismo se dice que es
recursiva. El ejemplo clásico de la recursividad es el cómputo del factorial de
un número. El factorial de un número n es el producto de todos los números
enteros entre 1 y N. Por ejemplo, 3 factorial es 1 × 2 × 3 o 6. Aquí es cómo
puede calcularse un factorial por uso de un método recursivo:





// A simple
example of recursion.


class
Factorial {


// this is
a recursive function


int
fact(int n) {


int result;


if(n==1)
return 1;


result =
fact(n-1) * n;


return
result;


}


}


class
Recursion {


public
static void main(String args[]) {


Factorial f
= new Factorial();


System.out.println("Factorial
of 3 is " + f.fact(3));


System.out.println("Factorial
of 4 is " + f.fact(4));


System.out.println("Factorial
of 5 is " + f.fact(5));


}


}


La salida de este programa se muestra aquí:


Factorial
of 3 is 6


Factorial
of 4 is 24


Factorial of 5 is 120


Si no está familiarizado con los métodos recursiva, la
operación de hecho () puede parecer un poco confusa. Aquí es cómo funciona.
Cuando se llama (de hecho) con un argumento de 1, la función devuelve 1; de lo
contrario devuelve el producto de hecho (n–1) * n. Para evaluar esta expresión,
(de hecho) se llama con n–1. Este proceso se repite hasta que n es igual a 1 y
las llamadas al método comienzan a regresar.


Para entender mejor cómo funciona el método () de hecho,
vamos a través de un ejemplo corto. Cuando se calcula el factorial de 3, la
primera llamada a (de hecho) hará una segunda llamada a hacerse con un
argumento de 2. Esta invocación causará (de hecho) a ser llamada una tercera
vez con un argumento de 1. Esta llamada devuelve 1, que luego se multiplica por
2 (el valor de n en la invocación del segunda). Este resultado (2), a
continuación, se volvió a la invocación original de (de hecho) y multiplicado
por 3 (el valor original de n). Esto da la respuesta, 6. Puede ser interesante
insertar las declaraciones de Console.println () en (de hecho) que mostrará el
nivel de cada llamada y cuáles son las respuestas intermedias.


Cuando un método llama a sí mismo, parámetros y variables
locales nuevas están asignados a almacenamiento de información en la pila y se
ejecuta el código del método con estas nuevas variables desde el principio. Una
llamada recursiva no hace una copia nueva del método. Sólo los argumentos son
nuevos. Como cada llamada recursiva devuelve, los parámetros y variables
locales antiguas se quitan de la pila y se reanuda la ejecución en el momento
de la llamada dentro del método. Métodos de recursiva pueden decirse que
"telescopio" fuera y de vuelta. Versiones de recursiva de muchas
rutinas pueden ejecutar un poco más lentamente que el equivalente iterativo
debido a la mayor sobrecarga de las llamadas a funciones adicionales. Muchas
llamadas recursivas a un método podrían causar un desbordamiento de la pila.
Porque es de almacenamiento para los parámetros y las variables locales en la
pila y cada nueva llamada crea una nueva copia de estas variables, es posible
que la pila podría estar agotada. Si esto ocurre, el sistema de tiempo de
ejecución de Java producirá una excepción. Sin embargo, probablemente no tendrá
que preocuparse por esto a menos que una rutina recursiva se ejecutaen forma nativa.

lunes, 20 de febrero de 2012

2.5 Constructores y destructores

Para descargar el documento de este tema, haz clic aquí

Constructores.

Puede ser tedioso inicializar cada vez que se crea una instancia de todas las variables en una clase. Incluso al agregar funciones de conveniencia como setDim (), es más simple y más conciso tener toda la configuración en el momento en que el objeto se crea por primera vez. Debido a la exigencia de inicialización es tan común, Java permite inicializar ellos mismos cuando se crean los objetos. Esta inicialización automática se realiza mediante el uso de un constructor.
Un constructor inicializa un objeto inmediatamente tras su creación. Tiene el mismo nombre que la clase en la que reside y es sintácticamente similar a un método. Una vez definido, automáticamente se llama al constructor, inmediatamente después de crear el objeto, antes de que finalice el operador new. Constructores de mirar un poco extraños porque no tienen ningún tipo de valor devuelto, no incluso nula. Esto es porque la implícita volver tipo de constructor de una clase es el propio tipo de clase. Es trabajo del constructor para inicializar el estado interno de un objeto, por lo que el código de creación de una instancia tendrán un objeto totalmente inicializado, utilizable inmediatamente. Usted puede repasar el ejemplo de cuadro para que las dimensiones de un cuadro se inicializan automáticamente cuando se construye un objeto. Para ello, reemplazar (de setDim) con un constructor. Comencemos por definir un constructor simple que simplemente establece las dimensiones de cada cuadro en los mismos valores. Esta versión se muestra aquí:
/* Here, Box uses a constructor to initialize the
dimensions of a box.
*/
class Box {
double width;
double height;
double depth;
// This is the constructor for Box.
Box() {
System.out.println("Constructing Box");
width = 10;
height = 10;
depth = 10;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class BoxDemo6 {
public static void main(String args[]) {
// declara, alojat, e inicializa objetos Caja
Box mybox1 = new Box();
Box mybox2 = new Box();
double vol;
// obtiene el volumen de la primer caja
vol = mybox1.volume();
System.out.println("Volumen es " + vol);
// obtiene el volumen de la segnda caja
vol = mybox2.volume();
System.out.println("Volumen es " + vol);
}
}
 
Cuando se ejecuta este programa, que genera los siguientes resultados:
Constructing Box
Constructing Box
Volumen es 1000.0
Volumen es 1000.0
Como puede ver, mybox1 y mybox2 fueron inicializa el constructor de cuadro () cuando fueron creados. Ya que el constructor da a todos los cuadros de las mismas dimensiones, 10 por 10 por 10, mybox1 y mybox2 tendrá el mismo volumen. La declaración de () Console.println () del cuadro es por el bien de la ilustración sólo. La mayoría de los constructores no mostrarán nada. Inicializará simplemente un objeto. Antes de pasar, vamos reexaminar el operador new. Como ustedes saben, cuando asigna un objeto, se utiliza el siguiente formulario general: clase-var = (; nuevo classname) Ahora usted puede entender por qué son necesarios los paréntesis después del nombre de la clase. Lo que realmente sucede es que se llama al constructor de la clase. Así, en la línea
Box mybox1 = new Box();
new Box( ) está llamando a  Box( ) constructor. Cuando no se define explícitamente un constructor de una clase, Java crea un constructor predeterminado para la clase. Por eso, la línea de código anterior funcionaba en versiones anteriores del cuadro que no define un constructor. El constructor predeterminado inicializa automáticamente todas las variables de instancia a cero. El constructor predeterminado suele ser suficiente para las clases de la simples, pero que generalmente no hará para más sofisticadas. Una vez que usted defina su propio constructor, ya no se utiliza el constructor predeterminado.

Constructores Parametrizados

 
While the Box( ) constructor en el ejemplo anterior inicializar un objeto de cuadro, no es muy útil — todos los cuadros de tengan las mismas dimensiones. Lo que se necesita es una manera de construir un cuadro de objetos de diferentes dimensiones. La solución fácil es añadir parámetros al constructor. Como usted probablemente puede suponer, esto hace mucho más útil. Por ejemplo, la siguiente versión de cuadro define un constructor parametrizado que define las dimensiones de un cuadro según lo especificado por los parámetros. Preste especial atención a cómo se crean los objetos del cuadro.
 
 
 
 
/* Aquí, Box usa un constructor parametrizado para
inicializar las dimensiones de la caja
*/
class Box {
double width;
double height;
double depth;
// Este es el constructor para la caja
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// computa y devuelve el volumen
double volume() {
return width * height * depth;
}
}
class BoxDemo7 {
public static void main(String args[]) {
// declara, aloja, e inicializa objetos Box
Box mybox1 = new Box(10, 20, 15);
Box mybox2 = new Box(3, 6, 9);
double vol;
// obtiene el volumen de la primer caja
vol = mybox1.volume();
System.out.println("Volumen es " + vol);
// obtiene el volumen de la segunda caja
vol = mybox2.volume();
System.out.println("Volumen es " + vol);
}
}
La salida de este programa se muestra aquí:
Volumen es 3000.0
Volumen es 162.0
Como puede ver, cada objeto se inicializa como se especifica en los parámetros de su constructor. Por ejemplo, en la línea siguiente,
Box mybox1 = new Box(10, 20, 15);
los valores de 10, 20 y 15 se pasan al cuadro de constructor () cuando nuevo crea el objeto. Por lo tanto, copia del mybox1 de anchura, altura y profundidad contendrá los valores de 10, 20 y 15, respectivamente.
 
Variable instancia Hiding…. Como ustedes saben, es ilegal en Java declarar dos variables locales con el mismo nombre dentro de los ámbitos de los mismos o envolventes. Curiosamente, puede hacer que las variables locales, incluidos los parámetros formales a métodos, que se superponen con los nombres de las variables de instancia de la clase. Sin embargo, cuando una variable local tiene el mismo nombre que una variable de instancia, la variable local oculta la variable de instancia. Por eso, la anchura, altura y profundidad no se utilizaron como los nombres de los parámetros al constructor () cuadro dentro de la clase de cuadro. Si hubieran sido, ancho habría mencionado para el parámetro formal, ocultando el ancho variable de instancia. Aunque es generalmente más fácil simplemente utilizar nombres diferentes, hay otra manera alrededor de esta situación. Porque esto le permite hacer referencia directamente al objeto, se puede utilizar para resolver cualquier conflictos de espacio de nombres que puedan surgir entre las variables de instancia y variables locales. Por ejemplo, aquí es otra versión de () del cuadro, que utiliza la anchura, altura y profundidad para los nombres de parámetro y, a continuación, utiliza para tener acceso a las variables de instancia con el mismo nombre:
 
 
// Usa this para reolver coalisiones nombre-espacio.
Box(double width, double height, double depth) {
this.width = width;
this.height = height;
this.depth = depth;
}
Advertencia: el uso de this en un contexto a veces puede ser confuso, y algunos programadores son cuidadosos de no utilizar nombres de parámetro formal que se ocultan las variables de instancia y variables locales. Por supuesto, otros programadores creen lo contrario: que es una buena Convención para utilizar los mismos nombres para mayor claridad y utilizar esto para superar el ocultamiento de variable de instancia. Es una cuestión de gusto decidir qué enfoque adoptar. Aunque no se trata de ningún valor significativo en los ejemplos que acabamos de ver, es muy útil en determinadas situaciones.
Garbage collector.
Dado que los objetos se asignan dinámicamente utilizando el operador new, quizás se pregunte cómo dichos objetos son destruidos y su memoria preparada para una reasignación posterior. En algunos lenguajes, como C++, la asignación dinámica de objetos debe ser liberada manualmente por el uso de un operador delete. Java adopta un enfoque diferente; maneja automáticamente la desasignación para usted. La técnica que logra esto se llama la recolección de basura. Funciona así: cuando no existe ninguna referencia a un objeto, se asume que ya no sera necesario, y la memoria ocupada por el objeto puede ser reclamada. No hay un operador explícito para destruir objetos como en C++. Garbage collector sólo ocurre esporádicamente durante la ejecución de su programa. No ocurrirá simplemente porque existen uno o más objetos que ya no se utilizan. Además, diferentes implementaciones de tiempo de ejecución de Java tendrá diversos enfoques para la recolección de basura, pero en su mayor parte, no debería tener que pensar en ello al escribir sus programas.
El método finalize ()
Alguna  veces en un objeto será necesario realizar alguna acción cuando este es destruido. Por ejemplo, si un objeto es manteine algún recurso no Java como una fuente de caracteres de control o ventana de archivo , a continuación, puede asegurarse de que estos recursos se liberan antes de que el objeto sea destruido. Para controlar este tipo de situaciones, Java proporciona un mecanismo denominado finalización. Mediante el uso de finalización, puede definir las acciones específicas que se producen cuando un objeto está a punto de ser reclamado por el recolector. Para agregar un finalizador a una clase, es simplemente definir el método finalize(). En tiempo de ejecución Java llama ese método cada vez que va a reciclar un objeto de esa clase. Dentro del método  finalize () deberá especificar las acciones que deben realizarse antes de que un objeto sea destruido. El recolector se ejecuta periódicamente, buscando objetos que ya no tengan referencia a cualquier Estado de ejecución o indirectamente a través de otros objetos que se hayan referenciados. Antes de que se libera un activo, Java llama al método finalize () en el objeto.
El método finalize() tiene esta forma general:
protected void finalize( )
{
// finalization code here
}
Aquí, la palabra clave protegida es un especificador que impide el acceso a finalizar () por código definido fuera de su clase. Esto y los otros especificadores de acceso se explican en el capítulo 7. Es importante entender que finalizar () sólo se llama justo antes de que la recolección. No se llama cuando un objeto se va fuera de ámbito, por ejemplo. Esto significa que usted no puede saber cuándo — o incluso si — finalizar se ejecutará (). Por lo tanto, el programa debería proporcionar otros medios de liberar recursos del sistema, etc., que se utiliza el objeto. No debe confiar en Finalizar () para la operación normal del programa.

Una clase de pila

La clase Box() mientras fue  útil para ilustrar los elementos esenciales de una clase, es de poco valor práctico. Para mostrar el poder real de clases, este capítulo concluirá con un ejemplo más sofisticado. Como recuerda, en la discusión de la programación orientada a objetos (OOP), presentada anteriormente, uno de los beneficios más importantes de programación orientada a objetos es la encapsulación de los datos y el código que manipula los datos. Como ha visto, la clase es el mecanismo por el cual se logra la encapsulación en Java. Mediante la creación de una clase, está creando un nuevo tipo de datos que define la naturaleza de los datos manipulados y las rutinas que se utiliza para manipularla. Además, los métodos de definición una interfaz coherente y controlada a datos de la clase. Por lo tanto, puede utilizar la clase a través de sus métodos sin tener que preocuparse de los detalles de su aplicación o cómo los datos realmente se administran dentro de la clase. En un sentido, una clase es como un "motor de datos". Ningún conocimiento de lo que ocurre dentro del motor se requiere para utilizar el motor a través de sus controles. De hecho, ya que se ocultan los detalles, su funcionamiento interno puede cambiar según sea necesario. Mientras el código utiliza la clase a través de sus métodos, detalles internos pueden cambiar sin causar efectos secundarios fuera de la clase.
Para ver una aplicación práctica de la discusión anterior, vamos desarrollar uno de los ejemplos arquetípicos de encapsulación: la pila. Una pila almacena datos utilizando pedidos  primero en entrar-, último en salir. Es decir, una pila como una pila de placas en una tabla — la primera placa presentada sobre la mesa es la última placa que se utiliza. Las pilas son controladas a través de dos operaciones, tradicionalmente llamado push y pop. Para poner un elemento en la parte superior de la pila, que utilizará la inserción. Para tener un elemento de la pila, se utilizará la pop. Como puede ver, es fácil encapsular el mecanismo de pila completo.
Aquí es una clase denominada pila que implementa una pila de números enteros:
// Esta clase define una pila de enteros que puede contener 10 valores.
class Stack {
int stck[] = new int[10];
int tos;
// Inicializa la pila
Stack() {
tos = -1;
}
// Pone un objeto en la pila
void push(int item) {
if(tos==9)
System.out.println("La pila está llena.");
else
stck[++tos] = item;
}
// Pone un objeto en el frente de la pila
int pop() {
if(tos < 0) {
System.out.println("Pila desbordada.");
return 0;
}
else
return stck[tos--];
}
}
Como puede ver, la clase Stack define los elementos de datos de dos y tres métodos. La pila de números enteros es celebrada por la stck de arreglo de discos. Esta matriz está indexada por la tos variable, que siempre contiene el índice de la parte superior de la pila. El pila () constructor inicializa tos a-1, que indica una pila vacía. El empuje de método () pone un elemento en la pila. Para recuperar un elemento, llame () pop. Dado que el acceso a la pila es a través de () push y pop (de), el hecho de que la pila se celebra en una matriz es realmente no pertinente al uso de la pila. Por ejemplo, la pila podría celebrarse en una estructura de datos más complicada, como una lista enlazada, sin embargo, la interfaz definido por () push y pop (de) seguirá siendo el mismo. La clase TestStack, que se muestra a continuación, muestra la clase de pila. Crea dos pilas de entero, empuja a algunos valores en cada uno y, a continuación, les aparece.
class TestStack {
public static void main(String args[]) {
Stack mystack1 = new Stack();
Stack mystack2 = new Stack();
// pone algunos numeros en la pila
for(int i=0; i<10; i++) mystack1.push(i);
for(int i=10; i<20; i++) mystack2.push(i);
// extrae esos numeros de la pila
System.out.println("Stack in mystack1:");
for(int i=0; i<10; i++)
System.out.println(mystack1.pop());
System.out.println("Stack in mystack2:");
for(int i=0; i<10; i++)
System.out.println(mystack2.pop());
}
}
 
 
Este programa genera el siguiente resultado:
Stack in mystack1:
9
8
7
6
5
4
3
2
1
0
Stack in mystack2:
19
18
17
16
15
14
13
12
11
10
 
Como puede ver, el contenido de cada pila está separado. Un último punto acerca de la clase de pila. Como actualmente se implementa, es posible que la matriz que contiene la pila, stck, a ser alterado por código fuera de la clase Stack. Esto deja a pila abierta al uso indebido o travesuras. En el siguiente tema, se verá cómo remediar esta situación.