por
David Muñoz Díaz
Este documento está escrito para ayudar a quien lo necesite. Puedes hacer con él lo que quieras, pero siempre de forma gratuita y desinteresada. Además, es demasiado malo como para venderlo. Se reirían de ti :) Si quieres aportar algo a este documento, no dudes en ponerte en contacto con el Grupo de Usuarios de Linux de la Universidad Carlos III de Madrid (
[email protected]).
Contenido Aquí tienes la lista de contenidos del texto. Los apartados marcados con * son sólo orientativos y no están ampliamente explicados aquí, ya que no forman parte del objetivo de este texto. Puedes encontrar una explicación exhaustiva de estos apartados en cualquier libro de Java, además de las explicaciones que te dará el profesor de la asignatura. Bueno, en algunas ocasiones, que debería dar. Prefacio* (que no, que es coña) Introducción. Cultureta. pelín de historia Platón y la OOP el problema... ... y la solución concepto de objeto propiedades métodos concepto de clase Java ¿por qué Java? caracteríaticas generales de Java * facilidad (je je jeeee) lenguaje interpretado
tamaño de los programas orientado a Internet JVM Cómo usar el compilador y la JVM (javac, java) El movimiento se demuestra andando definción de una mesa propiedades tipos básicos de datos * métodos valor de retorno parámetros creación y uso de objetos creación primero: las referencias segundo: new uso el operador punto más sobre creación: constructores ¿qué son? peculiaridades definición y uso breve iniciación al polimorfismo * métodos get/set ¿Por qué? uso
Variables de clase. Modificador Static el problema... ... y la solución uso de static en propiedades en métodos constantes
El método main, arrays y ¡a lanzar programas! por qué el método main definición del método main argumentos un inciso: arrays sacabó el inciso nuestro primer programa
Programando a lo bestia: estructuras de control Sentencias condicionales sentencia if expresión lógica bloque de sentencias comparadores de magnitud AND, OR, NOT comparadores de cadenas ifs anidados if sin elses: CUIDADO! solución rara solución más rara sentencia switch uso break comerte el coco 4 Sentencias repetitivas definición sentencia while regla general para el uso de bucles variación do - while sentencia for ¿While o for?
Recursividad definición metodología: la cola del cine partes de un método recursivo los factoriales
Herencia para qué sirve cómo se usa: extends redefinición de métodos uso de super()
una nota bastante importante
Casting preludio al casting: las referencias la señora referencia cómo funciona comparación de referencias Arreglando bicicletas: parámetros que son referencias casting ¿Por qué? cómo se usa resumen comerte el coco 6 upcasting, downcasting, Y siguiendo con el inglés, explicit and implicit cast upcasting downcasting casting implícito casting explícito
La Clase Object
definición contenido *
¿para qué ese contenido? la gran utilidad de la clase Object: referencias de tipo Object Interfaces definición uso básico de interfaces uso avanzado de interfaces: referencias de tipo de interfaces ¿herencia?¿Interfaces?¿comorl? Epílogo
El contenido de este texto es el estrictamente necesario para poder abordar cualquier problema de Programación. Este texto es, por tanto, la base necesaria para poder enfrentarnos a las asignaturas de Programación y Laboratorio de Programación. En este texto no vamos a ver determinados temas, como Applets, paquetes, pruebas de programas, etc. Para cualquier consulta sobre dichos temas, te recomiendo los libros de Java de la editorial Sun o alguno de la infinidad de manuales gratuitos que hay por la red. Por tanto, es necesario que te conciencies de que leer este texto no implica aprobar: implica ser capaz de aprobar. Si te sirve de algo, cuando yo llegué a primero, sabía bastante menos de lo que hay aquí escrito.
Prefacio No te engañes: tenemos mucha labor por delante. Pero no hay que pensar que es ardua y temible. En absoluto. O al menos, espero que no lo sea para ti. Programar implica siempre dos cosas: Primero, hay que comerse el coco bastante, pero no para alcanzar complejos conceptos (bueno, para esto también), sino para todo lo contrario: para conseguir poner nuestro pensamiento a la altura de un cacho de silicio, que no parece muy listo.... Segundo, la curiosidad no mata al gato, sino que curiosity
Skilled the cat
(por cierto, n.p.i. de de quién es esta frase. Se la he copiado a un miembro del GUL. Así que si esta frase es suya, le pido diculpas por no pagar derechos de autor) O sea, que siempre, repito, siempre es bueno parar de estudiar y encender el ordenador para hacer alguna prueba de lo que estemos estudiando. Si vemos casting será muy positivo que mandes a tomar por saco los apuntes y escribas un par de clases para probar “el acceso a los miembros de una calse vía una referencia de tipo la superclase”. Parece mentira, pero esta frase tiene sentido. En serio, nunca vas a aprender a programar con un libro, siempre lo aprenderás por tu cuenta. Así que vete haciendo a la idea de que vas a pasar muncho rato delante de la pantalla, aunque espero que no sea demasiado tiempo. O que al menos no sea tedioso. He intentado explicar todo este rollo usando ejemplos de la vida real. Tal vez quede “un poco” asquerosamente pedante explicar las clases metiendo a Platón por medio, pero creo que haciéndolo vamos a entender los conceptos más fácilmente, y sobre todo, no se van a olvidar. Por supuesto, no creas que todo es Platón, también hablaremos de tu vecina, de la mesa Margarita, de “páginas gué”, la bicicleta Anacleta, y de mil cosas más. Bueno, mil... lo que se dice mil.... Por supuesto, todo ello respaldado por el pez Otilio, el pato Onorato y el pájaro que no sé cómo demonios se llama, que no son más que excusas para apartar un poco la atención y hacer la lectura un pelín más agradable. Espero haber logrado, aunque sólo sea en pequeña parte, estos objetivos. Ahora, como dice la historia de la sopa de piedra, debes poner tú de tu parte. ¡Ánimo!
Introducción. Cultureta. Normalmente, para empezar se opta por definir qué es programar, cómo se lleva a cabo, conceptos como “software”, “hardware”, “memoria”, etc etc etc. Vamos a ignorar todo este rollo porque realmente no importa en absoluto. Nosotros, por ahora, no nos vamos a preocupar de la Yo soy un programador capacidad de la memoria o de la leche. de la velocidad del procesador. A nosotros nos interesa programar. Cuando aprendemos a programar siempre se nos plantea si debemos compaginar la programación con el lenguaje de programación. Parece una tontería, pero podemos aprender OOP sin saber ningún lenguaje. Es cierto: podemos saber sobre clases, objetos, herencia, casting, etc sin saber una maldita palabra de ningún lenguaje. Sin embargo, siempre es positivo compaginar nuestro aprendizaje de programación con el de un lenguaje propicio para ello. Además de la OOP, y a modo de curiosidad (cultureta), existen otros tipos de programación, cada uno con un lenguaje de programación típico. Esta parte, si quieres, te la puedes saltar. La programación “lineal” tiene como fundador el antiguo BASIC. A lo largo de los años, el BASIC ha ido cambiando hasta que hoy en día existen compiladores de BASIC orientado a objetos (Visual Basic, por ejemplo, mantiene una filosofía de OOP). La programación lineal se daba con los primeros intérpretes de BASIC. Aquí hay un ejemplo de este lenguaje: 10 20 30 40
INPUT “¿Cómo te llamas?”, NOMBRE$ SALUDO$ = “¡Hola “+NOMBRE$+” ¡” PRINT SALUDO$ END
¿Ves? El programa empieza en un punto, y acaba en otro. Esto, claro, tiene sus inconvenientes: no podemos hacer repeticiones, ni nada de eso. Nacieron las “sentencias condicionales”: 10 20 30 40 50 60 70 80
INPUT “¿Cómo estás?”,COMO$ IF COMO$=”bien” GOTO 50 IF COMO$=”mal” GOTO 70 GOTO 10 PRINT “Vaya, me alegro.” GOTO 80 PRINT “Alégrate!” END
El siguiente paso es la programación procedimental: consiste en dividir el código en fragmentos independientes. Lenguaje típico, el Pascal. var cad:String; procedure debuti; begin Writeln('me alegro'); end; procedure notandebuti; begin Writeln('alegrate!'); end; begin Writeln('¿Cómo estás?'); readln(cad); if (cad='bien') then debuti else notandebuti; end.
Hay muchos más tipos de programación, y también muchos otros lenguajes muy raros, Smalltalk, ADA, Prolog.... En realidad, hoy en día muchos de estos lenguajes se usan con fines didácticos o experimentales. A nosotros nos interesa la “Programación Orientada a Objetos” (OOP, de Object-Oriented Programming). Se basa en “encapsular” fragmentos de código en “burbujas” independientes entre sí. Los códigos mostrados antes no tienen sentido en OOP. Vamos a ver por qué. Platón y la OOP La pregunta es las siguiente: Tenemos una mesa en la cocina, una en el salón, una mesilla de noche y una en un taller. Estas mesas son diferentes entre sí, pero, sin embargo, hay algo en ellas que hace que todas SEAN mesas. Una es cuadrada, otra redonda, unas con tres patas y otras con cuatro. ¿Por qué, si son tan diferentes, son todas mesas? Este hombre de aquí dijo que había un mundo aparte del nuestro en el que existían unos “seres” perfectos, inmutables, universales, etc etc etc, a quienes llamó Ideas. Los seres de nuestro mundo físico serían “copias” imperfectas de estas Ideas. Imagina por tanto que todos los objetos que tenemos alrededor son copias de las Ideas. De esta forma, al ser copias imperfectas, no serían iguales entre sí, pero seguirían teniendo esa “esencia común”. Esta teoría sirve para explicar la realidad y dar un poco de cabida a la ciencia. Si la ciencia trata sobre las cosas globales, es decir, los hechos universales, no los específicos, entonces tiene que haber algo universal.
Bueno, pues nuestra intención es semejante: vamos a tratar de explicar, no la realidad, sino los problemas que nos encontramos, que de una forma u otra, son parte de la realidad. ¿o no? En OOP definimos un concepto más que fundamental: el concepto de objeto. Un objeto es un conjunto de: a) Datos b) Métodos Supongamos dos objetos semejantes, por ejemplo, dos mesas. Datos que pueden concernir a las mesas, por ejemplo, son su color, número de patas, su forma, etc. Así, si tenemos dos “objetos mesa”, cada uno con datos diferentes, tenemos dos mesas diferentes, que es lo que planteábamos al principio: dos objetos de la misma naturaleza (“mesa”), pero diferentes entre sí. ¡A Platón esto le habría encantado! Debido a que los datos de un objeto definen en parte este objeto (lo diferencian de los demás), a veces llamamos a estos datos “Propiedades”. ¿Se te ocurre algún método propio de una mesa? A mí no, las mesas son objetos puramente pasivos, ellas mismas no hacen nada. Sin embargo, vamos a suponer que el cambio de color es una acción propia de la mesa (y no del pintor). De acuerdo, ya tenemos el diseño de nuestra mesa. Ya conocemos el concepto de objeto. Vamos a seguir con el señor Platón. ¿Cómo un carpintero puede crear una mesa? Según este griego, su alma, antes de nacer, vislumbró las Ideas, y durante la vida de la persona, el alma recuerda algunas de estas Ideas (aunque no todas....) Así, un carpintero recuerda haber visto la “Idea de Mesa”, y por ello sabe hacer mesas. Es decir, el carpintero se FIJA en la “Idea de Mesa” para crear una mesa. Si ya conocemos lo que es un Objeto, ¿Qué es una Idea? Pues, sencillamente, la definición de ese objeto, que, por cierto, nosotros llamamos clases. Platón dice que los objetos físicos son copias de las Ideas. Nosotros decimos que los objetos con instancias de las clases.
Para Platón, los objetos son copias de unos seres universales llamados Ideas. Las Ideas son, por tanto, la definición de los objetos físicos, aunque éstos pueden ser diferentes entre sí. Para la OOP, los Objetos son instancias de unas definiciones generales que llamamos Clases. Dos objetos con propiedades diferentes (dos mesas con diferente color) siguen siendo instancias de la misma clase (la “clase mesa”). Éstos son los conceptos fundamentales de la OOP. Es estrictamente necesario comprenderlos a fondo antes de seguir adelante. Párate un momento a pensar si entiendes a fondo... OBJETO CLASE PROPIEDADES MÉTODOS INSTANCIA ¡Cuidado! Si decimos que dos objetos de la misma clase tienen las mismas propiedades, tal vez lo que realmente queramos decir es que dos objetos de la misma clase tienen EL MISMO VALOR PARA TODAS Y CADA UNA.DE SUS PROPIEDADES. Es evidente que dos objetos de la misma clase van a tener las mismas propiedades: dos mesas tendrán color, forma, número de patas, etc. Pero tal vez digamos que dos mesas tienen las mismas propiedades cuando queremos decir que ambas mesas tienen la misma forma, el mismo color y el mismo número de patas. Hay que tener cuidado con esto, aunque tampoco es tan importante. Para comerse el coco.... (1) Tenemos dos objetos de la misma clase, con exactamente las mismas propiedades ¿Son el mismo objeto? Java Java es un lenguaje de programación. La verdad es que no va a recibir ningún premio por este simple hecho. Sin embargo, sí va a recibir cierta atención por nuestra parte, porque, sencillamente, es la herramienta que nosotros vamos a utilizar. Que quede muy claro, Java es un MEDIO para programar en OOP. Podemos programar en OOP con otros muchos lenguajes, Delphi, Visual Basic, o C++. Java nació con una intención muy clara: hacer la vida más fácil al programador. C++ es el lenguaje más potente que existe ahora mismo. Pero tiene un grave problema: es lo más complicado que hay. Un ejemplo:
#include // Define la clase derivada de TApplication class THelloApp : public TApplication { public: THelloApp(LPSTR AName, HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) : TApplication(AName, hInstance, hPrevInstance, lpCmdLine, nCmdShow) {}; virtual void InitMainWindow(); };
// el MainWindow de la clase void THelloApp::InitMainWindow() { MainWindow = new TWindow(NULL, "Hola Mundo"); } int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { THelloApp HelloApp ("HelloApp", hInstance, hPrevInstance, lpCmdLine, nCmdShow); HelloApp.Run(); return HelloApp.Status; }
Bueno, pues este fragmento de código abre una ventana con el título “Hola Mundo!”. Visto lo visto, Java trata de hacer la vida un poco más fácil. Además, un programa en Java ocupa bastante poco espacio en disco, mientras que uno en C++ ocupa todo lo que quieras y un poco más. Así que, gracias a eso, Java es también propicio para aplicaciones que corran por Internet y esas cosas. Pero todo eso ya lo veremos. Java NO lo entiende directamente el procesador. Java es un lenguaje INTERPRETADO, es decir, que se “traduce” para que el procesador pueda entenderlo. C++, una vez compilado, es entendido perfectamente por el procesador, mientras que Java no. Java se compila y se generan los “bytecodes”, que es otro lenguaje mucho más sencillo, pero el procesador sigue sin entenderlo. Para ello, los bytecodes tienen que
INTERPRETARSE por la “Java Virtual Machine” (JVM), que, en realidad, es otro programa (escrito, por cierto, en C++) que sabe interpretar los bytecodes. Bueno, esto es un poco extraño, pero en realidad es muy fácil. Al escribir un programa en C++, por ejemplo, se compila y funciona solito. Pero al escribirlo en Java, se compila y necesita otro programa que lo haga funcionar. Este otro programa se llama JVM. Ya está. No es tan mortal, ¿no? ¿Ventajas de Java sobre los lenguajes no interpretados? muchas, por ejemplo, seguridad: Si un programa en Java intenta hacernos la puñeta, la JVM lo detendrá, mientras que el procesador no podrá detener nunca un virus escrito en C++. Otra ventaja es el ya mencionado reducido tamaño de los programas en Java. Otra, mucho más importante, un programa en Java funciona en Windows, en Linux, en Mac, en Solaris, etc, porque lo que cambia es la JVM. Aquí no vamos a extendernos en el funcionamiento de los compiladores de Java y las JVM. Sin embargo, vamos a recordar un poco el proceso. 1.- Creamos el archivo “xxxx.java” 2.- lo compilamos con “javac xxxx.java” 3.- lo ejecutamos con “java xxxx” El paso 3 es el que invoca a la JVM. Más explicaciones, ayuda en línea. Antes de seguir adelante, vamos a pararnos un poco para recapacitar sobre si has entendido los conceptos de:
LENGUAJE LENGUAJE INTERPRETADO Y COMPILADO COMPILADOR JVM BYTECODES
Empiezo a plantearme esto de programar....
Para comerse el coco......(2) Teniendo en cuenta que la JVM es un programa escrito en un lenguaje compilado, ¿puede ser entendido por la propia JVM? El movimiento se demuestra andando. Vamos a escribir un pequeño programa en Java. ¿Su fin? definir las mesas que tanto tiempo nos han acompañado. Recordemos la CLASE MESA: Propiedades: color, número de patas, forma Métodos: cambiar el color
Bueno, vamos a pensar un poco. Como sabrás, los nombres o IDENTIFICADORES en Java son una cadena de caracteres que almacenan un valor determinado, o el nombre de un método, o el nombre de una clase, o yo qué sé. Tenemos que tratar de inventarnos identificadores con nombres sencillos, que no sean rebuscados, sin caracteres raros. A mí se me ocurren los siguientes: para el color, pues “color” para el número de patas, “numPatas” para la forma, pues “forma” para el método que cambia el color, pues “cambiarColor”
Definición de propiedades Vamos a centrarnos en las propiedades, y luego veremos los métodos. Seguimos pensando. El color será una cadena de caracteres (“marrón”, “violeta”, joer, una mesa violeta...), que en Java se denomina “String”, al igual que la forma (“Redonda”, ”cuadrada”). El número de patas será un número entero, que se llama “int”. La definición de la clase Mesa.java empezaría así: class Mesa { String color; int numPatas; String forma; . . . Esto ya tenemos que ser capaces de entenderlo bien. Una mesa tiene un color, un número de patas y una forma. El número de patas es un número entero, mientras que la forma y el color son cadenas de caracteres. Bueno, pues así se han definido. Ahora no ha lugar a pensar cómo se define el color de una mesa determinada, o su forma, o cómo saber qué color tiene otra mesa cualquiera..... todo eso ya lo veremos. Ahora estamos DEFINIENDO la clase Mesa, no hacemos nada más. En cualquier libro encontrarás los tipos básicos de variables y sus rangos de valores. Tendrás que aprendértelos, aunque sólo para el examen. Luego, puedes olvidarlos. Básicamente son
int, short, long, double, float, char, String y boolean. Este es un buen momento para aprenderte los tipos de datos básicos. Definición de métodos ¿ya te los has aprendido? ¡Bien! Vamos a por el método cambiarColor. Un método consta, aparte de su nombre (o IDENTIFICADOR), claro está, de
a) valor de retorno. b) parámetros Un método puede devolver un valor, o puede no hacerlo. Por ejemplo, un método que calcule la suma de dos números debería devolver otro número, o un método que abra un fichero del disco debe devolver si ha podido abrirlo o no. Ahora bien, hay métodos que no tienen por qué devolver nada. Por ejemplo, un método que muestre un saludo en pantalla no tiene por qué devolver nada. A la hora de definir el tipo de dato que devuelve un método, se antepone al nombre de ese tipo de dato (int, float, boolean, String....) al nombre del método. Por ejemplo: int calculaSuma() float raizCuadrada() boolean abrirArchivo()
// devuelve un entero // devuelve un real // devuelve un valor lógico
Los métodos que no devuelven nada se declaran como void: void saludo() // no devuelve nada Bien. Vayamos ahora a por los parámetros. Un método que calcule 5*4 está claro que no va a necesitar parámetros, porque el método no puede hacer nada más que calcular 5*4. Sin embargo, un método que calcule la multiplicación de dos enteros cualquiera necesitará conocer esos enteros, es decir, se los daremos como parámetro. Es como si queremos saber dónde está determinada calle, y le preguntamos a alguien: tenemos que decirle qué calle buscamos. Imagínate, “Disculpe, ¿podría decirme dónde está la calle?” No tiene sentido, habría que preguntar
“Disculpe (educación ante todo), ¿podría decirme dónde está la calle Butarque?”. Bueno, pues como éste se te pueden ocurrir mil ejemplos
Es MUY IMPORTANTE que diferencies cuándo un método necesita parámetros y cuándo no.
Una cosa sí es cierta: no hay una regla general para saber si un método necesita parámetros, para saberlo necesitamos la mplementación del código, y el sentido común. Vamos a ver unos ejemplos: int suma(int a, int b) recibe un entero a y un entero b y devuelve otro entero String concatenar(String a, String b) recibe dos cadenas de caracteres y devuelve otra cadena de caracteres float raizCuadrada(int a) recibe un entero y devuelve un real Debe quedar MUY MUY MUY claro este rollo de los parámetros y del tipo de dato devuelto por un método. Vamos a ver los métodos anteriores implementados y comentados para que se vea todo mucho mejor, y ya de paso aprendemos cómo hace un método para devolver un valor usando “return”: int suma(int a, int b){ return a+b; } Este método recibe dos enteros, a y b, y devuelve su suma, que será otro entero. Si hacemos en cualquier parte del programa int numero = suma(3,5);
estamos diciendo que el entero “numero” va a valer lo que devuelva el método “suma” pasándole como parámetros 3 y 5. “suma” devolverá 3+5, y entonces “numero” pasará a valer 3+5. String concatenar(String a, String b){ return a+“ ”+b; // las comillas encierran un espacio en blanco } Recibe dos cadenas y las concatena poniendo un espacio en blanco entre medias. Fíjate en la diferencia entre usar “+” con números y usarlo con cadenas. Si en cualquier punto del programa escribimos String cad = concatenar(“Hola”,”Mundo”); estamos diciendo que la cadena (String) “cad” va a valer el resultado que “concatenar” devuelva pasándole como argumentos las cadenas “Hola” y “Mundo”; Como “concatenar” coge las dos cadenas que hemos pasado como parámetros (“Hola” y “Mundo”) y las devuelve juntas con un espacio entre medias (devuelve “Hola Mundo”), la cadena “cad” pasará a valer “Hola Mundo” void saludo(){ System.out.println(“Holas!”); } este método no tiene parámetros, y no devuelve nada de nada. Así, se le invocará en cualquier parte del programa: saludo(); y ya está. Como en el método no hay definidos parámetros, pues no le pasamos parámetros, y como el método no devuelve nada, pues es ilícito escribir algo como: int resultado = saludo();
// mal!!!!!
porque “saludo()” no devuelve nada (recuerda: se ha definido como “void” y no se ha escrito ningún “return”), y como no devuelve nada, Java no sabe qué valor meter en “resultado”. Bueno, pues este es el funcionamiento de un método. A modo de resumen, cuando un método es invocado, puede necesitar o no parámetros. Que los necesite o no depende de cómo hemos definido dicho método, es decir, si hemos definido variables entre los paréntesis del método: ... metodo(parametro1, parametro2, ....) int suma(int a, int b) o si no las hemos definido:
por ejemplo:
... metodo() por ejemplo: void saludo() Un método puede devolver un valor, o bien puede no devolver nada. Depende de cómo hemos definido el método: si lo definimos como “void”: void saludo() entonces no devuelve nada, pero si lo definimos de otra forma: int suma(....) entonces devolverá un dato de un tipo determinado (int, float...), y será OBLIGATORIO escribir un “return” en el cuerpo del método. int suma(int a, int b){ int resultado = a+b; } Este método es casi igual al que hemos visto poco más arriba, peeeeeeeeeero tiene un grave problema: hemos dicho que devuelve un entero ( INT suma(int a, int b) ), pero no hay ningún “return” en el cuerpo del método. Habrá un error de compilación! Una cosa más, que un método devuelva algún valor NO IMPLICA (curiosamente) que haya alguna variable que reeciba dicho valor. Es decir, las siguientes líneas son perfectamente legales: int resultado = suma(5,3); . . . suma(5,3); La única pega es que la segunda línea llama al método “suma”, le pasa sus parámetros pertinentes, pero nadie recibe el resultado. Esto, parece una chorrada, pero es útil. Supongamos que tenemos un método: boolean abrirFichero(String nombreFichero) cuya tarea es abrir un fichero determinado. Este método devuelve un boolean que indica si se ha abierto el fichero con éxito. Bien, puede que en algún momento hagamos: boolean hemosPodido = abrirFichero(“miFichero.txt”);
para saber si hemos podido abrir el fichero. Esto sería lo más normal, pero en algunas ocasiones no necesitamos saber si se ha abierto exitosamente, porque sabemos que va a ser así, por ejemplo, al hacer algún programilla tonto en nuestra casa. Si no necesitamos asegurarnos de si el fichero se ha abierto o no, entonces podemos hacer abrirFichero(“miFichero.txt”); e ignoramos el valor que abrirFichero devuelve. Bueno, ¡pues esto es todo sobre los métodos! Antes de seguir adelante, como siempre, asegúrate de haber entendido bien los conceptos de: PROPIEDAD MÉTODO TIPO DE DATOS (int, float....) PARÁMETROS VALOR DE RETORNO DE UN MÉTODO SENTENCIA RETURN VOID Recuerda siempre que un valor de retorno puede ser ignorado Ejercicio Una vez hayas entendido perfectamente todo este rollo, puedes escribir la definición completa de la clase Mesa. Plantéate qué parámetros le pasaríamos al método cambiarColor() y qué valor devolvería. ¡Vamos! Solución A mí se me ocurre que cambiarColor() tendría como parámetros una única String, y no devolvería nada. El resto de la clase ya lo hemos definido.
class Mesa { String color; int numPatas; String forma; void cambiarColor(String nuevoColor){ color = nuevoColor; }
Empieza lo bueno...
}
Creación y uso de objetos Ya tenemos definida la clase Mesa. Ahora quiero crear una mesa. ¿Cómo se hace eso?... primero: las referencias Cuando creemos un objeto, la JVM genera una “burbujita” que es nuestro objeto. Claro, que para que ese objeto haga cosas necesitamos “llamarlo” de alguna forma. Si quieres que, en medio de una fiesta atestada de gente tu amigo Miguel te diga, por ejemplo, la hora, como digas “¡Dime la hora!”, no te van a hacer ni caso, porque cada uno pensará (si te ha oído) que no le estás llamando a él. Tendrás que decir “¡Miguel, dime la hora!”, para que el señor Miguel te haga caso. En definitiva, que si vas dando gritos por la calle ni Dios te va a hacer caso, mientras que si a cada frase le antepones el nombre de la persona a la que imperas, pues ya te puede atender. En Java esto es exactamente igual, a cada objeto se le debe dar un nombre para poder “llamarle” de alguna forma cuando le necesites. Bien, pues a estos nombres se les denomina Referencias. Si yo creo dos mesas y quiero cambiar el color de una sóla, ¿Cómo le digo a la JVM a qué mesa quiero cambiar el color? Pues fácil, si creo dos mesas, a una la llamo “Margarita” y a la otra “Alberto”, y luego digo que cambie el color de Margarita. segundo: el operador “new” “new“significa “nuevo”. Y este es un ejemplo de otra cosa que por este simple hecho no va a recibir ningún premio. “new” sirve para a) crear un nuevo objeto b) definir la REFERENCIA a ese nuevo objeto. ¿Cómo se usa? Pues muy fácil:
CLASE REFERENCIA = new CLASE();
por ejemplo: Mesa Margarita = new Mesa(); Mesa Alberto = new Mesa(); por ahora, olvida los paréntesis de “new CLASE()”
¿Por qué esos paréntesis en “new Mesa()”? Por ahora, haz caso al pez. Pues ya sabemos definir objetos e incluso crearlos! Ahora, vamos a aprender a utilizarlos. Supongamos que quiero crear dos mesas, Margarita y Alberto, y quiero cambiar el color de la mesa Margarita. Tendré que llamar al método cambiarColor() de la mesa Margarita. ¿Cómo se hace esto? Pues usando algo tan tonto y tan simple como un punto: Mesa Margarita = new Mesa(); Mesa Alberto = new Mesa(); Margarita.cambiarColor(“Rojo”); ¿Quiero cambiar el color de la mesa Alberto? Alberto.cambiarColor(“Azul”); Ya está. Hala. ¿Parecía complicado todo esto? Pues ya ves qué complicación más grande. Ya sabemos definir objetos, crearlos y usar sus métodos. Para que te convenzas, aquí tienes un cachito de mi práctica de Junio: log.write(" log.write(" log.write(" log.write("
tipoReserva horaInicio horaFin fecha
= = = =
"+r.getTipoReserva()); "+r.getHoraInicio()); "+r.getHoraFin()); "+r.getStringFecha();
Sabiendo que “log” y “r” son objetos, ¿Hay algo complicado? ¿Hay algo que no entiendas? ¡Claro que no! Constructores
A primera vista, parece que “new” es un CONSTRUCTOR, porque su misión es construir nuevos objetos. Bueno, pues nooooop. “new” INVOCA a un constructor, NO ES un constructor. Bueno, no es tan difícil. “new” invoca a un constructor. Vale. ¿Y qué es un constructor? Un constructor es un método especial, propio de cada clase. Su función es INICIALIZAR el contenido, las propiedades, el estado, etc etc del objeto. Podemos hacer que cuando creemos una mesa cualquiera, comience siendo cuadrada de cuatro patas y azul. Pero eso lo veremos luego. ¿Por qué el constructor es especial? Por varias razones: Primero: se invoca UNA VEZ para cada objeto. Los métodos de un objeto se pueden llamar todas las veces que quieras: Margarita.cambiarColor(“rojo”); // y acto seguido.... Margarita.cambiarColor(“azul”); // y otra vez... Margarita.cambiarColor(“verde”); Sin embargo, el constructor va a ser invocado una sóla vez, cuando lo diga “new”. Segundo: nunca devolverá ningún valor Tercero: sin embargo, no se define como “void”. Recuerda que cuando un método no devuelve ningún valor, se define como “void”. Bueno, pues el constructor no. Cuarto: su nombre es el mismo nombre de la clase a la que pertenece. No puedes escoger el nombre del constructor. ¿Recuerdas los paréntesis a los que se refería el pez? Mesa Margarita = new Mesa(); ¿Por qué estos paréntesis? Pues está ya claro: “Mesa()” es el nombre de un método, el CONSTRUCTOR, y por eso lleva paréntesis. Bueno, pues una vez dicho esto, vamos a cambiar un poco lo que decíamos sobre el uso de “new”, y vamos a cambiarlo por: CLASE REFERENCIA = new CONSTRUCTOR(); Antes de seguir, asegúrate de entender bien los conceptos de REFERENCIA OPERADOR PUNTO “.”
NEW CONSTRUCTOR Definición de constructores Vamos a definir un constructor para la clase Mesa. Nuestra intención, hacer que cada mesa nueva sea cuadrada, azul y con cuatro patas. Bueno, pues es muy fácil, añadimos a la clase el método en negrita:
class Mesa { String color; int numPatas; String forma; void cambiarColor(String nuevoColor){ color = nuevoColor; }
RECUERDA: el constructor tiene el mismo nombre que su clase, no devuelve ningún valor, y se define sin “void”
Mesa() { color = “azul”; numPatas = 4; forma = “cuadrada”; } } Ahora, cuando hagamos Mesa Margarita = new Mesa(); Margarita será una mesa cuadrada azul de cuatro patas, ¡Una mesa en condiciones, vaya! Pero hay un problema: yo no quiero mil mesas iguales. Yo quiero crear una mesa como a mí me dé la gana, yo no quiero que todas las mesas sean azules y cuadradas con cuatro patas. ¿Se te ocurre algo para solucionar esto? ¡Podemos definir un constructor con parámetros! Así, cuando creemos una mesa le daremos su color, su número de patas y su forma iniciales. A ver, sería algo así: Mesa(String colorInicial, int numPatasInicial, String formaInicial){ color = colorInicial; numPatas = numPatasInicial; forma = formaInicial; }
Esto es genial. Ahora podemos hacer: Mesa Margarita = new Mesa(“rojo”, 4, “redonda”); Mesa Catalina = new Mesa(“verde”, 3, “triangular”); Hala. ¿Qué constructor te gusta más? Podemos prescindir de uno de ellos. Aunque, gracias a algo que llamaremos “Polimorfismo”, podemos quedarnos con los dos constructores A LA VEZ. (¡Vaya! dos constructores diferentes!)
El Polimorfismo permite tener varios métodos diferentes entre sí y con el mismo nombre. Un buen momento para echarle un vistazo!
Si te digo la verdad, yo pocas veces uso los constructores con parámetros (que no significa que sean malos, ¿eh? no te creas......). En su lugar, utilizo los.....
Métodos get/set Cuando no definimos un constructor en una clase, al invocar “new” la JVM usará el CONSTRUCTOR POR DEFECTO, que inicializa las propiedades del objeto con ceros, referencias nulas, etc (¡que lo de “referencias nulas” no te asuste! ya lo veremos). Para inicializar las propiedades usamos un constructor “personalizado”, “propio”, “característico”, o como lo quieras llamar. Estupendo. supón que en medio del programa quiero cambiar UNA SOLA propiedad de un objeto. En nuestro caso, puedo cambiar el color. Pero imagina que tengo una mesa roja: Mesa Margarita = new Mesa(“rojo”, 4, “redonda”); Y quiero hacer que sea amarilla. Bueno, pues tengo dos opciones:
primera: crear una nueva mesa, casi igual: Mesa Margarita = new Mesa(“amarillo”, 4, “redonda”); (Esto parece guai, pero te aseguro que no siempre va a resultar fácil) segunda: definir una serie de métodos que cambien UNA SOLA propiedad del objeto. A este tipo de métodos se les denomina “métodos set”, porque suelen comenzar con la palabra “set” (“poner”): Vamos a definir tres nuevos métodos para nuestra clase Mesa:
void setColor(String nuevoColor){ color = nuevoColor; } void setForma(String nuevaForma){ forma = nuevaForma; } void setNumPatas(int nuevoNumPatas){ numPatas = nuevoNumPatas; } Fíjate tú qué cosas, oche. Ahora resulta que “setColor” y “cambiarColor” hacen exactamente lo mismo. Prescindiremos de uno de ellos. Por ejemplo, de “cambiarColor”. Está bien esto, ¿eh? Bueno, tómate un respiro, y vamos a por los métodos “get”. Su función es obtener (get) el valor de una propiedad determinada. No es difícil adivinar que todo método “get” se definirá con algún tipo de dato de retorno (int, String...) y que además tendrá un “return”. Normalmente los métodos “get” no tendrán parámetros. ¿Recuerdas todo eso? Si no, ya sabes.... Vamos a definir los métodos “get” de nuestra clase Mesa. Empecemos con el color. Llamaremos al método “getColor”. Ya que el color es una String, getColor devolverá una String: String getColor(){ return color; }
De forma semejante haremos con getForma: String getFoma(){ return forma; } getNumPatas devolverá un valor entero: int getNumPatas(){ return numPatas; } Bueno, pues resumimos nuestra clase Mesa; tres propiedades, tres métodos “set” para definir esas propiedades, tres métodos “get” para obtener el valor de las propiedades, un constructor con parámeros y otro sin parámetros, y pasamos del método “cambiarColor”. La clase Mesa está escrita en la siguiente página. Aquí no cabe....
class Mesa { String color; int numPatas; String forma; void setColor(String nuevoColor){ color = nuevoColor; } void setNumPatas(int nuevoNumPatas){ numPatas = nuevoNumPatas; } void setForma(String nuevaForma){ forma = nuevaForma; } String getColor(){ return color; } String getForma(){ return forma; } int getNumPatas(){ return numPatas; } Mesa(String colorInicial, int numPatasInicial, String formaInicial){ color = colorInicial; numPatas = numPatasInicial; forma = formaInicial; } Mesa() { color = “azul”; numPatas = 4; forma = “cuadrada”; } }
Variables de clase. Modificador static. Supón que tenemos una empresa que publica páginas web en Internet para otras compañías. Supón que, como empresa que somos, en cada página queremos anunciar nuestros servicios, por ejemplo, poniendo nuestro número de teléfono. Así, cada vez que alguien visitase la página de uno de nuestros clientes sabría cuál es nuestro teléfono para así poder contratar nuestros servicios. Hasta aquí bien, ¿verdad? Vale. Supón que tenemos una clase paginaGué con las propiedades dirección, contenido y numeroTeléfono. Cada vez que creemos una página, le daremos un contenido, una dirección y nuestro número de teléfono. Por supuesto definimos en la clase todos los métodos set y get necesarios. Creo que eres Ahora supón que cambiamos nuestra sede y, por tanto, totalmente capaz de nuestro número de teléfono. escribir la clase paginaGué ¡Dios! ¡Hemos creado mil páginas web y ahora tenemos que cambiar el número de teléfono de todas! ¡Mil llamadas al método setNumeroTeléfono! Pues no, si hacemos que todos los objetos de la clase paginaGué compartan la propiedad numeroTeléfono. Así, si cambiamos el número de teléfono de una sola página, cambiará el de todas las demás páginas. Guai. Creo que este ejemplo es muy ilustrativo, ¿verdad? Bueno, pues pasemos a la acción. En Java, una propiedad que se comparte por todos los objetos de una clase se llama “variable (propiedad) de clase” o “estática”. ¿Por qué? La primera denominación hace referencia a que podemos entender que una variable compartida NO pertenece a los propios objetos, sino sólo a su clase. Es como si las Ideas de Platón definiesen no sólo las propiedades y el comportamiento de los objetos físicos, sino que además definiesen el contenido de alguna propiedad. Si todas las mesas fuesen de madera, la propiedad “material” de una mesa estaría definida en la Idea de mesa, no en cada mesa. Bueno, pues es sólo una idea. Respecto al otro nombre, “variable estática”, éste viene dado porque en Java, para definir una variable compartida se le antepone el modificados “static”: static int numeroTeléfono; Hala. Pues ya está. Añadiendo un “static” antes de una propiedad hacemos que esa propiedad sea compartida por todos los objetos. Supón ahora que queremos saber cuántas mesas hemos fabricado. Bueno, pues vamos a añadir una propiedad estática numMesasFabricadas a la clase Mesa.
Inicialmente, numMesasFabricadas valdrá cero, pero cada vez que un constructor sea invocado, aumentará en una unidad. Después definiremos un método getNumMesasFabricadas() para saber cuántas mesas llevaremos creadas. Bueno, pues la clase Mesa quedaría definida asín (de nuevo, en una hoja aparte):
class Mesa { String color; int numPatas; String forma; static int numMesasFabricadas = 0; void setColor(String nuevoColor){ color = nuevoColor; } void setNumPatas(int nuevoNumPatas){ numPatas = nuevoNumPatas; } void setForma(String nuevaForma){ forma = nuevaForma; } String getColor(){ return color; } String getForma(){ return forma; } int getNumPatas(){ return numPatas; } Mesa(String colorInicial, int numPatasInicial, String formaInicial){ color = colorInicial; numPatas = numPatasInicial; forma = formaInicial; numMesasFabricadas = numMesasFabricadas + 1; } Mesa() { color = “azul”; numPatas = 4; forma = “cuadrada”; numMesasFabricadas = numMesasFabricadas + 1; } int getNumMesasFabricadas() { return numMesasFabricadas; } }
Y ya está. Ahora, si hacemos: Mesa Margarita = new Mesa(“Azul”, 3, “redonda”); int num1 = Margarita.getNumMesasFabricadas(); Mesa Catalina = new Mesa(“Verde”, 4, “triangular”); int num2 = Catalina.getNumMesasFabricadas(); int num3 = Margarita.getNumMesasFabricadas(); ¿Cuánto valdrán num1, num2 y num3? Pues, respectivamente, 1, 2 y 2. Tal vez te hagas la preguna siguiente: si podemos definir propiedades compartidas, ¿Podemos hacer lo mismo con los métodos? ¿Tiene sentido definir un método compartido? ¿Cómo se hace? ¿Una existencia divergente implica un universo divergente? Bueno, pues sí se puede definir un método estático, y sí que tiene sentido. Además, se hace igual que con las propiedades, anteponiendo un “static” a la definición del método. Lo de la existencia lo dejamos para otro momento. Por ejemplo, podemos saber cuál es el número de mesas fabricadas sin necesidad de “preguntárselo” a una mesa determinada. ¡Podemos preguntar a la propia clase Mesa! Cambiamos el método: static int getNumMesasFabricadas() { return numMesasFabricadas; } y ahora podemos hacer: Mesa Margarita = new Mesa(“Azul”, 3, “redonda”); int num1 = Mesa.getNumMesasFabricadas(); Mesa Catalina = new Mesa(“Verde”, 4, “triangular”); int num2 = Mesa.getNumMesasFabricadas(); int num3 = Mesa.getNumMesasFabricadas(); Fíjate que la llamada a getNumMesasFabricadas() la hacemos sobre Mesa y no sobre un objeto determinado. Bueno, éste hecho no debe traerte de cabeza nunca de los jamáses, ¡de verdad! los métodos y las variables estáticas no es que se usen demasiado, ¿eh? Además, los puristas de la OOP (que son personas) no admiten nada estático, ni propiedades ni métodos, salvo en una excepción: las constantes.
Constantes ¡Sí! También podemos definir constantes en Java. Una constante, como comprenderás, no sólo no puede cambiar de valor: además, debe ser compartida por todos los objetos. Vaya una constante tan estúpida aquélla que no es la misma en todos los objetos de la clase donde esa constante se define. En Pascal, C, C++, BASIC, etc etc etc las constantes se definen como const (de constant). Bueno, pues en Java, como es un lenguaje tan especialito el jodío, se definen con final. Es decir, que si yo quiero definir una constante, por ejemplo, pi, hago: static final int pi = 3,1415926535897932384626433832795; Hala. Ahora pi es una variable compartida por todos los objetos de la clase en la cual hemos definido la propia pi, y además no puede cambiar de valor. Hala. Antes de seguir adelante, ya sabes... VARIABLE COMPARTIDA MODIFICADOR STATIC MÉTODO COMPARTIDO LLAMADAS A MÉTODOS MEDIANTE EL NOMBRE DE LA CLASE ( Mesa.getNumMesasFabricadas() ) MODIFICADOR FINAL Para comerse el coco..... (3) La clase Math contiene métodos que hacen cálculos numéricos, como coseno, seno, etc..... Si miras la definición de estos métodos, verás que ninguno se salva, todos son métodos static. ¿Por qué crees que es así? El método main, arrays y ¡a lanzar programas! Seguro que te has dado cuenta de que hemos definido mil clases, métodos y propiedades, pero que realmente todavía no hemos hecho ningún programa. Bueno. Vamos a solucionar eso. Un programa siempre tiene un punto de arranque, es decir, empieza en un momento determinado. Bueno, pues en Java, este punto de arranque se llama método main. Un programa suele constar de varios archivos, uno por cada clase que definimos. Bueno, pues en una de estas clases debemos definir el método main. Como comprenderás, será una buena práctica de programación definir una clase exclusivamente para albergar el main. No es necesario, pero es muy buena práctica, es decir, que al margen de toda nuestra fabulosa colección de clases definimos otra más cuyo contenido sea un sólo método main. Bueno, pero vamos con el propio método.
El main se define siempre igual, por norma en Java. Es un método: estático: ya sabes qué es esto. público: si lo sabes, te felicito. Básicamente significa que puede accederse a este método desde cualquier lugar, a diferencia de otros métodos que pueden ser privados. Bueno, ya lo veremos, ¿vale? no devuelve datos: se define como void se llama siempre “main” recibe como argumentos un array de Strings: también lo veremos. main queda definido entonces como: public static void main(String[] IDENT) Como comprenderás, IDENT es un identificador al que puedes llamar como te dé la gana. Normalmente recibe el nombre arg o args. ¿Para qué sirve? Cuando ejecutamos un programa Java, lo que hacemos es escribir java clase y esto invoca a la JVM. Pero nosotros somos muy listos, y podemos arrancar el programa pasándole argumentos: java clase arg1 arg2 arg3 ... argn Bueno, pues la JVM coge estos argumentos los mete en una lista. A este tipo de listas se les denomina array, y funciona de la siguiente manera: Un inciso: arrays Un array es una lista ordenada de elementos. Cada elemento tiene asociado un índice. El primer índice es el cero, y el último depende del número de elementos que haya guardados en el array. Un array, como una variable cualquiera, tiene un TIPO (int, float, String...) y un IDENTIFICADOR. Veamos cómo se declaran: tipo[] IDENT = new tipo[tamaño]; por ejemplo, hagamos un array de Strings de 5 posiciones: String[] miArray = new String[5];
y usamos el array como si fuese una variable normal, pero teniendo en cuenta los índices en los que guardamos valores: miArray[0] = “Posición primera...”; miArray[1] = “... segunda...”; miArray[2] = “...tercera...”; miArray[3] = “...cuarta...”; miArray[4] = “y al siguiente día dejé el colegio”; Para saber el número de posiciones de un array usamos el operador .length ¡Fíjate en el punto inicial de .length!: int tamaño = miArray.length; Ahora tamaño vale 5. sacabó el inciso. Sigamos con el main. public static void main(String[] args) Cuando invocamos a la JVM, ésta determina el número de argumentos que hemos introducido en java clase arg1 arg2 arg3 ... y crea el array que hemos llamado args. Bueno, Podemos, por ejemplo, escribir un programa al que le pasemos como argumentos un color y la forma, y él nos cree una mesa con cuatro patas y con ese color y esa forma. Usando System.out.println podemos mostrar en pantalla las propiedades de la mesa. Meteremos el main en una nueva clase MesaMain (archivo MesaMain.java): class MesaMain{ public static void main(String[] args) { Mesa Margarita = new Mesa( args[0], 4, args[1] ); System.out.println(“Hemos creado una mesa”); System.out.println(“con ”+Margarita.getNumPatas()+” patas,”); System.out.println(“de color “+Margarita.getColor()); System.out.println(“y de forma “+Margarita.getForma()); } } Ahora, si compilamos Mesa y MesaMain con:
javac Mesa.java javac MesaMain.java y hacemos: java MesaMain rojo, redonda el programa nos dice: Hemos creado una mesa con 4 patas, de color rojo y de forma redonda Hala. Nuestro primer programa. Es una pena, normalmente el primer programa que se escribe es mostrar en pantalla el saludo “Hola mundo!”, pero alguna vez hay que saltarse las reglas, ¿no? De todas formas eres perfectamente capaz de programar tal saludo, ¿no? Haz una clase Saludo que contenga un main que muestre en pantalla el saludo “¡Hola mundo!”. En total, cinco líneas de código. ¡Cuidado! El programa MesaMain exige que metamos al menos dos argumentos. Si ponemos sólo uno, habrá un error, y si ponemos siete, los últimos cinco se ignorarán. Programando a lo bestia. Estructuras de control. Hay una serie de estructuras que la mayoría de los lenguajes de programación poseen. Son las estructuras del control. Estas estructuras son fundamentalmente las condicionales y las repetitivas. Empecemos por las primeras. Sentencias condicionales sentencia if if es la estructura condicional por antonomasia en todos los lenguajes. Su sintaxis es muy fácil: if (condición) { bloque1 } else { bloque2 }
Si cualquier bloque consta sólo de una sentencia, entonces las llaves correspondientes a ese bloque pueden eliminarse. Es cuestión de comodidad. Expliquemos. la condición es una expresión lógica, es decir, booleana. Si esta expresión da como resultado TRUE, se ejecuta el bloque1, mientras que si la expresión es FALSE se ejecuta bloque2. Así de simple. Claro, que queda un poco al aire eso de la expresión booleana. Veámosla: Una expresión booleana es cualquier cosa que pueda dar como resultado TRUE o FALSE. Por ejemplo, la comparación de dos variables.Este es un buen momento para ver los comparadores de magnitud: == != > < >= 5) && (n2 < 3)) || !(n3 >= n4) Otro tipo de expresión booleana puede ser un valor boolean devuelto por un método. Veamos un ejemplo: Para saber si dos números son iguales utilizamos el símbolo == if (num1 == num2){ System.out.prinln(“Son iguales”); } else { System.out.println(“Son diferentes”); } Sin embargo, para saber si dos String son iguales no podemos utilizar el simbolo de igualdad. Es una peculiaridad de Java que no viene ahora a cuento:
Aparentemente, si tenemos String string1 = “Hola”; String string2 = “Hola”; String string3 = “Adiós”; la expresión booleana ( string1 == string2 ) debería ser TRUE, pero no es así. Para comparar Strings utilizamos el método .equals() Éste es un método que funciona de la siguiente manera: partiendo de las strings anteriores, string1.equals(string2) da un resultado de TRUE, mientras que string1.equals(string3) da como resultado FALSE. En definitiva, .equals() es el método que utilizamos para comparar una String con otra. Como ves, un método puede devolver un valor booleano, por lo que este valor puede ser utilizado en una sentencia if: if (string1.equals( string2 )) { System.out.println(“Son iguales”); } else { System.out.println(“Son diferentes”); }
por supuesto, dentro de un bloque de un if podemos anidar más ifs: if (tal y cual) { hacemos esto if (nosequé){ pues esto otro } else { y si no, esto } } else { if (vaya rollo){ bah, yo me piro } else { paso de todo } }
RECUERDA: los números se comparan con == != > < >= n2. No nos importa saber si n2 > n1. Recuerda que cuando un bloque consta sólo de una sentencia (que puede ser perfectamente una sentencia if), las llaves pueden eliminarse. int n1 = 1; int n2 = 10; if (n1 != n2) if (n1 > n2) System.out.println(“n1 > n2”); // no comprobamos si n2 > n1 else System.out.println(“Son iguales”); Bueno, pues este fragmento es erróneo. Si n1 y n2 son iguales, el programa no avisa. De hecho, tomando los valores 1 y 10, el programa dice que son iguales. La razón es que el else aparentemente pertenece al primer if, pero en realidad pertenece al segundo if! El fragmento correcto sería el siguiente: int n1 = 1; int n2 = 10; if (n1 != n2) if (n1 > n2) System.out.println(“n1 > n2”); else // cualquier sentencia que no haga nada, por ejemplo n1 = n1; else System.out.println(“Son iguales”);
¡Bueno, hay que pasar siempre por estas cosas! Mucho cuidado cuando anides ifs, pon siempre sus elses, aunque no hagan nada. Otra opción, tal vez te guste más, es la siguiente:
int n1 = 1; int n2 = 10; if (n1 != n2) if (n1 > n2) System.out.println(“n1 > n2”); else { } // bloque de código vacío else System.out.println(“Son iguales”);
Aquí hacemos uso de un bloque vacío, lo delimitamos con { }, y sin embargo no metemos nada entre esas llaves. Bueno, todo son opciones. Personalmente te recomiendo que cuando empieces a programar siempre escribas las llaves, aunque encierren una sóla sentencia, o aunque estén vacías: int n1 = 1; int n2 = 10; if (n1 != n2) { if (n1 > n2) { System.out.println(“n1 > n2”); } else { } } else { System.out.println(“Son iguales”); } Así siempre verás claramente los bloques y la dependencia de ifs y elses. Como verás en las pruebas de programas, las sentencias condicionales son siempre una fuente de errores, debido, por ejemplo, a cosas como estas. Estos errores se eliminan mediante las pruebas de caja blanca, es decir, conociendo el código del programa. Si tienes que probar un programa y te dan el código, mira siempre la dependencia de ifs y elses, ¿vale?
sentencia switch Esta sentencia es muy peculiar. Permite ejecutar determinada parte de un fragmento de código en función de un número enteo. Me explico. switch (expresión) { case caso1: bloque1 case caso2: bloque2 . . . } Bueno, pues esto es fácil. Vamos a ver. expresión es un número entero. Si hay algún caso que coincida con ese número entero, entonces se ejecutan todos los bloques que haya a partir de ese caso, no sólo el bloque que corresponde a ese caso. Un poco raro, ¿no? Bueno, veamos un ejemplo: switch (n){ case 1: System.out.println(“uno”); case 2: System.out.println(“dos”); case 3: System.out.println(“tres”); } Date cuenta de que no es necesario poner llaves, sean los bloques como sean, una sentencia o más. Si n = 1, por lo que se ejecuta el caso 1 y todos los casos posteriores, es decir, el 2 y el 3. Por eso, si n = 1 se imprimiría en pantalla uno dos tres
mientras que si n = 2 se imprimiría dos tres y si n = 3, pues se mostraría tres Por la razón que hemos visto. Repito. Se ejecuta el bloque que corresponde al caso y los bloques siguientes. Por eso, si n = 1, se ejecuta el caso 1 y los siguientes, es decir, los casos 2 y 3. ¿Hay alguna forma de hacer que se ejecute sólo el caso que corresponda, y que no se ejecuten los casos siguientes? ¡Pues claro! usando break:
switch (n){ case 1: System.out.println(“uno”); break; case 2: System.out.println(“dos”); break; case 3: System.out.println(“tres”); break; } ahora, si n = 1, se muestra en pantalla uno a diferencia de antes. Otra cosa sobre los switches: existe un caso especial, el llamado default. Se ejecuta este caso si no hay otro caso que corresponda a la expresión: switch (n){ case 1: System.out.println(“uno”); case 2: System.out.println(“dos”); case 3:
System.out.println(“tres”); case default: System.out.println(“más de tres”); } Aquí, si n = 4, se mostraría en pantalla más de tres mientras que si n = 1, se vería uno dos tres más de tres Bueno, no es tan mortal. Para comerse el coco...... (4) ¿se te ocurre algún error en el siguiente código de programa? switch (n){ case 1: System.out.println(“uno”); break; case 2: System.out.println(“dos”); break; case 3: System.out.println(“tres”); break; case default: System.out.println(“más de tres”); } piensa, piensa............ Sentencias repetitivas (o iterativas) Adivina por qué se llaman así. Nos permiten repetir determinado fragmento de código un número definido de veces. En realidad lo que se hace es determinar una condición, una expresión lógica. Así, mientras esta expresión sea TRUE el fragmento se repite, hasta que sea FALSE. Es decir, que no definimos un número de repeticiones, sino una condición para que se
repita. Naturalmente podemos adecuar una condición para que un fragmento se repita un número determinado de veces. Bueeeno, poco a poco. Vale. Ya hemos tomado contacto con las expresiones lógicas, ¿verdad? Fantástico. Apréndetelas bien, porque las vas a usar muncho muncho muncho. while permite repetir un bloque mientras una condición sea TRUE. Primero se comprueba la condición, y si es TRUE entonces se ejecuta el bloque: while (condición){ bloque } Un ejemplo: int n = 0; System.out.println(“a contar”); while (n