Práctica 1 (TSIOCA 2007-2008)

Fecha
2008-02-22
Conceptos
Repaso
Soluciones
soluciones_p01.tgz
Profesores
Alberto Cortés y Pablo Basanta (Mañana). José María Rubio y Jorge Ruiz (tarde).
Última modificiación
alcortes, 2008-03-10

Introducción

Comentarios sobre las prácticas

Las prácticas no están pensadas para que las empieces y las termines durante la sesión de prácticas. La idea es que llegues a las prácticas habiéndotelas mirado ya en casa.

Idealmente tendrías que haberlas intentado enteras en casa, y utilizar las horas de prácticas para preguntar las dudas que te hayan surgido.

Hayas o no intentado las prácticas en casa, durante la clase no te dará tiempo a terminarlas, como mucho podrás hacer un 20% o un 30%. Es importante que termines todas las prácticas al completo.

Las prácticas de una semana están puestas suponiendo que has terminado las de la semana anterior. Si te dejas alguna práctica a medias, seguro que será lo que te preguntemos en el examen.

Algunos comentarios sobre esta primera práctica

En esta práctica se tratarán conceptos básicos de programación en general; intentaremos dejar para otro día los conceptos relacionados con la programación orientada a objetos.

Dado que Java es un lenguaje de programación orientado a objetos, será necesario un mínimo de programación orientada a objetos. Para que esto no afecte al objetivo principal de la práctica, se hará a continuación una breve descripción de lo imprescindible...

Dogmas de fe:

Ejercicio 0

Responde por escrito a las siguientes preguntas:

  1. ¿Qué editor de textos vas a usar para crear los fuentes de sus programas?, ¿sabes usarlo?

  2. ¿Cuál es el "path" del compilador java que vas a usar en las prácticas?

  3. ¿Qué opción del compilador te parece más inútil?

  4. ¿Qué versión de la jre se usará en las prácticas?

  5. ¿Cuál es el "path" de la máquina virtual java?

  6. ¿Cuál es la URL oficial para la API de dicha jre?

Ejercicio 1

Los objetos de la clase java.io.PrintStream representan un canal de comunicación por el que tu programa manda bytes, por eso se les suele llamar "flujos de bytes". Se supone que al otro lado del canal habrá otro programa interpretando los bytes que tu programa manda.

La clase PrintStream tiene un método write(int) que permite mandar un byte al canal. También tiene un método print(Object) que permite mandar por el canal una representación en forma de bytes de cualquier objeto.

Cuando la máquina virtual Java empieza a ejecutar el método main(String[]) de un programa, automáticamente crea dos objetos PrintStream conectados, por defecto, a la pantalla del ordenador:

System.out
La salida estándar, por donde se escribe el resultado de la ejecución del comando. Por omisión, suele estar redirigida al terminal y por tanto se mostrará por pantalla.
System.err
La salida de error estándar, por donde se escriben los errores de ejecución del programa. Por omisión suele estar redirigida al terminal por lo que también se muestra por pantalla.

Así pues, el siguiente programa manda el objeto Hello World! (de la clase String) por la salida estándar y el objeto This is an error message! (también de la clase String) por la salida de error estándar:


public class Demo {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.err.println("This is an error message!");
        System.exit(0);
    }
}

  1. Puedes redirigir la salida de error y la salida de error estándar a ficheros para diferenciar ambos flujos. Para ello, la mayoría de terminales utilizan esta sintaxis:

    ; java Demo 1>out.txt 2>error.txt
    
    ;
    

    Comprueba que el contenido de los ficheros out.txt y error.txt es el que tu te esperas.

  2. El método System.exit(int) termina la ejecución del programa e informa al sistema operativo de si la ejecución del mismo ha sido exitosa (argumento igual a 0) o fallida (argumento distinto de 0). En caso de retornar del método main sin usar System.exit(int) se supone que la ejecución del programa ha sido exitosa y se comunica al sistema operativo convenientemente.

    Cada sistema operativo y terminal de usuario tiene maneras diferentes de informar al usuario si la ejecución de un programa fue exitosa o no. En algunas versiones de Windows puedes preguntar al sistema accediendo a la variable de entorno %errorlevel%. En la bash de Unix puedes utilizar la variable $?. Ejemplo en Windows XP:

    ; java Demo
    Hello World!
    This is an error message!
    
    ; echo %errorlevel%
    0
    
    ;
    

    Prueba a cambiar el valor del parámetro de System.exit(int) en el programa Demo y comprobar que el sistema operativo se entera del cambio.

  3. Crea un fichero fuente HelloWorld.java que contenga exactamente el siguiente contenido:

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello World!");
        }
    }
    
  4. Compila el fichero que acabas de crear y guarda el bycode generado en un fichero HolaMundo.class en el directorio temporal de tu máquina.

  5. Ejecuta la máquina virtual Java sobre el bytecode generado.

  6. Escribe, en una hoja, un programa HelloWolrd200 que imprima 200 veces seguidas la frase Hello World! acabada con un fin de línea.

  7. Comprueba que el programa que has escrito funciona correctamente.

Ejercicio 2

Cuando se ejecuta un programa Java se le pueden pasar argumentos de entrada. Este es el método más sencillo de comunicación de un usuario con el programa, aunque no es muy flexible ya que no permite un diálogo interactivo entre el usuario y el programa.

Dado el siguiente programa...

public class EchoArgs {
    public static void main(String[] args) {
        for (int i=0; i<args.length; i++)
            System.out.println(i + ": " + args[i]);
        return;
    }
}
  1. Sin ejecutarlo, escribe en una hoja el resultado del comando:

    ; java EchoArgs uno dos 3 17 "doce 34"
    
  2. Comprueba el apartado anterior ejecutando el comando propuesto

  3. Crea un programa EchoArgsReverse que mande a salida estándar sus argumentos en orden inverso, la numeración empezará en 0 (el último argumento) e irá creciendo hasta el primer argumento.

  4. Crea un programa Echo que mande a la salida estándar sus argumentos de entrada separados por un espacio. ¡Cuidado con no imprimir un espacio extra al final!. Compruébalo con:

    ; java Echo uno dos 3 "4 cinco" " seis      " 7
    uno dos 3 4 cinco  seis       7
    ; java Echo
    ; 
    

Ejercicio 3

Ya hemos visto como un programa puede mandar datos al usuario mediante la salida estándar. También hemos visto como un usuario puede mandar datos a un programa, de manera no interactiva, mediante el uso de los argumentos del programa.

Si queremos una comunicación interactiva entre el usuario y el programa necesitaremos algún canal de comunicación adicional. Los objetos de la clase InputStream permiten la lectura de datos desde un flujo de bytes. El método read() bloquea la ejecución del programa hasta que hay disponible un byte para leer del canal, cuando esto sucede, extrae el byte del flujo y retorna su valor.

Cuando la máquina virtual Java arranca un programa, crea automáticamente un objeto de la clase InputStream conectado, por defecto, al teclado del usuario. Dicho canal de comunicación será accesible desde el objeto System.in y se le conoce como la entrada estándar.

La entrada estándar la genera el sistema operativo según el siguiente algoritmo:

  1. Cada vez que el usuario presiona una tecla, el sistema operativo muestra el caracter correspondiente a esa tecla por la pantalla, de forma que el usuario pueda ver en pantalla la tecla presionada.

  2. El caracter pulsado es traducido a uno o varios bytes, según la codificación de caracteres que se esté utilizando.

  3. El conjunto de bytes resultantes se almacena en un buffer junto con el resto de los bytes resultantes de anteriores pulsaciones.

  4. En caso de que el caracter pulsado sea el correspondiente a la tecla <ENTER>, el sistema operativo extrae del buffer todos los bytes y los manda al flujo conectado a la entrada estándar.

Un programa que lea datos de System.in y responda a System.out permitirá mantener un diálogo interactivo con el usuario a través del teclado y la pantalla.

Así pues, este sencillo programa escribe por salida estándar lo que le mandes por la entrada estándar. Si no sabes aún lo que son las excepciónes, puedes pensar en el bloque try como algo que quieres intentar hacer y el bloque catch como las medidas a tomar si el intento a fallado debido a algún error interno del sistema que no tienen nada que ver contigo.

import java.io.IOException;

public class CatSimple {
    public static void main(String[] args) {
        int b = -1;
        for (;;) {
            try {
                b = System.in.read();
            } catch (IOException e) {
                System.err.println(e);
                return;
            }
            if (b == -1)
                break;
            System.out.write(b);
        }
        return;
    }
}
  1. Busca en el API el porqué de la comparación de b con -1.

  2. Compila y prueba el programa. Se puede indicar al sistema operativo que el flujo de la entrada estándar se ha terminado pulsando <ENTER> seguido de Control-D (en UNIX) o Control-C (en Windows).

  3. Crea un programa CountBytes que mande a su salida estándar el número de bytes de su entrada estándar. Por ejemplo, si mandamos a su entrada estándar "hola caracola", nos tiene que contestar por salida estándar con "15", que son el número de bytes en el mensaje anterior (El fin de línea en Windows se representa por dos bytes).

    ; java CountBytes
    hola caracola<Enter><Control-C>
    15
    ; java Echo hola caracola | java CountBytes
    15
    

    Los dos ejemplos anteriores dan la misma cuenta de bytes, pero en el segundo ejemplo el usuario ha escrito menos bytes, en concreto no ha escrito ningún fin de línea. Explique este comportamiento.

  4. Generalmente la salida estándar está conectada a la pantalla pero podemos redirigirla al fichero que se nos antoje mediante el operador > (sinónimo de 1>). Compruébalo mediante el siguiente comando:

    ; java CountBytes > file.txt
    

    ¿Cual es el contenido del fichero file.txt tras semejante invocación?

  5. También podemos conectar la salida estándar de un comando a la entrada estándar de otro, mediante el operador |. Compruébalo mediante el siguiente comando:

    ; java Echo uno       dos  3 | java CountBytes
    
  6. ¿Cual sería el resultado del siguiente comando?

    ; java Echo uno dos 3 | java CountBytes > file2.txt
    

Ejercicio 4

El sistema operativo representa los caracteres como grupos de bytes. Así, cuando tecleamos caracteres, el sistema operativo está pasándoles bytes a las aplicaciones.

Para muchas aplicaciones será más interesante interpretar esos flujos de bytes como flujos de caracteres, en especial en aquellas aplicaciones pensadas para procesar datos textuales.

La clase java.io.InputStreamReader, es una representación a nivel de caracteres de un flujo de bytes; es, a todos los efectos un flujo de caracteres del que se pueden ir leyendo (con el método read()) uno a uno.

Normalmente no estamos interesados en leer caracteres de uno en uno, sino que queremos leer líneas completas. La clase BufferedReader toma un flujo de caracteres y lo interpreta a nivel de flujo de líneas (entre otras cosas). Tiene un método readLine() que nos devuelve una String con la línea leída.

Por ejemplo, a continuación se muestra una versión alternativa del programa CatSimple que anteriormente a creado:

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class CatSimple2 {
  public static void main(String[] args)
  throws IOException {
    BufferedReader in =
        new BufferedReader(
          new InputStreamReader(System.in));
    String s;
    while((s = in.readLine()).length() != 0)
      System.out.println(s);
  }
}
  1. Crea un programa CountChars que cuente caracteres. A partir de ahora ya no te pediremos que pruebes tus programas, ahora eres tu el que tiene que preocuparse de probarlos y encontrar maneras adecuadas de hacerlo.

  2. ¿Cuándo será la salida de CountChars diferente de la salida de CountBytes?

  3. Crea un programa CountLines que cuente las líneas de la entrada estándar.

  4. Comprueba que el programa HelloWorld200 que escribiste antes manda de verdad 200 líneas.

Ejercicio 5

Muchas veces no queremos trabajar con la entrada estándar, sino con el contenido de un fichero. La clase java.io.FileReader nos permite acceder al contenido de un fichero como si fuera un flujo de caracteres.

  1. Crea un comando Cat. Éste comando copiara su entrada estándar a su salida estándar. Si dicho comando se invoca con un argumento, se interpretara que el argumento es el nombre de un fichero cuyo contenido sustituye al de la entrada estándar.

  2. Pruébalo con:

    ; java Echo uno dos tres | java Cat
    

    Y con:

    ; java Echo uno dos tres | java Cat Cat.java
    
  3. Comenta las diferencias entre:

    ; java Echo Cat.java
    

    y

    ; java Cat Cat.java
    

Ejercicio 6

  1. Crea un programa Head que imprima las 10 primeras líneas de su entrada estándar. Si el comando tiene un argumento, será el número de líneas a imprimir en vez de 10. Si el comando tiene 2 argumentos, el primero será el número de líneas a imprimir y el segundo el nombre del fichero cuyo contenido sustituirá a la entrada estándar.

  2. Crea un programa Tail similar a Head pero que imprima las n últimas líneas.

  3. Utilizando la redirección de entrada/salida, invoca un comando que cuente el número de caracteres de un fichero entre sus líneas 13 y 17. Considera la posibilidad de usar algunos de los programas que a creado hasta ahora: Cat, Echo, Head, Tail, CountChars...

Ejercicio 7

  1. ¿Puedes ejecutar programas Java si no tienes acceso a su código fuente?

  2. Es probable que uses a diario muchos programas que hacen otras personas: el Internet Explorer, el Messenger, el interfaz Web del correo de GMail, el eMule, el Notepad, algún juego... Hoy sin ir más lejos has usado algunas clases Java que nos has hecho tú mismo.

    Averigua quien ha hecho alguno de los programas que usas a diario.

  3. Averigua en que lenguaje de programación están programados.

  4. Bájate las fuentes y échales un vistazo.

  5. Habrá programas de los que no has encontrado el código fuente. ¿Por qué crees que es eso?, ¿Has conseguido averiguar el autor/autores de alguno de ellos?

  6. ¿Qué es el movimiento del código libre?, ¿Para que cosas crees que es mejor que el código propietario y para qué peor?

  7. Supón que eres un gobierno o empresa de cierta envergadura, y que tienes secretos importantes en tus ordenadores. ¿Usarías sistemas operativos o programas de los que no puedes ver el código y de los que no conoces a los autores?

  8. Supón que necesitas llevar a cabo una tarea informática de mucha importancia, que ha de ser llevada a cabo con presión y sin fallos, como el control del sistema vital de un enfermo, el trafico aéreo de un aeropuerto, la privacidad de tus comunicaciones personales... ¿Usarías programas de los que no puedes ver el código?

  9. ¿Crees que leer código de otros puede ser una buena manera de aprender a programar?

Valid HTML 4.01 Strict