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.

No hay comentarios:

Publicar un comentario