lunes, 26 de enero de 2009

Conseguir ser Programador Certificado en Java

Certificarse en Java es una de las primeras cosas que uno como Desarrollador de aplicaciones con tecnología Java, fija en su horizonte. En realidad para muchos se convierte en una utopía porque lo consideran difícil de conseguir, otras personas, consideran que no aporta nada a su desempeño, más que el simple hecho de demostrar de una forma un poco más tangible a los ojos de los empleadores que se es habil en los conceptos que manifiesta saber. Ambas formas de ver la certificación tienen mucho sentido. En lo que a mí respecta, considero injusto tener que pagarle a una empresa, para demostrar que uno sabe usar lo que ellos producen.

No obstante todo lo anterior y a mi pequeño inconformismo, me he dado a la tarea de iniciar mi proceso de certificación con el primer nivel de certificación java el SCJP CX-310-065. Se trata del primer escalón para el proceso de conseguir la tan anhelada certificación de Sun como arquitecto. El panorama pareciera no ser tan obscuro después de todo, pues a pesar de tener que pasar por distintos peldaños, finalmente se puede hacer carrera en el uso de una tecnología que ha venido creciendo y que ha tenido un considerable aumento en la receptividad y uso especialmente en los programas de formación en ingeniería de países subdesarrollados, que ven como una alternativa de ofrecer soluciones tecnológicas robustas a buen precio.

Después de la carreta, y cuando considero que seguramente ya ustedes se están cansando de leer, vamos a iniciar a ver lo que me interesaba registrar en éste Blog. Temas que serán tomados en cuenta en el Examen de certificación, ejemplos de aplicación y explicación.


Declaraciones, Inicialización y Alcance.

Las declaraciones en un programa Java pueden ser de dos tipos: Declaraciones de Clases, de métodos y de variables.

Iniciaremos con las declaraciones más simples:


Declaraciones de Variables.

Cuando haga declaraciones de variables, no use ningunas de las palabras reservadas del lenguaje. Estas son:

abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while.

Una declaración de variable en java es de la forma:

tipoVariable nombreVariable;

Notar que siempre va un punto y coma al final de la expresión. Así mismo, es posible efectuar distintas declaraciones de variables en una sola linea. El ejemplo siguiente muestra una forma de declarar cuatro variables distintas del tipo int:

int a=0, b=0,c=0,d;

Notar que las tres primeras variables fueron de una vez inicializadas en cero, y la cuarta simplemente fue declarada.

Antes de continuar con el tema de las distintas formas de declaración de variables, debemos considerar los distintos tipos de datos en Java:

Tipos de Datos primitivos

Los tipos de datos primitivos y su longitud es mostrada en el siguiente listado:

Enteros
byte longitud: 8 bits
short longitud: 16 bits
int longitud: 32 bits
long longitud: 64 bits

De punto flotante

float longitud: 32 bits
double longitud: 64 bits

De caracteres

char longitud: 16 bits

Booleanos

boolean No es un valor numérico, solo admite los valores true o false

void Tipo vacío.

Tipos de Datos Referenciados

Los tipos de datos referenciados son todos aquellos que se derivan de una clase y son denominados instancias u objetos. Como java es un lenguaje Orientado a Objetos, el manejo de los tipos de datos primitivos citados anteriormente también es posible desde la perspectiva de tipos de datos referenciados, las clases que manejan los distintos tipos primitivos son denominadas clases envolventes. Consisten en estructuras de datos que literalmente envuelven el tipo de dato primitivo, y los hacen manejables desde el punto de vista de estructuras complejas.

Hasta este punto, no hemos definido en concreto que son en realidad las referencias. Intentaré explicarlas de un modo gráfico antes que con tecnicismos. A diferencia de las variables de tipo primitivo que apuntan directamente a posiciones de memoria en donde se encuentran realmente los datos, las variables que apuntan a objetos, están en realidad almacenando referencias a donde se encuentran almacenados los objetos en la memoria física del ordenador.

Para ilustrar un poco mejor lo citado arriba, vamos a ver un ejemplo con un tipo de dato StringBuffer.

StringBuffer cadena = new StringBuffer("Saludos Comunidad Java.");
StringBuffer cadena1 = cadena;
cadena1.append(" Exitos en sus labores");
System.out.println(cadena);
System.out.println(cadena1);

Después de estas instrucciones, ¿que creen ustedes que está almacenado en la variable cadena y en la variable cadena1?

Las dos variables tienen “Saludos Comunidad Java. Exitos en sus labores”. Esto explica que cualquier cambio que se haga al contenido del objeto a través de cualquiera de sus referencias, es reflejado cuando sea consultado desde cualquiera de sus otras referencias.

Existe un caso excepcional con el tipo de dato String. Vamos a ver el comportamiento del mismo ejemplo, pero para el tipo de dato String.

String cadena = "Saludos Comunidad Java.";
String cadena1 = cadena;
cadena1 +=" Exitos en sus labores";

System.out.println(cadena);
System.out.println(cadena1);

En este ejemplo, la salida que se produce es:

Saludos Comunidad Java.
Saludos Comunidad Java. Éxitos en sus labores

Lo que quiere decir que el cambio en los datos de la variable que apuntaba a cadena, ha conseguido su propia referencia de memoria. Es decir que en este sentido, la variable String se comporta como un tipo de dato primitivo, en el que un cambio en alguna de las variables que apuntan a un valor, solo se ve reflejado para sí misma.

Ordenamientos propios del API de Java.

Java permite el ordenamiento de datos primitivos y de datos complejos cada uno usando una técnica distinta.

Ordenamiento automático de datos primitivos:

Vamos a citar como ejemplo el siguiente array de números enteros:

int [] num = {9,5,2,6,8,4,1,3,7};

Si necesitáramos tenerlos ordenados, acudiríamos a alguno de los métodos para ordenamientos como:

Ordenación por Intercambio (Burbuja).
Ordenación por Inserción (Inserción)
Ordenación por selección (Shell)

Y su respectiva implementación (fuera del propósito de éste documento).

En java escribiríamos lo siguiente:

java.util.Arrays.sort(num);
for (int i : num) {
System.out.println(i);
}

El método que realiza el ordenamiento no retorna ningún valor, pero el arreglo que se le pasa queda ordenado ya que lo que se envía es su referencia.

Es posible efectuar ordenamiento a los siguientes tipos de arreglos:


Object[]
byte
char
double
float
int
long
short
Ordenamientos a Plantillas o Templates
Ordenamientos a los tipos de datos enunciados más arriba, pero en dentro de un rango inicial y final.

Otro ejemplo de ordenamiento natural para un tipo de datos String (String no es un tipo de datos primitivo)

String [] cadenas = {"Zorro","Perro","Gato","Oveja","Lobo"};
java.util.Arrays.sort(cadenas);
for (String string : cadenas) {
System.out.println(string);
}
La salida de este programa será la siguiente:

Gato
Lobo
Oveja
Perro
Zorro

Lo que supone un ordenamiento alfabético de las entradas.


Ordenamiento automático de datos complejos:

El ordenamiento de datos complejos tiene ciertos aditamentos adicionales que se deben tener presentes para que sea posible llevarlo a efecto. Primero vamos a definir la estructura compleja que vamos a ordenar:

package Ordenamiento;

/**
* @author William Enrique Parra Alba
*/
public class Estudiante implements Comparable {

private String Nombres;
private int grado;
private int curso;

public Estudiante() {
}

public Estudiante(String Nombres, int grado, int curso) {
this.Nombres = Nombres;
this.grado = grado;
this.curso = curso;
}

public String getNombres() {
return Nombres;
}
public void setNombres(String Nombres) {
this.Nombres = Nombres;
}
public int getGrado() {
return grado;
}
public void setGrado(int grado) {
this.grado = grado;
}
public int getCurso() {
return curso;
}
public void setCurso(int curso) {
this.curso = curso;
}
public int compareTo(Object o) {
/* Hacemos un cast, para convertir el tipo Object recibido, en tipo Estudiante */
Estudiante otro = (Estudiante) o;
/* El criterio de más peso para determinar si un objeto es mayor que otro es el grado en el que se encuentra */
if (this.getGrado() > otro.getGrado()) {
return 1;
}

/* Si el grado del curso con el que es comparado es mayor, el objeto local es menor */
else if (this.getGrado() < otro.getGrado()) {
return -1;
}
/* Pero si son del mismo grado, se debe entrar a detectar los cursos*/
else if (this.getGrado() == otro.getGrado()) {
if (this.getCurso() > otro.getCurso()) {
return 1;
} else if (this.getCurso() < otro.getCurso()) {
return -1;
}
/* Si son tanto del mismo grado, como del mismo curso, tenemos que ordenarlos alfabéticamente */
else if (this.getCurso() == otro.getCurso()) {
return this.getNombres().compareTo(otro.getNombres());
}
}
/* En caso de que no sea posible determinar quien es mayor que quien, retornamos un valor cero que se corresponde
con igual*/
return 0;
}
}

Como podemos apreciar, este programa presenta la particularidad de que implementa la interface comparable y es sobre escrito el método compareTo en el que se incluye la lógica que determina cuando un objeto es mayor que otro. El valor de retorno se resuelve de la siguiente manera:

Si el objeto local (this) es mayor que el recibido como parámetro, se retorna 1
Si el objeto local (this) es menor que el recibido como parámetro, se retorna -1
Si los dos son iguales, se retorna 0.

Ahora que ya tenemos el método de comparación implementado, procedemos a probar el código. Para ello creamos los siguientes datos de prueba en el programa:


Grado Curso Estudiante
1 1 Ivan
1 1 Juan
1 2 Luis
1 3 Pedro
2 1 Gabriel
2 1 Hector
2 2 Dario
2 2 Estevan
2 3 Lucas
3 1 Pedro
3 2 Juvenal
3 3 Diomedez



Y ahora el código..



List <estudiante> listaEst = new ArrayList<estudiante>();
listaEst.add(new Estudiante("Pedro",1,3));
listaEst.add(new Estudiante("Hector",2,1));
listaEst.add(new Estudiante("Gabriel",2,1));
listaEst.add(new Estudiante("Pedro",3,1));
listaEst.add(new Estudiante("Diomedez",3,3));
listaEst.add(new Estudiante("Juvenal",3,2));
listaEst.add(new Estudiante("Estevan",2,2));
listaEst.add(new Estudiante("Ivan",1,1));
listaEst.add(new Estudiante("Luis",1,2));
listaEst.add(new Estudiante("Dario",2,2));
listaEst.add(new Estudiante("Lucas",2,3));
listaEst.add(new Estudiante("Juan",1,1));

Collections.sort(listaEst);
for (Estudiante estudiante : listaEst) {
System.out.println("Grado: --> "+estudiante.getGrado()+" Curso --> "+estudiante.getCurso()+" Nombres --> "+estudiante.getNombres());
}

Iniciamos definiendo un List que solo permitirá tipos de dato Estudiante. A partir de ésta referencia creamos un ArrayList que también recibirá solo tipos de dato estudiante.

Llenamos el arreglo con datos de estudiantes que ingresan desordenadamente, y tras usar la instrucción:

Collections.sort(listaEst);

Ya los datos estarán ordenados. Prueba de ello es la salida del programa que presento a continuación:


Grado: --> 1 Curso --> 1 Nombres --> Ivan
Grado: --> 1 Curso --> 1 Nombres --> Juan
Grado: --> 1 Curso --> 2 Nombres --> Luis
Grado: --> 1 Curso --> 3 Nombres --> Pedro
Grado: --> 2 Curso --> 1 Nombres --> Gabriel
Grado: --> 2 Curso --> 1 Nombres --> Hector
Grado: --> 2 Curso --> 2 Nombres --> Dario
Grado: --> 2 Curso --> 2 Nombres --> Estevan
Grado: --> 2 Curso --> 3 Nombres --> Lucas
Grado: --> 3 Curso --> 1 Nombres --> Pedro
Grado: --> 3 Curso --> 2 Nombres --> Juvenal
Grado: --> 3 Curso --> 3 Nombres --> Diomedez


Espero que les haya servido de algo.

Sigo en construcción...

Próximas entregas:

Tipos enum
Corrimientos de Bits
Templates