martes, 5 de agosto de 2008

Tips para el SCJP VI

Creando objetos
Thread[] threads = new Thread[5];
Son importar que el código pareciera decir lo contrario. El constructor de Thread no esta siendo invocado. No estamos creando una instancia de Thread, sino un arreglo de objetos. Es decir no existen hasta ahora objetos Thread.

Debido a ciertos atajos de sintaxis es posible crear objetos sin usar la palabra reservada new.

Si tenemos un arreglo de objetos, este arreglo no guarda los objetos, como lo haría un arreglo de primitivas, sino que guarda referencias a estos objetos.

Los arreglos tienen una variable publica llamada length que da el número de elementos caben en el arreglo. No obstante no nos dice si los elementos ya han sido inicializados.

int[] dots = {6,x,8};
La línea anterior realice 4 cosas:
  1. Declara una referencia a un arreglo de ints llamado dots
  2. Crea un arreglo con un tamaño de 3 (tres elementos).
  3. Llena el arreglo con los valores 6, 9 y 8.
  4. Asigna el nuevo arreglo a la referencia dots.

Esta sintaxis funciona de la misma manera para objetos.

Perro perrito = new Perro("Nash");
Perro[] misPerros = {perrito, new Perro("Blacky"), new Perro("Nala")};

Y por supuesto también se puede usar esta sintaxis para arreglos multidimensionales
int[][] scores = {{5,2,4,7}, {9,2}, {3,4}};

Existe también otra forma de crear arreglos e inicializarlos, para después asignarlos a una referencia que ya había sido declarada
int[] testScores;
testScores = new int[] {4,7,2};

Los arreglos de primitivas aceptan cualquier valor que pueda ser promovido implícitamente al tipo declarado. Es decir un arreglos de int puede aceptar cualquier variable menor de 32 bits.
int[] weightList = new int[5];
byte b = 4;
char c = 'c';
short s = 7;
weightList[0] = b; // OK, byte mas pequeño que int
weightlist[1] = c; // OK, char mas pequeño que int
weightList[2] = s; // OK, short mas pequeño que int

Para arreglos de objetos, es posible colocar subclases del tipo declarado dentro del arreglo.
Si el arreglo es declarado de tipo de alguna interfaz, entonces los elementos pueden hacer referencia a una instancia de cualquier clase que implemente esa interfaz.

Bloques de inicialización
Los bloques de inicialización son un lugar donde pueden realizarse operaciones. Estos corren cuando la clase es cargada (bloque de inicialización static) o cuando una instancia es creada (bloque de inicialización de una instancia)
class Ejemplo {
static int x;
int y;
static { x = 7 ; } // static init block
{ y = 8; } // instance init block
}

Los bloques de instancia se ejecutan después de la llamada a super dentro del constructor. Es decir después que todos los super constructores haya sido ejecutados.

Puede haber múltiples bloques de inicialización dentro de una clase. Es importante notar que a diferencia de los métodos o constructores, el orden en el que aparecen es importante. Cuando estos son ejecutados el orden es de arriba hacia abajo.

Utilizando los Wrappers o envoltorios.

  1. Son clases que están correlacionadas con los tipos primitivos
  2. Tienen dos funciones principales
  3. Envolver a los tipos primitivos para que puedan ser manejados como objetos
  4. Proveer métodos para conversión entre ellos
  5. Los métodos más importantes son:
  6. xxxValue() regresa un tipo primitivo (intValue,longValue, etc)
  7. parseXxx() Toma un String y regresa un tipo primitivo (parseInt,parseDouble,etc)
  8. valueOf() Toma un String, regresa un objeto envuelto

Autoboxing
Desde Java 5 existe algo que se llama Autoboxing que permite manejar un tipo Wrapper como si fuera un tipo primitivo por ejemplo:
Integer y = new Integer(567); // Crear objetos
y++; // quita el enovoltorio, lo incremente y pone el envoltorio
System.out.println("y = " + i); // imprimir
Este código pareciera usar el operador de incremento en un objeto. Pero lo que realmente sucede es que el compilador hace el trabajo por ti.

Cuando la JVM ve un método que toma un tipo primitivo pero que no concuerda exactamente entonces busca ampliarlo al siguiente tipo primitivo. Es decir short se puede pasar a int.

La pregunta que surge es, ¿el compilador prefiere ampliar una primitiva o usar el Autoboxing y los envoltorios?. La respuesta es, el compilador prefiere ampliar un tipo primitivo. Es decir si el compilador ve un short lo hara int antes que hacerlo Short.

Lo importante es entender que la ampliación de tipos se basa en herencia. Es decir usa una prueba de la relación “Es Un”. Por lo tanto no es valido decir que Short Es Un Integer.


En resumen:
  1. La ampliación de tipos primitivos usa el más pequeño argumento posible.
  2. Usados individualmente, el autoboxing y los Var-args son compatibles con la sobrecarga
  3. No se puede realizar la ampliación de un tipo envoltorio a otro (Prueba de Es Un va a fallar)
  4. No se puede ampliar y luego realizar el autoboxing (int no se puede convertir en Long)
  5. Pero si se puede realizar el autoboxing y luego ampliar (un int se vuelve vuelve un Integer, mediante Object)
  6. Se puede combinar Var-args con ampliación o autoboxing
  7. Usar == con envoltorios es engañoso. Ya que valores iguales y pequeños darán == verdadero, pero para valores mayores a 128 ya no.

Recolección de basura
  1. La recolección de basura provee un manejo de memoria automatizado
  2. El propósito de la GC es borrar objetos que sea inalcanzables
  3. Solo la JVM decide cuando ejecutar el GC, aunque tu puedes pedirlo.
  4. No puedes conocer con certeza el algoritmo que usa el GC
  5. Los objetos deben de ser considerados elegibles antes de que puedan ser recolectados
  6. Un objeto es elegible cuando ningún hilo vivo puede alcanzarlo
  7. Para alcanzar un objeto, debe haber una referencia viva a ese objeto
  8. Las aplicaciones Java se pueden quedar sin memoria
  9. Existen formas para decir a la JVM que puede eliminar a un objeto si lo desea.
  10. La primera es poner la referencia a ese objeto a null.
  11. También se puede cambiar la referencia de un objeto a otro.
  12. La tercera forma es cuando existen instancias que hacen referencias circulares entre sí pero no hay forma de acceder a alguna de ellas “desde afuera”.
  13. El método finalize por un objeto puede ser ejecutado, pero no debes de confiar en ello. Así que no pongas código en ese método. De hecho se recomienda no sobrescribir el método finaliza nunca.

No hay comentarios: