Examen de Programación Convocatoria 22 de Junio 2005 Licenciatura de Lingüística y Nuevas Tecnologías Pregunta 1 [1 punto] Tenemos el siguiente programa: public class P1 { public static void main(String args[ ]) { P1 un = new P1(); P1 dos = new P1(); P1 tres = null; System.out.println("un == dos: " + (un == dos)); // 1 System.out.println("dos == tres: " + (dos == tres)); // 2 P1 cuatro = dos; System.out.println("dos == cuatro: " + (dos == cuatro)); // 3 dos = null; System.out.println("dos == cuatro: " + (dos == cuatro)); // 4 } } ¿Qué imprime cada uno de los cuatro System.out.println numerados 1, 2, 3 y 4 y por qué? [1 punto] 1. Imprime “false” porque las variables de objeto “un” y “dos” refieren a 2 objetos distintos, cada cual construido con el constructor por defecto de P1 en las líneas anteriores. 2. Imprime “false” porque “dos” refiere a un objeto concreto, mientras “tres” tiene la referencia nula (es decir, no refiere a ningún objeto concreto). 3. Imprime “true”, porque al escribir en la línea anterior “P1 cuatro = dos”, asignamos a “cuatro” la misma referencia que “dos”, es decir que las 2 variables de objeto apuntan al mismo objeto. 4. Imprime “false” porque al escribir en la línea anterior “dos = null”, cambiamos la referencia de “dos” a null pero no la de “cuatro” que sigue siendo la misma (apunta a un objeto concreto). 1 Pregunta 2 [2 puntos] Tenemos en la misma carpeta las clases P2A (en el archivo P2A.java) y P2B (en el archivo P2B.java). P2A.java public class P2A { public static int c = 3; public int i = 2; private int j = 4; } P2B.java public class P2B { public static void main(String[] args) { int i = -1; P2A un = new P2A(); } } a) Explicar el significado de cada una de estas 5 denominaciones, ilustrandolas con las variables dentro de P2A y P2B: [1.25 punto] 1. Variable de objeto: Una variable de objeto contiene una referencia a un objeto. Por ejemplo “un” dentro de P2B es una variable de objeto, contiene referencias a objetos de tipo P2A. 2. Variable de tipo básico: Una variable de tipo básico contiene el valor de un tipo básico como int, float, char, double or bolean. Por ejemplo, las variables i, j y c dentro de P2A son de tipo básico, también lo es i dentro de P2B. 3. Variable local: Una variable local es una variable cuyo ámbito es un bloque de instrucciones: se puede acceder dentro de este bloque pero no fuera del bloque. Un bloque puede ser el “cuerpo” de un bucle o un condicional o de un método. Por ejemplo, en P2B, “i” y “un” son variables locales al método main. 4.Variable de clase: Una variable de clase, a diferencia de una variable de instancia, es una variable común a toda la clase. No es especifica a cada objeto creado. Una variable de clase está definida con la palabra clave “static”, como por ejemplo la variable “c” dentro de P2A. 5. Variable de instancia: Una variable de instancia describe una propiedad de un objeto, y como tal, es especifica a cada objeto creado. Por ejemplo, las variables “i” e “j” dentro de P2A son variables de instancia. b) Mostrar cual es la manera correcta y más típica de acceder dentro del main de P2B a cada una de las variables definidas dentro de P2A.java (si es que se pueden acceder...). [0.5 puntos] 2 un.i = 3; P2A.c = 4; // la variable j no se puede acceder desde fuera de su clase: esta // declarada como “private”. Habría que escribir un método “getj” para // acceder a esta variable c) Supongamos que llamamos dentro del main de P2B los siguientes métodos: [0.25 puntos] int num = Integer.parseInt(args[0]); String cadena = "Hola" ; int tamanyo = cadena.length(); Explicar por qué los métodos parseInt y length se llaman de forma diferente. parseInt es un método de clase definido con la palabra clave “static”, no pertenece a ningún objeto en concreto sino que pertenece a la clase, por lo que para llamarlo, no hace falta crear un objeto: ponemos el nombre de la clase para que Java pueda saber donde está ubicado dentro del paquete. Por otro lado, el método length es un método de instancia que define el comportamiento de un objeto especifico, en este caso de un objeto de tipo String especifico. Pregunta 3 [1 punto] Tenemos el siguiente programa: import java.util.*; public class P3 { public static void main(String[ ] args) { String cadena = "Hola! Me llamo Harry. Como estas?"; StringTokenizer st = new StringTokenizer(cadena,"!.?"); while (st.hasMoreTokens()) { String token = st.nextToken(); System.out.println(token); } } } a) ¿Qué imprime el programa a la ejecución? (A notar que el segundo argumento de StringTokenizer es “!.?”, es decir punto de exclamación, punto, punto de interrogación) [0.5 puntos] Se imprime lo siguiente (a notar que se conserva los espacios entre las palabras): Hola Me llamo Harry Como estas 3 b) ¿Por qué hacemos import java.util.* al principio de la clase? [0.5 puntos] Porque la clase StringTokenizer esta definida dentro del paquete java.util Pregunta 4 [1.5 punto] a) ¿Qué pasa al ejecutar el siguiente código? [0.75 puntos] public class P4a { public static void doIt(int[] z) { int temp = z[z.length-1] ; z[ z.length-1 ] = z[0] ; z[0] = temp; } public static void main ( String[] args ) { int[] myArray = {0, 1, 2, 3, 4} ; doIt( myArray ); for (int j=0; j<myArray.length; j++ ) System.out.print( myArray[j] + " " ) ; } } Se imprime: 4 1 2 3 0 4 b) ¿Qué pasa al ejecutar el siguiente código? [0.75 puntos] public class P4b { static void doIt( int[ ] z ) { int[ ] a = z ; a[0] = 99; } public static void main ( String[ ] args ) { int[ ] myArray = {0, 1, 2, 3, 4} ; doIt( myArray ); for (int j=0; j<myArray.length; j++ ) System.out.print( myArray[j] + " " ) ; } } Se imprime: 99 2 3 4 5 Pregunta 5 [2 puntos] Aquí viene un extracto de la API de la clase ArrayList: boolean add(Object o) Appends the specified element to the end of this list. Object get(int index) Returns the element at the specified position in this list. int size() Returns the number of elements in this list. int indexOf(Object elem) Searches for the first occurence of the given argument, testing for equality using the equals (==) method. Returns the index of the first occurrence of the argument in this list; returns -1 if the object is not found. 5 a) ¿Qué imprime el siguiente programa a la ejecución (líneas numeradas 1 a 4)? [0.5 puntos] import java.util.*; class P5a { int val; public P5a(int x) { this.val = x; } public static void main(String []args) { ArrayList list = new ArrayList(); System.out.println(list.size()); // 1 list.add(new P5a(1)); System.out.println(list.size()); // 2 list.add(new P5a(1)); System.out.println(list.size()); // 3 System.out.println(list.get(0) == list.get(1)); // 4 } } 0 1 2 false b) Utilizando algunos de los métodos de ArrayList descritos más arriba, escribir los dos siguientes métodos: [1 punto] public static boolean isEmpty(ArrayList a): returns true if arraylist a is empty, false otherwise public static boolean isEmpty(ArrayList a) { if (a.size() > 0) return false; else return true; } public static boolean containsObject(ArrayList a,Object o): returns true if arraylist a contains object o, false otherwise public static boolean containsObject(ArrayList a,Object o) { if (a.indexOf(o) > 0) return true; else return false; } 6 c) Tenemos la siguiente clase Alumno: public class Alumno { String nif; String nombre; String apellido; } Si tenemos un ArrayList de Alumno y un HashMap de Alumno, explicar de manera informal la diferencia entre como buscar un alumno especifico dentro del ArrayList y como buscarlo dentro del HashMap. ¿Cual es la manera más sencilla? [0.5 puntos] Si buscamos un alumno, lo más fácil es buscarlo por su NIF. En el caso del ArrayList, el algoritmo consiste en hacer una busque lineal por todo el ArrayList desde el primer índice hasta encontrar el elemento o hasta llegar al último índice. En el caso del HashMap, la búsqueda es más sencilla, pues podemos acceder (o no) directamente al elemento buscado por su clave que sería el NIF. Evidentemente, si no tenemos el NIF del alumno (la clave) perdemos este ventaja. Pregunta 6 [1 punto] Tenemos el siguiente código: import java.io.*; public class P6 { public static void main(String[ ] args) throws IOException { FileReader f = new FileReader(args[0]); BufferedReader b = new BufferedReader(f); String s1 = b.readLine(); String s2 = b.readLine(); FileWriter w = new FileWriter(args[1]); while ((s1 != null) && (s2 != null)) { w.write(s1 + " " + s2 + "\n"); s1 = b.readLine(); s2 = b.readLine(); } w.close(); b.close(); f.close(); } } 7 a) Si args[0] corresponde a un fichero que contiene lo siguiente (un carácter por línea): [0.5 puntos] a b c d e ¿Cuál será el contenido del fichero que corresponde a args[1] después de la ejecución? ab cd b) Explicar qué son f y b, y qué es w, y para qué sirven. [0.25 puntos] Son flujos. f y b son flujos de entrada que establecen una conexión entre la fuente datos (el fichero) y el programa. w es un flujo de salida que establece una conexión entre el programa y el destino de datos (el fichero). c) ¿Por qué necesitamos b además de f para leer un fichero? [0.25 puntos] Con un flujo de tipo FileReader, solo podemos leer caracteres ascii con el método read. Un flujo de tipo BufferedReader actua como tapón entre el flujo FileReader y el programa, almacenando los caracteres y liberandolos cuando llegan a formar una línea entera. Pregunta 7 [1.5 puntos] a) Tenemos el siguiente código: [0.5 puntos] public class P7a { public static void main(String[ ] args) { int i = 3; System.out.println(i); String s = "Hola"; System.out.println(s); } } Partes de este código ilustran el concepto de overloading. Explicar qué es el overloading y por qué nos resulta útil. El método println existe bajo diferentes firmas (signatura) dentro de la misma clase asociada con la variable System.out. Eso se llama method overloading. En el ejemplo a la ejecución se llama en el primer caso a void println(int i), siendo el argumento de entrada un int. En el segundo caso se llama a void println(String s), siendo el argumento de entrada un String. El overloading es útil porque permite utilizar en apariencia el mismo método dentro del programa, pero son distintos métodos que se llaman a la ejecución. 8 b) El método String toString() esta definido dentro de la clase Object y también dentro de su sub-clase indirecta Integer. Si escribimos: [0.5 puntos] Object o1 = new Object(); String s1 = o1.toString(); // 1 Object o2 = new Integer(3); String s2 = o2.toString(); // 2 ¿Qué toString() va a ser llamado a la ejecución a cada una de las líneas 1 y 2 y por qué? En 1 se llama al toString de Object, pues o1 es una variable de objeto que refiere a un objeto de tipo Object. En 2 se llama al toString de Integer, pues o2 es una variable de objeto que refiere a un objeto de tipo Integer. La clase Integer hereda de Object pero redefine (“overloading”) el método toString, por lo que se llama a su propio método cuando se trata de un objeto de tipo Integer. c) Tenemos al principio de la clase A la siguiente declaración: [0.25 puntos] public class A extends B { ... } ¿Qué quiere decir la palabra clave extends? Explicar el concepto a que se refiere. La palabra extends quiere decir que A hereda directamente de B, es decir que A hereda todas las variables y todos los métodos de B (si no es que redefine estos métodos). d) Tenemos al principio de la clase A la siguiente declaración: [0.25 puntos] public abstract class A { ... } ¿Qué quiere decir la palabra clave abstract? Explicar el concepto a que se refiere. La palabra abstract quiere decir que A no se puede instanciar: no se pueden crear objetos de tipo A. 9