Ésta pequeña guía1 está pensada no para ser un curso de Java ni nada por el estilo, sino que es para dar a conocer cómo usar lo básico de Java para poder resolver las tareas de Algoritmos mientras siga siendo el lenguaje oficial del curso. La segunda parte aún no está terminada, pero la puedes encontrar aquí.

<preámbulo> Algo de historia: antiguamente, el curso de Algoritmos también era un curso para profundizar Java (recordar que Computación I, el antiguo Intro a la Progra, se dictaba en Java), por lo que cosas más técnicas sobre el lenguaje era posible evaluarlas. Ahora para Java está el CC3002 de Bergel, y el mito dice que posiblemente Algoritmos migre a Python en el futuro, pero eso está por verse. </preámbulo>

0. Instalar Java

No es necesario que tengas ningún sistema operativo en particular instalado: la magia de Java es que los programas corren en una máquina virtual, de la misma manera siempre. Voy a suponer que no vas a usar ningún IDE, así que vas a necesitar una terminal (Símbolo del Sistema de Windows, ttyN o emulador de terminal de *nix, lo que sea que traiga Mac) en el que puedas correr los comandos java y javac.

Para ver si tienes java, haz:

    java -version

Adivina qué tienes que hacer para ver si tienes javac 🙄:

    javac -version

Si te sale algo la versión de Java, la palabra “runtime” o simplemente “javac 1.8…”, sáltate el resto de la sección. Si te sale algo como -bash: java: orden no encontrada, sigue leyendo.

Ok, seguiste leyendo, así que probablemente no tienes java/javac instalado. Voy a decirte como instalarlos en Linux. Si tienes Windows, probablemente en la primera auxiliar tu auxiliar dijo cómo instalarlo, o dejó un post en el foro. Si no fuiste a la primera auxiliar, pues no es culpa mía, yo no soy tu auxiliar.

Para instalar javac (viene con java incluido) en (K,L,X)Ubuntu, haz:

    sudo apt install default-jdk

(Si quieres el oficial de Oracle™, el repositorio es oracle-java8-installer (o 7, dependiendo de la versión), pero con la versión abierta estás bien).

Para instalarlo en Arch, el comando es

    sudo pacman -S jdk7-openjdk

Puedes escoger instalar la versión 7, 8, hasta la 10 cambiando el numerito: en el curso no va a importar. Puede que hayas escuchado que tal versión tiene algo genial… no te sirve de nada: vas a tener que implementar a mano hasta tus funciones2 para mover arreglos.

Ahora deberías tener instalado el JRE (Java Runtime Environment, más conocido como el programita java que te deja correr otros programitas), y el JDK (Java Development Kit, más conocido como el programita javac que compila -por eso la c- tus archivos .java en archivos que entiende la máquina de Java).

1. Hola mundo!

Vamos a escribir el programa de prueba más simple, para que te familiarices con las costumbres/mañas de Java. En un editor de texto, escribe el siguiente código (siempre es recomendable escribirlo a mano en vez de copypastear, para recordar las cosas por memoria muscular):

    public class Hola {
        public static void main(String[] args) {
            System.out.println("Hola mundo!");
        }
    }

En la carpeta en que quieras, es importante que lo guardes con el nombre Hola.java. Esto es porque la clase tiene que tener el mismo nombre del archivo (más detalle adelante). Ahora, para correrlo, puedes hacer:

    javac Hola.java
    java Hola

El primer comando generó un archivo llamado Hola.class, el cual luego es el que corre el comando java. Es un error muy común al arreglar un problema en el código no compilar de nuevo antes de correrlo. Puedes perder horas intentando arreglar errores que no existen. Siempre compila después de cualquier cambio.

2. public static qué? Me lo tengo que aprender?

Respuesta simple: No necesariamente.

Si vas a escribir Java desde un editor de textos simple, siempre puedes copiar el código de tu tarea anterior y modificar lo que necesites. De todas formas, si te hacen programar en Java en el control, no te van a descontar puntos por no poner los public: Algoritmos es un curso de algoritmos, no de Java.

Cuando tienes un archivo NombrePrograma.java, lo compilas y luego lo corres, la máquina de Java lo primero que hace es buscar dentro de tu archivo una clase llamada NombrePrograma (no tiene por qué ser public). Luego, dentro de esa clase, busca una función2 que empiece exactamente con

    public static void main(String[] args) {
        ...
    }

Intenta cambiarle el nombre a la función, o quitarle el public, o ponerle un argumento extra. Lo único que te deja hacer es cambiar el nombre de la variable args por otro.

Ahora bien, puedes tener más de una función, y en distintas clases… pero siempre va a ser uno el programa principal, ¿no?. Lo otro: si el archivo se llama NombrePrograma.java, sólo la clase NombrePrograma puede ser public, si creas una clase Otra Clase que también sea public, Java te va a retar por no dejar esta otra clase en un archivo llamado OtraClase.java.

En general es buena práctica en Java dejar todas las clases en archivos distintos. En general es buena práctica en las tareas de Algoritmos dejar todas las clases en el mismo archivo. Trust me, he corregido tareas de Algoritmos.

2.1. Entonces… qué significa el public?

Para eso está tooooodo el curso CC3002 Metodologías de Diseño y Programación

2.2. y el static?

Ah, sí, eso es importante3 (al menos útil) aquí. Más adelante lo veremos.

3. Ejemplos de Java

Como regla general, si vas a implementar Algoritmos, basta escribir todo dentro de la misma clase. Si vas a implementar Estructuras de Datos, posiblemente te ayude tener clases separadas.

Una de las cosas más simples y que probablemente más tengas que hacer en Algoritmos es usar, crear y recorrer arreglos, generalmente de números enteros. Si ya sabes C, C++ o algún otro hijo de C, probablemente ya tienes una idea de cómo se crean y utilizan variables, pero Java tiene más burocracia.

3.1. Primer intento

Si creamos un archivo con el contenido:

    class Main {
        public static void main(String[] args) {
            System.out.println("Voy a crear un número");
            int num = 7;
            System.out.println("Número: "+num);

            System.out.println();

            String s = "Hola, voy a crear un arreglo";
            System.out.println("Largo string: "+s.length());
            System.out.println("Contenido string: '"+s+"'");

            System.out.println();

            int arr[] = new int[] {4, 6, 2, num, 281, 42};
            System.out.println("Largo arreglo: "+arr.length);
            System.out.println("Contenido arreglo: '"+arr+"'");
        }
    }

Obviamente el archivo anterior se tiene que llamar Main.java. Probablemente ya te diste cuenta que el System.out.println es una especie de print; bueno, es más que eso: Java tiene más de un print:

  • System.out.print imprime lo que le pases en pantalla.
  • System.out.println hace lo mismo que el System.out.print, pero agrega un salto de línea al final. System.out.println(string) es equivalente a System.out.print(string+"\n").
  • System.out.printf toma como argumento strings con formatos. Probablemente te sea confuso a menos que hayas usado printf en C. Un ejemplo: System.out.print("Tengo %d años y me llamo %s. Este es un número con 5 caracteres: '%05d'\n", 6, "Pablito", 42);. Más información en Wikipedia

Java es un lenguaje tipado, hay que decirle a las variables de qué tipo van a ser. Como buen hijo de C tiene los tipos de números enteros (int), números con decimales (float o double) y caracteres sólos (char). También tiene otros tipos para facilitarnos la vida, como valores booleanos (boolean) y Strings (String). Este último en realidad no es un tipo, sino que un objeto de Java, pero apenas nos vamos a dar cuenta de eso.

A diferencia de otros lenguajes, para imprimir un número en pantalla no tuvimos que convertirlo antes de int a String o lo que sea: bastó con hacer System.out.println("Número: "+num); y ese + que concatena un número a un String también se encarga de convertirlo a String. Si quisiéramos imprimir sólo un número, un truco sucio muy utilizado es System.out.println(""+42). Más adelante incluso llamamos a System.out.println sin argumentos, sólo para que imprima líneas vacías (hay mejores formas de hacerlo, pero es un ejemplo).

Después creamos un String y obtuvimos su largo con s.length(). Esta función length() es una función2 que tienen todos los String de Java. En el bloque siguiente, creamos un arreglo arr (se utiliza la keyword new para crear cosas) con toda la burocracia que tiene Java para crear objetos; incluso utilizamos el número num como uno de los elementos del arreglo. Para saber su largo, arr tiene la propiedad length (no es una función, así que no se llama “length()”).

3.2. Arreglando el intento

Si utilizaste el mismo código, vas a ver la línea que dice Contenido arreglo: ... imprime pura basura. Esto es porque los arreglos no tienen una representación directa a String. Esto se podría solucionar fácilmente con la librería java.util.Arrays, pero en el curso no te dejan utilizarla, así que vamos a crear una solución a mano: Hay que recorrer el arreglo, y cada elemento concatenarlo en un String. Cambiemos nuestra clase Main para que tenga una función que haga esto:

    class Main {
        static String stringArreglo(int[] arreglo) {
            String s = "[";
            int n = arreglo.length;

            s += arreglo[0];

            int i = 1;
            while (i < n) {
                s += ", "+arreglo[i];
                i++;
            }

            s += "]";

            return s;
        }

        public static void main(String[] args) {
            System.out.println("Voy a crear un número");
            int num = 7;
            System.out.println("Número: "+num);

            System.out.println();

            String s = "Hola, voy a crear un arreglo";
            System.out.println("Largo string: "+s.length());
            System.out.println("Contenido string: '"+s+"'");

            System.out.println();

            int arr[] = new int[] {4, 6, 2, num, 281, 42};
            System.out.println("Largo arreglo: "+arr.length);
            System.out.println("Contenido arreglo: '"+stringArreglo(arr)+"'");
        }
    }

Si lo corres ahora, va a funcionar por arte de magia. Hagámosle una disección a la magia paso por paso:

  • Primero creamos una función con el encabezado static String stringArreglo(int[] arreglo). ¿Por qué es static? Bueno, prueba sacándole el static. No va a funcionar, porque esta función la estamos llamando desde public static void main(), y los métodos estáticos sólo pueden llamar métodos estáticos. ¿Por qué es String? Pues porque retorna un String. En Java siempre hay que decir qué retornan tus funciones. Si no retornara nada (por ejemplo, una función que sólo imprime en pantalla), basta poner void. ¿Por qué stringArreglo? Porque me pareció que era un buen nombre para una función que toma un arreglo y devuelve una representación como String.
  • Dentro de la función, creamos un String s que sólo contiene un [. Esto porque se me ocurrió que una buena forma de mostrar arreglos es rodeando sus elementos de [paréntesis cuadrados].
  • Creamos la variable n que almacena el largo del arreglo. ¿Por qué? Bueno, perfectamente pudimos haber usado arreglo.length cada vez que necesite el largo, pero si hubiera necesitado ese número muchas veces, me da lata escribir arreglo punto length a cada rato, y tengo la costumbre de guardar ese número en una n.
  • Después guardamos en el String (la representación de) el primer elemento del arreglo. ¿Por qué? Porque luego vamos a agregar , <elemento> en cada iteración, y si guardáramos todos los elementos así, obtendríamos un string [, 1, 2, 3]. ¿Lo ves? La primera coma está de más.
  • Ahora vamos a guardar todos los elementos desde el índice 1 (que es el segundo, olvídate de Matlab aquí) iterando con un ciclo while. Básicamente dice guárdame el i-ésimo elemento de arreglo (s += ", "+arreglo[i];) desde el segundo (int i = 1;) mientras arreglo tenga i elementos. Es importante el i++ de abajo, o si no estarías guardando eternamente el segundo elemento del arreglo, y nunca se cumpliría que \(i \geq n\), corriendo el programa hasta el infinito (o al menos hasta el String s llene la memoria de tu computador).
  • Le agregamos el corchete de cierre y lo retornamos. ¡Listo! Tienes una función que te deja ver una representación de tus arreglos, perfectamente funcionando, ¿no?

Pues no. Agrega en la parte final del main la línea

    System.out.println(stringArreglo(new int[] {}));

compila y corre; la línea new int[] {} sólo crea un arreglo vacío (que no guarda en ninguna variable) y se lo pasa a la función.

3.3. Mi primera excepción

Felicidades, deberías tener tu primera Runtime Exception (excepción en tiempo de ejecución: “por más que haya compilado bien, Java no tenía la culpa de que esto haya fallado”). Veamos qué dice la excepción:

    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at Main.stringArreglo(Main.java:6)
        at Main.main(Main.java:36)

Leyendo desde abajo hacia arriba, la línea (36 en mi caso, depende de cuantos saltos de línea hayas puesto tú) del archivo Main.java corresponde a System.out.println(stringArreglo(new int[] {}));, que es justo la línea que agregamos recién. El siguiente llamado en el stack de la excepción corresponde a la línea 6, dentro de la función stringArreglo. La línea que falla ahí dice s += arreglo[0];.

¿Te diste cuenta del error? La misma máquina de Java nos está diciendo: ArrayIndexOutOfBoundsException: 0. Excepción de índice fuera de rango: 0. Estamos buscando el primer elemento ¡de un arreglo que no tiene elementos!. Esto se soluciona con un if:

    if (n > 0) {
        s += arreglo[0];
    }

Listo, ahora la excepción no debería ocurrir, porque le estamos preguntando primero al arreglo si es que tiene algún elemento. Compila y corre, vas a ver que la pantalla muestra bien el [], que es lo esperado.

3.4. Función arreglada

Una forma “más bonita” de recorrer el arreglo es cambiando el while por un for, con lo que la función stringArreglo queda:

    static String stringArreglo(int[] arreglo) {
        String s = "[";
        int n = arreglo.length;

        if (n > 0) {
            s += arreglo[0];
        }

        for (int i=1; i < n; i++) {
            s += ", "+arreglo[i];
        }

        s += "]";

        return s;
    }

Ah, se me olvidaba: el var += algo es equivalente a var = var + algo, y var++ es el var = var + 1.

<dato curioso> Los while son equivalentes a los for: el primer elemento del for corresponde a una variable declarada antes del while, el segundo elemento del for equivale a la condición del while, y el último elemento del for equivale a la última línea del while. La única diferencia sería que la variable int i deja de estar declarada fuera del for y no así en el while, pero eso se soluciona encerrando el int i=1; while(...) {... i++;} entre otro par de { y }. </dato curioso>

Listo, eso es todo en la primera parte de esta miniguía. En la segunda parte (update: aquí) planeo colocar cosas sobre códigos con más de una clase, y métodos no estáticos.

Notas al pie

  1. No tengo la más remota idea de cuál es el diminutivo de guía

  2. Que Bergel me pille confesado por decirles “funciones” a los métodos…  2 3

  3. Cuando fui auxiliar del ramo, más de una vez vi a alumnos confundidos por esto.