programas pdf

Programación I Tabla de Contenido Tabla de Contenido ...................................................................

Views 181 Downloads 4 File size 320KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

Programación I Tabla de Contenido Tabla de Contenido ............................................................................................................................. 1 Unidad I. Fundamentos del Lenguaje.................................................................................................. 3 1.1 Introducción al lenguaje y a su entorno de desarrollo ............................................................. 3 Características de Java ................................................................................................................ 3 1.2 Comentarios .............................................................................................................................. 4 1.3 Variables y Constantes .............................................................................................................. 5 1.4 Objetos que permiten Entrada/Salida por consola ................................................................... 5 Entrada/Salida estándar .............................................................................................................. 6 System.in ................................................................................................................................. 6 System.out............................................................................................................................... 6 System.err ............................................................................................................................... 6 Entrada/Salida por fichero .......................................................................................................... 7 Clase FileOutputStream .......................................................................................................... 8 Clase FileInputStream ............................................................................................................. 8 Clase RandomAccessFile ......................................................................................................... 9 Métodos de desplazamiento............................................................................................... 9 Métodos de escritura .......................................................................................................... 9 Métodos de lectura ........................................................................................................... 10 1.5 Operadores.............................................................................................................................. 11 Operadores aritméticos ............................................................................................................ 12 Operadores aritméticos unarios ............................................................................................... 12 Operadores relacionales ........................................................................................................... 13 Operadores booleanos .............................................................................................................. 14 Operadores de bits .................................................................................................................... 16 Operadores de Asignación ........................................................................................................ 17 Precedencia de operadores ...................................................................................................... 18

1

Utilización de paréntesis ........................................................................................................... 19 1.6 Tipos de datos ......................................................................................................................... 20 1.6.1 Fundamentales ................................................................................................................. 20 Enteros .................................................................................................................................. 20 Números en coma flotante ................................................................................................... 20 Carácter ................................................................................................................................. 20 Booleano ............................................................................................................................... 21 Cadenas ................................................................................................................................. 21 Tabla comparativa de tipos de datos simples en Java .......................................................... 21 1.6.2 Definidos por el usuario ................................................................................................... 21 1.7 Palabras reservadas................................................................................................................. 21 1.8 Expresiones ............................................................................................................................. 22 1.9 Estructuras de control ............................................................................................................. 22 1.9.1 Asignación ........................................................................................................................ 23 1.9.2 Selección........................................................................................................................... 23 Sentencia if-else .................................................................................................................... 24 Sentencia switch.................................................................................................................... 27 Operador if-else .................................................................................................................... 30 1.9.3 Iteración ........................................................................................................................... 31 Sentencia while ..................................................................................................................... 33 Sentencia do-while ................................................................................................................ 35 Sentencia for ......................................................................................................................... 36 for mejorado ......................................................................................................................... 37 Ejercicios........................................................................................................................................ 38 Unidad II. Subprogramas ................................................................................................................... 39

2

Unidad I. Fundamentos del Lenguaje 1.1 Introducción al lenguaje y a su entorno de desarrollo Java es un lenguaje sencillo de aprender. Su sintaxis es la de C++ “simplificada”. Los creadores de Java partieron de la sintaxis de C++ y trataron de eliminar de este todo lo que resultase complicado o fuente de errores en este lenguaje. Java es un lenguaje orientado a objetos, aunque no de los denominados puros; en Java todos los tipos, a excepción de los tipos fundamentales de variables (int, char, long, ...) son clases. Sin embargo, en los lenguajes orientados a objetos puros incluso estos tipos fundamentales son clases, por ejemplo en Smalltalk. El código generado por el compilador Java es independiente de la arquitectura: podría ejecutarse en un entorno UNIX, Mac o Windows. El motivo de esto es que el que realmente ejecuta el código generado por el compilador no es el procesador del ordenador directamente, sino que este se ejecuta mediante una máquina virtual. Esto permite que los Applets de una web pueda ejecutarlos cualquier máquina que se conecte a ella independientemente de qué sistema operativo emplee (siempre y cuando el ordenador en cuestión tenga instalada una máquina virtual de Java). Características de Java Lenguaje totalmente orientado a Objetos. Todos los conceptos en los que se apoya esta técnica, encapsulación, herencia, polimorfismo, etc., están presentes en Java. Disponibilidad de un amplio conjunto de librerías. Como ya se mencionó anteriormente, Java es algo más que un lenguaje. La programación de aplicaciones con Java se basa no solo en el empleo del juego de instrucciones que componen el lenguaje, sino, fundamentalmente, en la posibilidad de utilizar el amplísimo conjunto de clases que Sun pone a disposición del programador y con las cuales es posible realizar prácticamente cualquier tipo de aplicación. Lenguaje simple. Java posee una curva de aprendizaje muy rápida. Resulta relativamente sencillo escribir applets interesantes desde el principio. Todos aquellos familiarizados con C++ encontrarán que Java es más sencillo, ya que se han eliminado ciertas características, como los punteros. Debido a su semejanza con C y C++, y dado que la mayoría de la gente los conoce aunque sea de forma elemental, resulta muy fácil aprender Java. Los programadores experimentados en C++ pueden migrar muy rápidamente a Java y ser productivos en poco tiempo. Distribuido Java proporciona una colección de clases para su uso en aplicaciones de red, que permiten abrir sockets y establecer y aceptar conexiones con servidores o clientes remotos, facilitando así la creación de aplicaciones distribuidas. Interpretado y compilado a la vez Java es compilado, en la medida en que su código fuente se transforma en una especie de código máquina, los bytecodes, semejantes a las instrucciones de ensamblador. Por otra parte, es interpretado, ya que los bytecodes se pueden ejecutar directamente sobre cualquier máquina a la cual se hayan portado el intérprete y el sistema de ejecución en tiempo real (run-time). Robusto Java fue diseñado para crear software altamente fiable. Para ello proporciona numerosas comprobaciones en compilación y en tiempo de ejecución. Sus características de

3

memoria liberan a los programadores de una familia entera de errores (la aritmética de punteros), ya que se ha prescindido por completo los punteros, y la recolección de basura elimina la necesidad de liberación explícita de memoria. Seguro (?) Dada la naturaleza distribuida de Java, donde las applets se bajan desde cualquier punto de la Red, la seguridad se impuso como una necesidad de vital importancia. A nadie le gustaría ejecutar en su ordenador programas con acceso total a su sistema, procedentes de fuentes desconocidas. Así que se implementaron barreras de seguridad en el lenguaje y en el sistema de ejecución en tiempo real. Indiferente a la arquitectura Java está diseñado para soportar aplicaciones que serán ejecutadas en los más variados entornos de red, desde Unix a Windows NT, pasando por Mac y estaciones de trabajo, sobre arquitecturas distintas y con sistemas operativos diversos. Para acomodar requisitos de ejecución tan diversos o variopintos, el compilador de Java genera bytecodes: un formato intermedio indiferente a la arquitectura y diseñado para transportar el código eficientemente a múltiples plataformas hardware y software. El resto de problemas los soluciona el intérprete de Java. Portable La indiferencia a la arquitectura representa sólo una parte de su portabilidad. Además, Java especifica los tamaños de sus tipos de datos básicos y el comportamiento de sus operadores aritméticos, de manera que los programas son iguales en todas las plataformas. Estas dos últimas características se conocen como la Máquina Virtual Java (JVM). Alto rendimiento. Multihilo. Hoy en día ya se ven como terriblemente limitadas las aplicaciones que sólo pueden ejecutar una acción a la vez. Java soporta sincronización de múltiples hilos de ejecución (multithreading) a nivel de lenguaje, especialmente útiles en la creación de aplicaciones de red distribuidas. Así, mientras un hilo se encarga de la comunicación, otro puede interactuar con el usuario mientras otro presenta una animación en pantalla y otro realiza cálculos. Dinámico El lenguaje Java y su sistema de ejecución en tiempo real son dinámicos en la fase de enlazado. Las clases sólo se enlazan a medida que son necesitadas. Se pueden enlazar nuevos módulos de código bajo demanda, procedente de fuentes muy variadas, incluso desde la Red. Produce applets Java puede ser usado para crear dos tipos de programas: aplicaciones independientes y applets. Las aplicaciones independientes se comportan como cualquier otro programa escrito en cualquier lenguaje, como por ejemplo el navegador de Web HotJava, escrito íntegramente en Java. Por su parte, las applets son pequeños programas que aparecen embebidos en las páginas Web, como aparecen los gráficos o el texto, pero con la capacidad de ejecutar acciones muy complejas, como animar imágenes, establecer conexiones de red, presentar menús y cuadros de diálogo para luego emprender acciones, etc.

1.2 Comentarios A medida que los programas se tornen más complejos, podría ser que su código sea difícil de leer para otros programadores debido a la cantidad de instrucciones que contengan. Ya que esos programadores tendrán que entender y, posiblemente modifica sus programas debe escribirlos del modo más legible posible. Al crear un programa, puedes colocar notas dentro del código fuente con el objeto de explicar el procesamiento de aquél. Esas notas, llamadas comentarios, no sólo son útiles para que otros programadores entiendan el programa, también puede ayudar a que recuerdes, después de

4

varios meses de que no has visto el programa, por qué utilizaste esas instrucciones específicas. La forma más común de incluir un comentario dentro del código del programa es, sencillamente, colocar dos diagonales, como se muestra enseguida: //Este es un comentario Cuando el compilador de Java encuentra dos diagonales, ignora todo el texto que le sigue en esa misma línea. Al principio de cada coloca debes colocar, cuando menos, comentarios que indiquen quién es el autor del programa, cuando lo escribió y por qué.

1.3 Variables y Constantes Una variable es el nombre de un lugar de almacenamiento en la memoria de la computadora. Durante su ejecución, el programa guarda información en variables, Cuando creas un programa, debes declarar variables indicando al compilador de Java los nombres y los tipos de aquéllas. La instrucción siguiente, por ejemplo, declara una variable llamada cuenta_de_usuario de tipo int: int cuenta_de_usuario; En una aplicación posiblemente nos encontremos con algún valor que permanece constante durante la ejecución. Podemos definirla como una variable común pero perderíamos el control. Por allí, en algún descuido, se cambiaría de valor pero no nos enteraríamos. Podemos agregar a la definición de variable el modificador final. La sintaxis es la siguiente: final tipo_variable nombre de variable [= valor];

Por ejemplo: final int unaConstante = 10;

Si tratamos de modificar el valor de esta constante, el compilador indicará un error. Una vez definido el valor no se puede modificar.

1.4 Objetos que permiten Entrada/Salida por consola Normalmente, cuando se codifica un programa, se hace con la intención de que ese programa pueda interactuar con los usuarios del mismo, es decir, que el usuario pueda pedirle que realice cosas y pueda suministrarle datos con los que se quiere que haga algo. Una vez introducidos los datos y las órdenes, se espera que el programa manipule de alguna forma esos datos para proporcionarnos una respuesta a lo solicitado. Además, en muchas ocasiones interesa que el programa guarde los datos que se le han introducido, de forma que si el programa termina los datos no se pierdan y puedan ser recuperados en una sesión posterior. La forma más normal de hacer esto es mediante la utilización de ficheros que se guardarán en un dispositivo de memoria no volátil (normalmente un disco).

5

A todas estas operaciones, que constituyen un flujo de información del programa con el exterior, se les conoce como Entrada/Salida (E/S). Existen dos tipos de E/S; la E/S estándar que se realiza con el terminal del usuario y la E/S a través de fichero, en la que se trabaja con ficheros de disco. Todas las operaciones de E/S en Java vienen proporcionadas por el paquete estándar de la API de Java denominado java.io que incorpora interfaces, clases y excepciones para acceder a todo tipo de ficheros. En este tutorial sólo se van a dar algunas pinceladas de la potencia de este paquete. Entrada/Salida estándar Aquí sólo trataremos la entrada/salida que se comunica con el usuario a través de la pantalla o de la ventana del terminal. Si creamos una applet no se utilizarán normalmente estas funciones, ya que su resultado se mostrará en la ventana del terminal y no en la ventana de la applet. La ventana de la applet es una ventana gráfica y para poder realizar una entrada o salida a través de ella será necesario utilizar el AWT. El acceso a la entrada y salida estándar es controlado por tres objetos que se crean automáticamente al iniciar la aplicación: System.in, System.out y System.err System.in Este objeto implementa la entrada estándar (normalmente el teclado). Los métodos que nos proporciona para controlar la entrada son: read(): Devuelve el carácter que se ha introducido por el teclado leyéndolo del buffer de entrada y lo elimina del buffer para que en la siguiente lectura sea leído el siguiente carácter. Si no se ha introducido ningún carácter por el teclado devuelve el valor -1. skip(n): Ignora los n caracteres siguientes de la entrada. System.out Este objeto implementa la salida estándar. Los métodos que nos proporciona para controlar la salida son: print(a): Imprime a en la salida, donde a puede ser cualquier tipo básico Java ya que Java hace su conversión automática a cadena. println(a): Es idéntico a print(a) salvo que con println() se imprime un salto de línea al final de la impresión de a. System.err Este objeto implementa la salida en caso de error. Normalmente esta salida es la pantalla o la ventana del terminal como con System.out, pero puede ser interesante redirigirlo, por ejemplo hacia un fichero, para diferenciar claramente ambos tipos de salidas.

6

Las funciones que ofrece este objeto son idénticas a las proporcionadas por System.out. A continuación vemos un ejemplo del uso de estas funciones que acepta texto hasta que se pulsa el retorno de carro e informa del número de caracteres introducidos. import java.io.*; class CuentaCaracteres { public static void main(String args[]) throws IOException { int contador=0; while(System.in.read()!='\n') contador++; System.out.println(); // Retorno de carro "gratuito" System.out.println("Tecleados "+contador+" caracteres."); } }

Entrada/Salida por fichero En Java es posible utilizar dos tipos de ficheros (de texto o binarios) y dos tipos de acceso a los ficheros (secuencial o aleatorio). Los ficheros de texto están compuestos de caracteres legibles, mientras que los binarios pueden almacenar cualquier tipo de datos (int, float, boolean, ...). Una lectura secuencial implica tener que acceder a un elemento antes de acceder al siguiente, es decir, de una manera lineal (sin saltos). Sin embargo los ficheros de acceso aleatorio permiten acceder a sus datos de una forma aleatoria, esto es indicando una determinada posición desde la que leer/escribir. En el paquete java.io existen varias clases de las cuales podemos crear instancias de clases para tratar todo tipo de ficheros. En este tutorial sólo vamos a tratar las tres principales: FileOutputStream: Fichero de salida de texto. Representa ficheros de texto para escritura a los que se accede de forma secuencial. FileInputStream: Fichero de entrada de texto. Representa ficheros de texto de sólo lectura a los que se accede de forma secuencial. RandomAccessFile: Fichero de entrada o salida binario con acceso aleatorio. Es la base para crear los objetos de tipo fichero de acceso aleatorio. Estos ficheros permiten multitud de operaciones; saltar hacia delante y hacia atrás para leer la información que necesitemos en cada momento, e incluso leer o escribir partes del fichero sin necesidad de cerrarlo y volverlo a abrir en un modo distinto. Para tratar con un fichero siempre hay que actuar de la misma manera: 1.-

Se abre el fichero. Para ello hay que crear un objeto de la clase correspondiente al tipo de fichero que vamos a manejar, y el tipo de acceso que vamos a utilizar: TipoDeFichero obj = new TipoDeFichero( ruta );

7

Donde ruta es la ruta de disco en que se encuentra el fichero o un descriptor de fichero válido. Este formato es válido, excepto para los objetos de la clase RandomAccessFile (acceso aleatorio), para los que se ha de instanciar de la siguiente forma: RandomAccessFile obj = new RandomAccessFile( ruta, modo );

2.3.-

4.-

Donde modo es una cadena de texto que indica el modo en que se desea abrir el fichero; "r" para sólo lectura o "rw" para lectura y escritura. Se utiliza el fichero. Para ello cada clase presenta diferentes métodos de acceso para escribir o leer en el fichero. Gestión de excepciones (opcional, pero recomendada). Se puede observar que todos los métodos que utilicen clases de este paquete deben tener en su definición una cláusula throws IOException. Los métodos de estas clases pueden lanzar excepciones de esta clase (o sus hijas) en el transcurso de su ejecución, y dichas excepciones deben de ser capturadas y debidamente gestionadas para evitar problemas. Se cierra el fichero y se destruye el objeto. Para cerrar un fichero lo que hay que hacer es destruir el objeto. Esto se puede realizar de dos formas, dejando que sea el recolector de basura de Java el que lo destruya cuando no lo necesite (no se recomienda) o destruyendo el objeto explícitamente mediante el uso del procedimiento close() del objeto: obj.close()

Clase FileOutputStream Mediante los objetos de esta clase escribimos en ficheros de texto de forma secuencial. Presenta el método write() para la escritura en el fichero. Presenta varios formatos: int write( int c ): Escribe el carácter en el fichero. int write( byte a[] ): Escribe el contenido del vector en el fichero. int write( byte a[], int off, int len ): Escribe len caracteres del vector a en el fichero, comenzando desde la posición off. El siguiente ejemplo crea el fichero de texto "/carta.txt" a partir de un texto que se le introduce por teclado: import java.io.*; class CreaCarta { public static void main(String args[]) throws IOException{ int c; FileOutputStream f=new FileOutputStream("/carta.txt"); while( ( c=System.in.read() ) != -1 ) f.write( (char)c ); f.close(); } }

Clase FileInputStream Mediante los objetos de esta clase leemos de ficheros de texto de forma secuencial.

8

Presenta el método read() para la lectura del fichero. Este método se puede invocar de varias formas. int read(): Devuelve el siguiente carácter del fichero. int read( byte a[] ): Llena el vector a con los caracteres leídos del fichero. Devuelve la longitud del vector que se ha llenado si se realizó con éxito o –1 si no había suficientes caracteres en el fichero para llenar el vector. int read( byte a[], int off, int len ): Lee len caracteres del fichero, insertándolos en el vector a. Todos ellos devuelven -1 si se ha llegado al final del fichero (momento de cerrarle). El siguiente ejemplo muestra el fichero de texto "/carta.txt" en pantalla: import java.io.*; class MuestraCarta { public static void main(String args[]) throws IOException { int c; FileInputStream f=new FileInputStream("/carta.txt"); while( ( c=f.read() ) != -1 ) System.out.print( (char)c ); f.close(); } }

Clase RandomAccessFile Mediante los objetos de esta clase utilizamos ficheros binarios mediante un acceso aleatorio, tanto para lectura como para escritura. En estos ficheros hay un índice que nos dice en qué posición del fichero nos encontramos, y con el que se puede trabajar para posicionarse en el fichero. Métodos de desplazamiento Cuenta con una serie de funciones para realizar el desplazamiento del puntero del fichero. Hay que tener en cuenta que cualquier lectura o escritura de datos se realizará a partir de la posición actual del puntero del fichero. long getFilePointer();Devuelve la posición actual del puntero del fichero. void seek( long l ); Coloca el puntero del fichero en la posición indicada por l. Un fichero siempre empieza en la posición 0. int skipBytes( int n ); Intenta saltar n bytes desde la posición actual. long length(); Devuelve la longitud del fichero. void setLength( long l); Establece a l el tamaño de este fichero. FileDescriptor getFD(); Devuelve el descriptor de este fichero. Métodos de escritura La escritura del fichero se realiza con una función que depende el tipo de datos que se desee escribir. void write( byte b[], int ini, int len ); Escribe len caracteres del vector b.

9

void write( int i ); Escribe la parte baja de i (un byte) en el flujo. void writeBoolean( boolean b ); Escribe el boolean b como un byte. void writeByte( int i ); Escribe i como un byte. void writeBytes( String s ); Escribe la cadena s tratada como bytes, no caracteres. void writeChar( int i ); Escribe i como 1 byte. void writeChars( String s ); Escribe la cadena s. void writeDouble( double d ); Convierte d a long y le escribe como 8 bytes. void writeFloat( float f ); Convierte f a entero y le escribe como 4 bytes. void writeInt( int i ); Escribe i como 4 bytes. void writeLong( long v ); Escribe v como 8 bytes. void writeShort( int i ); Escribe i como 2 bytes. void writeUTF( String s ); Escribe la cadena s utilizando la codificación UTF-8. Los métodos que escriben números de más de un byte escriben el primero su parte alta. Métodos de lectura La lectura del fichero se realiza con una función que depende del tipo de datos que queremos leer. boolean readBoolean(); Lee un byte y devuelve false si vale 0 o true sino. byte readByte(); Lee y devuelve un byte. char readChar(); Lee y devuelve un caracter. double readDouble(); Lee 8 bytes, y devuelve un double. float readFloat(); Lee 4 bytes, y devuelve un float. void readFully( byte b[] ); Lee bytes del fichero y los almacena en un vector b. void readFully( byte b[], int ini, int len ); Lee len bytes del fichero y los almacena en un vector b. int readInt(); Lee 4 bytes, y devuelve un int. long readLong(); Lee 8 bytes, y devuelve un long. short readShort(); Lee 2 bytes, y devuelve un short. int readUnsignedByte(); Lee 1 byte, y devuelve un valor de 0 a 255. int readUnsignedShort(); Lee 2 bytes, y devuelve un valor de 0 a 65535. String readUTF(); Lee una cadena codificada con el formato UTF-8. int skipBytes(int n); Salta n bytes del fichero. Si no es posible la lectura devuelven –1. Vamos a crear un pequeño programa que cree y acceda a un fichero binario, mediante acceso aleatorio. El siguiente ejemplo crear un fichero binario que contiene los 100 primeros números (en orden): // Crea un fichero binario con los 100 primeros numeros static void creaFichBin( String ruta ) throws IOException { RandomAccessFile f=new RandomAccessFile(ruta,"rw"); // E/S for ( int i=1; i Elemento 10 mas alla System.out.print(" 10 elementos más allá, esta el "); System.out.println( f.readByte() ); f.close(); }

Si incluimos ambos métodos en una clase, y les llamamos con el siguiente programa principal (main()): public static void main(String args[]) throws IOException { String ruta="numeros.dat"; // Fichero creaFichBin( ruta ); // Se crea imprimeEltoN( ruta, 14 ); // Accedo al elemento 14. }

Obtendremos la siguiente salida: El fichero numeros.dat ocupa 100 bytes. En la posición 13 está el numero 14 10 elementos más allá, esta el 24

1.5 Operadores Un operador realiza una función, toma uno o más argumentos y devuelve un resultado. Cuando vimos las variables, definimos un tipo de dato con un conjunto de operaciones asociadas. Es de esperar que podamos realizar operaciones aritméticas con números y lógicas con los booleanos. Estas operaciones se representan mediante operadores. Los operadores, al igual que los métodos, se pueden sobrecargar, es decir se puede redefinir su funcionalidad dependiendo de los tipos de datos de los operandos que reciba. Así, podemos indicar que el operador (+) realice una suma aritmética si los operandos que recibe son dos enteros y una concatenación si recibe una cadena y otro objeto.

11

Operadores aritméticos Realizan las operaciones aritméticas básicas: suma (+), resta (-), multiplicación (*), división (/) y módulo o residuo (%) para datos de tipo numérico, tanto enteros como reales. Estas son operaciones binarias porque admiten dos operandos. Ejemplo de utilización de estos operadores: public class Aritmetica { public static void { int i int j int suma int resta int mult int div int modulo

main(String[] args) = 12; = 10; = i + j; = i - j; = i * j; = i / j; = i % j;

System.out.print("Suma :"); System.out.println(suma ); System.out.print("Resta :"); System.out.println(resta); System.out.print("Multiplicacion :"); System.out.println(mult); System.out.print("Division :"); System.out.println(div); System.out.print("Modulo :"); System.out.println(modulo); } }

Operadores aritméticos unarios Dentro de los operadores aritméticos tenemos los unarios + y – que simplemente operan con el signo de un valor dado. Por ejemplo: int h = -1; int m = +h; // es equivalente a m = h * (+1) int n = -h; // es equivalente a n = h * (-1)

El operador – se encarga de cambiar el signo, y el + sencillamente deja el signo como está. Dentro del catálogo de operadores tenemos algunos unarios más. Se trata del auto incremental ++ y del auto decremental --. Respectivamente, suma y resta una unidad al valor. Ambos operadores pueden ser sufijos, es decir se coloca antes del operando o posfijo que se sitúa detrás. Veamos algunos ejemplos: int i = 1; i++; ++i;

12

i--; --i;

En este ejemplo estamos incrementado y decrementando el valor de i. Al principio parece que los operadores funcionan igual, tanto como posfijo como sufijo, pero su comportamiento es diferente. Observemos lo que ocurre en este ejemplo: public class Diferentes{ public static void main(String[] args){ int i = 2; int j = 2; System.out.println(i++); System.out.println(++j); System.out.print("Estado Final (i) :"); System.out.println(i); System.out.print("Estado Final (j) :"); System.out.println(j); } }

Partiendo del mismo valor, vemos que j se incrementó, mientras que la variable i se mostró sin cambios. Si colocamos el operador como sufijo, primero se evalúa la variable y luego se realiza la operación. En el caso de la variable i, antes de incrementar su valor se mostró por pantalla. Para la variable j el procedimiento fue inverso. Antes de mostrar su valor se incrementó. Operadores relacionales Revisando algunas definiciones matemáticas, nos enteramos que los números conforman un conjunto ordenado. Cada uno tiene una posición relativa. Sabemos que el 2 "es menor que" el 4 y que el 6 "es más grande que" el 1. Al comparar dos números, realizamos una función de relación. En java disponemos de los operadores relacionales para verificar si se cumple una relación. Por ejemplo el operador de equivalencia ( == ) nos devuelve un valor de verdadero si los operandos son iguales. Estas operaciones comparan dos valores numéricos y retorna con un valor booleano.

Operador > >= < B A >= B A " + i + " = " + b1); < " + b + " = " + b2); b;

Entre los booleanos solo se permiten los operadores de equivalencia, es igual (==) o es distinto (!= ). boolean x = true; boolean y = x == x; boolean z = x != y;

Operadores booleanos Como deben suponer, trabajan con operandos booleanos. Realizan las operaciones lógicas de conjunción (AND), disyunción (OR), negación ( NOT ) y la disyunción exclusiva ( XOR ).

Nombre AND

Operador &&

OR

||

NOT AND

! &

OR

|

XOR

^

Operadores Booleanos Utilización Resultado A && B Verdadero cuando A y B son verdaderos. Evaluación condicional. A || B Verdadero cuando A o B son verdaderos. Evaluación condicional. !A Verdadero si A es falso. A&B Verdadero cuando A y B son verdaderos. Siempre evalúa ambos operandos. A|B Verdadero cuando A o B son verdaderos. Siempre evalúa ambos operandos. A^B Verdadero cuando A y B son diferentes.

14

Cada una de estas operaciones tiene asociada una tabla de verdad. Esto nos permite ver el resultado de un operador aplicado a las distintas combinaciones de valores que pueden tener los operandos. A continuación mostraremos cómo se comporta el operador AND mediante su tabla de verdad. public class TablaAnd { public static void main(String args[]){ boolean x = true; boolean y = false; boolean a1 = x && x; boolean a2 = x && y; boolean a3 = y && x; boolean a4 = y && y; System.out.println("Tabla de verdad System.out.println( x + " AND " + x System.out.println( x + " AND " + y System.out.println( y + " AND " + x System.out.println( y + " AND " + y } }

de la + " = + " = + " = + " =

conjunción"); " + a1 ); " + a2 ); " + a3 ); " + a4 );

Si probamos quitando un ampersand ( & ) del operador, vemos que obtenemos los mismos resultados. Existen dos operadores AND, uno con dos símbolos & y el otro con uno solo. También tenemos dos operadores OR. boolean x1 = operando1 && operando2; boolean x2 = operando1 & operando2; boolean y1 = operando1 || operando2; boolean y2 = operando1 | operando2;

Parece extraño, sobre todo porque tienen la misma tabla de verdad. Pero internamente tienen un comportamiento diferente. Cuando estamos en presencia de un operador con un solo símbolo, siempre se evalúan ambos operandos. En cambio para el operador con el símbolo repetido, su evaluación cambia según el valor del primer operando. Por ejemplo tenemos la siguiente operación. boolean x = true && false;

El resultado es falso, pero el intérprete tiene que mirar el segundo operando para saberlo. boolean x = false && true;

Aquí ni se molesta en mirar el último operando, porque la operación AND es verdadera solamente cuando ambos operandos son verdaderos. En el caso del operador OR, se evalúa el segundo operador si el primero es falso. Cuando es verdadero, no tiene en cuenta el otro operando. El resultado es verdadero sin importar el valor del segundo.

15

Veamos un caso extremo para mostrar cómo funciona la evaluación condicional. Tenemos el siguiente programa en donde pretendemos hacer saltar al intérprete con un error. public class Condicional { public static void main(String args[]){ int x = 0; int y = 2; boolean b = ( x != 0 ) && ( ( y / x ) != 0 ); System.out.println(b); } }

Sin ningún tipo de emoción, aburridamente el intérprete nos avisa que el resultado es "false". Ahora verá. Quitemos un símbolo & y quedémonos con uno solo. El resultado es otro: java.lang.ArithmeticException: / by zero

La primera vez verificó que x!=0 era falso, entonces dio por terminada la operación con el resultado falso. Cuando cambiamos de operador, evaluó los dos operandos y cayó en nuestra trampa. Tuvo que calcular cuánto es y / x dando luego un error de división por cero. Los operadores booleanos son muy amigos de los relacionales. Se llevan bien porque los últimos dan resultados booleanos. Entre ambos tipos de operadores se pueden construir instrucciones más complejas. Por ejemplo, queremos saber si un número está dentro de un rango. Solo tenemos que compararlo con los extremos: int y = 4; boolean x = ( y > 3 ) && ( y < 6 );

Ahora deseamos saber si una variable tiene el valor "a" no importando si es mayúscula o minúscula. char c = 'b'; boolean x = ( c == 'a' ) || ( c == 'A' );

No olviden que el operador de equivalencia (==) tiene dos símbolos igual (=), si colocan uno solo les dará un error de asignación. Operadores de bits Como sabrán, los datos en una computadora internamente se representan en código binario. El microprocesador solo entiende de ceros y unos. Luego mediante una serie de procesos nosotros vemos a este código ya transformado en números, caracteres, imágenes y sonido. Pero en realidad en la trastienda todo sigue siendo binario. El método más sencillo de representación son los números naturales. Por ejemplo, si tengo el número 85 en decimal, solo tengo que llevarlo a binario y obtengo una serie de unos y ceros:

16

1010101 = 85 en binario

Cada dígito (un cero o un uno) de este número se llama bit. Java tiene una serie de operadores capaces de manipular estos dígitos, son los operadores de bits. Operadores de Bits Operador >

Utilización A > B

>>>

A >>> B

& | ^ ~

A&B A|B A^B ~A

Resultado Desplazamiento de A a la izquierda en B posiciones. Desplazamiento de A a la derecha en B posiciones, tiene en cuenta el signo. Desplazamiento de A a la derecha en B posiciones, no tiene en cuenta el signo. Operación AND a nivel de bits. Operación OR a nivel de bits. Operación XOR a nivel de bits. Complemento de A a nivel de bits.

Para operar a nivel de bit es necesario tomar toda la longitud predefinida para el tipo de dato. Estamos acostumbrados a desechar los ceros a la izquierda en nuestra representación de números. Pero aquí es importante. Si trabajamos una variable de tipo short con un valor de 3, está representada de la siguiente manera: 0000000000000011

Aquí los 16 bits de un short se tienen en cuenta. Operadores de Asignación Prácticamente lo hemos utilizado en todos los ejemplos de variables y operadores. Es el operador de asignación. Este aparece con un signo igual (=). Cambia el valor de la variable que está a la izquierda por un literal o el resultado de la expresión que se encuentra a la derecha. par = 2; perímetro

= Pi * diámetro;

En el ejemplo vemos la variable par toma el valor de 2 y perímetro el resultado de una expresión. Veamos un ejemplo de una instrucción tonta, que en realidad no hace nada. algo = algo;

La variable algo toma el valor de algo; todo queda como antes. Ahora aumentemos el valor de la variable en 3 unidades. algo = algo + 3;

Aquí la variable toma el valor que tenía más 3 unidades. Existe una forma de simplificar la notación anterior. Es la siguiente:

17

algo += 3; // equivalente a

algo = algo + 3

Se juntaron el operador de suma con el de asignación. Este atajo se puede realizar para otras operaciones además de la suma. Es útil cuando la operación contiene como primer operando al valor de la misma variable en la que se almacena el resultado.

Operación

Operadores de Asignación Operador

Suma Resta Multiplicación División Residuo Desplazamiento a la izquierda Desplazamiento a la derecha Desplazamiento a la derecha sin signo AND de bits OR de bits XOR de bits

+= -= *= /= %= = >>>= &= |= ^=

Utilización A += B A -= B A *= B A /= B A %= B A = B A >>>= B A &= B A |= B A ^= B

Operación Equivalente A=A+B A=A–B A=A*B A=A/B A=A%B A = A > B A = A >>> B A=A&B A=A|B A = A ^B

Precedencia de operadores Ya que conocimos a los operadores, vamos tratar de colocarlos todos juntos en una sola expresión. Vamos a ver como se organizan para trabajar: int j = 10; boolean esCierto =

j/j*j>j-j+j&&j>j==j%j||++j >>> < > = == != & ^ | &&

18

OR booleano Condicional Operadores de asignación

|| ?: = += -= *= /= %= &= ^= |= = >>>=

En la tabla se muestra el nivel de precedencia de los operadores. Los de mayor nivel se encuentran arriba. Podemos ver que los últimos son los de asignación. Esto es lógico, ya que se debe calcular primeramente la expresión antes de asignar un resultado a una variable. Veamos unos ejemplos de cómo actúa esta regla. int j = 1 + 3 * 4; // resultado j = 13

Desde que aprendimos aritmética básica, conocemos la regla que nos obliga a calcular la multiplicación antes de una suma. Esto también se cumple en Java. int j = 1 + 3 – 4;

resultado j= 0;

Si todos los operadores tienen un nivel idéntico de precedencia se evalúa la expresión de izquierda a derecha. Utilización de paréntesis Se utilizan para aislar una porción de la expresión de forma que el cálculo se ejecute de forma independiente. Puede forzar a una expresión a ignorar las reglas de precedencia. int j = 1 + 3 * 4; // resultado j = 13 int h = (1 + 3) * 4 // resultado j = 16

Tomando el primer ejemplo, forzamos al compilador a realizar la suma antes que la multiplicación. En este ejemplo es imprescindible la utilización de paréntesis: int k = 1 + (h = 3);

Si quitamos los paréntesis el compilador protestará. Porque al establecer un nivel muy bajo para la asignación, procede primero la suma. Pero estamos sumando con una variable sin valor. Como en matemáticas, podemos anidar los paréntesis. Se comenzara a evaluar los internos hasta llegar a los externos. Cabe agregar que los paréntesis no disminuyen el rendimiento de los programas. Por lo tanto, agregar paréntesis no afecta negativamente al programa. int k = ((12 - 2) * ( 21 - 11)) / ((1+1)*(15-10)) + 1 ;

19

1.6 Tipos de datos El trabajo con datos es parte fundamental de cualquier programa, las variables y sus tipos se encargan de almacenar esa información y la memoria que es necesaria para gestionarlas. La manera más habitual de declarar una variable siempre contiene dos elementos, el tipo de la variable y su nombre y terminando la declaración con punto y coma. Pero también se puede declarar en una misma instrucción más de una variable del mismo tipo siempre separadas por una coma, al igual que se puede inicializar una variable en el momento de declararla. Inicializar una variable consiste en almacenar un determinado valor en el espacio de memoria reservado para ella. int midato; tipoVariable nombre; int midato1 = 3, midato2 = 6,midato3 = 5;

Las variables en Java deben tener un tipo de dato asociado. El tipo de dato de esa variable indicará los valores que la variable puede contener y las operaciones que se pueden realizar con ella. Podemos diferenciar los datos en Java en dos categorías de datos principales: los tipos primitivos y los tipos referenciados. Los tipos primitivos contienen un sólo valor e incluyen los tipos como los enteros, coma flotante, los caracteres, de tipo booleano etc. Los tipos referenciados se llaman así porque el valor de una variable de referencia es una referencia (un puntero) hacia el valor real. En Java tenemos los arreglos, las clases y las interfaces como tipos de datos referenciados. 1.6.1 Fundamentales Enteros Estos tipos son byte, short, int y long, que guardan el signo valor, estos representan un número y no pueden representar elementos fraccionarios. Números en coma flotante Estos son float y double y pueden almacenar números en coma flotante y con signo, esto quiere decir que nos permiten representar números decimales. Todos los literales de coma flotante son del tipo double salvo que se especifique lo contrario, por eso si se intenta asignar un literal en coma flotante a una variable de tipo float el compilador nos dará un error (tipos incompatibles). Carácter Estos son de tipo char, que almacena la representación de los caracteres (letras o números), un carácter está almacenado en 16 bits, y siguen un estándar que es el Unicode.

20

Los caracteres en Java se pueden especificar de forma normal o con secuencias de escape, utilizando la barra invertida "\" seguida de una letra (\r) o utilizando la barra invertida con una "u" seguida de un numero hexadecimal (\u0000d), en este caso hemos especificado la secuencia de escape \r y su código Unicode correspondiente del retorno de carro. Booleano Este solo guarda dos valores: verdadero (true) o falso (false), y no como ocurre en otros lenguajes que toman los valores 0 y 1. Generalmente su utilización es muy frecuente para determinar el flujo de los programas. Cadenas En Java se tratan como una clase especial llamada String. Las cadenas se gestionan internamente por medio de una instancia de la clase String. Una instancia de la clase String es un objeto que ha sido creado siguiendo la descripción de la clase. Tabla comparativa de tipos de datos simples en Java Tipo byte short int long

Tamaño (en bits) 8 16 32 64

Rango

Descripción

float

32

-128 a 127 -32,768 a 32,767 -2,147,483,648 a 2,147,483,647 -9,223,372,036,854,775,808L a 9,223,372,036,854,775,807L +/- 3.4E+38F (6-7 dígitos importantes)

double

64

+/- 1.8E+308 (15 dígitos importantes)

char boolean

16 1

Conjunto de caracteres Unicode ISO Verdadero o falso

Entero de 1 byte. Entero corto. Entero. Entero largo. Coma flotante de precisión simple. Coma flotante de precisión doble. Un sólo carácter. Un valor booleano.

1.6.2 Definidos por el usuario

1.7 Palabras reservadas Palabras clave de Java que no pueden usarse en nombres de variable: abstract boolean break byte case

catch char class continue default

21

do double else extends final finally float for if implements import instanceof int interface long native new package prívate protected

public return short static strictfp super switch synchronized this throw throws transient try void volatile while wesureal true (literal booleano) false (literal booleano) null (literal nulo)

1.8 Expresiones 1.9 Estructuras de control Hasta el momento, el intérprete se dio un paseo por los ejemplos que hemos visto sin alterar el curso de ejecución. Una por una ejecutaba las sentencias sin detenerse, ni repetir y sin saltearse ninguna, salvo si se producía un error. Esto produce programas con poca utilidad y totalmente aburridos. public class Secuencia { public static void main(String args[]){ System.out.println("Primero se ejecuta esto"); System.out.println("Luego esto"); System.out.println("Por último esto"); } }

En el ejemplo se muestra una ejecución en secuencia. Existen ciertas sentencias que permiten modificar el curso de ejecución. Debido a ciertas condiciones que se dan en el programa podemos decidir que instrucciones se ejecutarán y qué cantidad de veces. Para lograr esto disponemos de un conjunto de estructuras de control.

22

1.9.1 Asignación

1.9.2 Selección A menudo nos encontremos con programas o algoritmos en donde las acciones se ejecutarán de acuerdo a una condición. Tenemos dos o más alternativas de acción y solo una de estas tendrá lugar. Por ejemplo, supongamos que estamos conduciendo un vehículo y en un cruce de calles nos encontramos con un semáforo. Si la luz del semáforo es roja nos detenemos. De otro modo seguiremos manteniendo la velocidad. Aquí se nos presentan dos alternativas: continuar con la marcha o frenar. Estas acciones se realizarán según una condición: si la luz del semáforo es roja. Podemos especificar este ejemplo mediante un pseudolenguaje que describa la condición. si luz_del_semáforo = rojo entonces velocidad_del_vehículo = 0

Estamos frente a una selección simple: tenemos dos alternativas. Este tipo de selección la condición toma un valor lógico de verdadero o falso. Se tienen que especificar las acciones que deben ocurrir si la condición es verdadera. Opcionalmente se puede indicar que debe ocurrir si la condición es falsa. Para lo último veamos un ejemplo. si luz = rojo entonces frenar de otro modo seguir con la misma velocidad.

Imaginemos que estamos en un día de mucho calor y deseamos tomar un refresco. Para ello nos dirigimos a una máquina expendedora de gaseosas, insertamos una moneda y elegimos el tipo de bebida. Generalmente una máquina expendedora tiene un panel con botones para realizar la elección. Internamente dispone de un programa que suelta una lata de acuerdo al botón pulsado. El algoritmo puede ser similar a esto: Selección de acuerdo al botón presionado: En En En En En

el el el el el

caso caso caso caso caso

de de de de de

"1" "2" "3" "4" "5"

expulsar expulsar expulsar expulsar expulsar

lata lata lata lata lata

de de de de de

Orange Duke. Lemmon Duke. Cola Duke. Coffee Duke. Cherry Duke.

Tenemos varias alternativas y la acción realizada dependerá de un valor discreto. Esta es una selección múltiple.

23

Sentencia if-else Queremos realizar una división de enteros. Es fácil, ya sabemos cómo hacerlo, utilizando variables y operadores. Pero nos queda un mal recuerdo con la división por cero. Podemos establecer una condición que permita la división para todos los números y que rechace cualquier divisor con cara de cero. Disponemos de dos opciones: realizar o no la división. También una condición: el divisor sea distinto de cero. Esto se parece mucho a una selección simple. La estructura de selección simple en Java se realiza mediante la sentencia if (si, en ingles). La sintaxis es la siguiente: if (condición) sentencia;

La condición es una expresión booleana. La sentencia se ejecuta solamente si la expresión booleana es verdadera. Retomando el problema de la división, incorporamos una estructura de selección para realizar la operación libre de ceros. public class DivisionSegura { public static void main(String args[]){ int x = 12; int y = 0; int z = 0; if( y !=0 ) z = x / y; System.out.println("El resultado es : " + z); } }

En el programa, la variable x tiene el valor del dividendo, la y el divisor y la z el cociente. La condición es una expresión que arroja un valor booleano. En este caso se utiliza un operador relacional que verifica si y es distinto de 0. Si esta condición se cumple realiza la división. En caso contrario se saltea la división y solo imprime el valor de z, que hemos inicializado convenientemente antes de la operación. ¿Qué ocurre si la condición no se cumple? En este caso nada. Podemos agregar una serie de instrucciones que se ejecutarán solo si la condición no se cumple. Para esto tendremos que agregar la sentencia else. La estructura de selección quedará así: if (condición) sentencia 1; else sentencia 2;

Si la condición es verdadera se ejecuta la sentencia 1 en caso contrario se ejecuta la sentencia 2. Ambas sentencias nunca se ejecutarán al mismo tiempo, son excluyentes. Ahora ampliemos el programa para mostrar una advertencia en el caso que se encuentre cara a cara con un cero siniestro. public class DivisionSegura {

24

public static void main(String args[]){ int x = 12; int y = 0; int z = 0; if( y !=0 ) z = x / y; else System.out.println("Atención! se pretende dividir por 0"); System.out.println("El resultado es : " + z); } }

El programa nos quedó más completo. Con la cláusula else incluimos otra alternativa de acción. Pero algo anda suelto. Este programa siempre muestra un resultado, se cumpla o no la condición. El mensaje por pantalla no está incluido en la estructura de selección. Tendremos que colocarlo dentro del sector de sentencias que se ejecutarán cuando la condición sea verdadera. Para agrupar las sentencias se utilizan las llaves ( { } ) Indicarán el inicio y el fin de un bloque de sentencias. Probemos como queda con un bloque: public class DivisionSegura { public static void main(String args[]){ int x = 12; int y = 2; int z = 0; if( y !=0 ) { z = x / y; System.out.println("El resultado es : " + z); } else { System.out.println("Atención! se pretende dividir por 0"); } } }

Las buenas prácticas en defensa de un código mantenible han dictaminado que utilicemos las llaves en todo momento, aún en el caso que utilicemos una sola sentencia. if (condición) { sentencia; } else { sentencia; }

Lamentablemente no siempre nos encontraremos con condiciones tan sencillas. Muchas veces tendremos que recurrir a proposiciones compuestas para formar una condición. Para ello podemos recurrir a operadores relacionales o lógicos. Recuerden que siempre debe dar como resultado un valor booleano. Supongamos que deseamos saber si un año es bisiesto. Sabemos que debe ser múltiplo de 4. Para esto tendremos que verificar si el módulo es igual a cero. año % 4 == 0

25

Pero no debe ser múltiplo de 100. ( año % 4 == 0 ) && ( año % 100 != 0 )

A menos que sea múltiplo de 400. ((( año % 4 == 0 ) && ( año % 100 != 0 )) || ( año % 400 == 0 ))

Formamos una proposición compuesta con conectores lógicos. Ahora vamos a incorporarlo en una estructura se selección. if ((( año % 4 == 0 ) && ( año % 100 != 0 )) || ( año % 400 == 0 )) { System.out.println("Es bisiesto"); } else { System.out.println("No es bisiesto"); }

Los conectores lógicos nos permiten simplificar la estructura. Sin ellos nos veríamos en la necesidad de anidar las sentencias. Veamos que ocurre si en el ejemplo anterior descartamos el AND y el OR. if ( x % 4 == 0 ) { if ( x % 100 == 0 ) { if ( x % 400 == 0 ) { System.out.println("Es bisiesto"); } else { System.out.println("No es bisiesto"); } } else { System.out.println("Es bisiesto"); } } else { System.out.println("No es bisiesto"); }

Parece complicado, pero nos demuestra muchas cosas. En primer lugar observamos que se pueden anidar las sentencias if-else. Cada resultado de una condición puede caer en una nueva comprobación para formar una estructura compleja de selección. También vemos que hay cierta relación entre conectores lógicos y la estructura.

Conjunción if (condición1 && condición2){ sentecia1; } else { sentencia2; }

if ( condición1 ) { if ( condición2 ) { sentencia1; } else { sentencia2; } } else { sentencia2; }

26

Disyunción if ( condición1 || condición2 ) { sentencia1; } else { sentencia2; }

if ( condición1 ){ sentencia1; } else { if ( condición2 ) { sentencia1; } else { sentencia2; } }

Negación if ( ! condición1) { sentencia1; } else { sentencia2; }

if ( condición1) { sentencia2; } else { sentencia1; }

Sentencia switch Vamos a desarrollar una calculadora totalmente elemental. Sin muchas características salvo de realizar operaciones aritméticas con dos operandos. Disponemos de una variable de tipo char que nos indicará que tipo de operación se debe efectuar. Realizamos la operación y mostramos el resultado en la pantalla. Después de luchar con las sentencias if-else nos quedó algo parecido a esto: public class MiniCalculadora { public static void main(String args[]){ int a = 1; int b = 1; char op = '/'; System.out.print("El resultado es : "); if ( op == '+' ) { System.out.println( a + b); } else if ( op == '-') { System.out.println( a - b); } else if ( op == '*') { System.out.println( a * b); } else if ( op == '/') { System.out.println( a / b); } } }

Ya nos alejamos bastante de las decisiones simples. Aquí tenemos de una cadena de sentencias if-else que realizan una selección múltiple. La condición general tiene más dos alternativas. Tendremos que acudir a la sentencia switch que se encarga de este tipo de selección. public class MiniCalculadora{ public static void main(String args[]){ int a = 1; int b = 1; char op = '/'; System.out.print("El resultado es : ");

27

switch ( op ) { case '+': System.out.println( a + b ); break; case '-': System.out.println( a - b ); break; case '*': System.out.println( a * b ); break; case '/': System.out.println( a / b ); break; default: System.out.println("error" ); break; } } }

La sentencia switch se encarga de estructurar una selección múltiple. Al contrario del enunciado if-else que sólo podemos indicar dos alternativas, maneja un número finito de posibilidades. La estructura general del enunciado switch es la siguiente: switch( expresión ) { case constante1: sentencia1; ... break; ... case constanteN: sentenciaN; ... break; default: sentencia; ... break }

El valor de la expresión y de las constantes tiene que ser de tipo char, byte, short o int. No hay lugar para booleanos, reales ni long porque, en la ejecución, todos los valores que incorporamos se transforman en valores de tipo int. Al evaluar la expresión de switch, el intérprete busca una constante con el mismo valor. Si la encuentra, ejecuta las sentencias asociadas a esta constante hasta que tropiece con un break. La sentencia break finaliza la ejecución de esta estructura. Si no encuentra ninguna constante que coincida con la expresión, busca la línea default. Si existe, ejecuta las sentencias que le siguen. La sentencia default es opcional. Volviendo a la mini calculadora, vemos como se organiza las distintas alternativas de acuerdo al valor de una constante char. Estas alternativas representan las distintas operaciones que están disponibles y solo se ejecutará una sola. Por ejemplo, si el valor del operador (en el

28

programa figura op) es igual al signo de la suma, la sentencia switch ejecutará solamente la línea que corresponde con esta operación. ¿Qué ocurre si cambiamos la variable op por algún carácter distinto a los especificados? Entra en juego la alternativa default y todas las sentencias que le siguen. En este caso imprime por pantalla el mensaje "error". Si nos olvidamos de incorporar esta alternativa, no pasa nada. Ninguna sentencia dentro de la estructura switch se ejecutará. Ya que hablamos de default, es conveniente mencionar que no es necesario que quede relegado al final de la estructura. Podemos situarla al comienzo, en el medio, en definitiva, en donde nos quede más útil según la lógica que apliquemos o donde queramos. switch ( op ) { default : System.out.println("error"); break; case '+': System.out.println( a + b ); break; ...

En el ejemplo presentado, funciona de la misma manera un default al principio. Obviamente no debe existir más de una alternativa default. Las sentencias break son opcionales. Se utilizan con el propósito de separar las alternativas. Pero fieles a nuestro estilo de meternos en problemas decidimos que algunos break deben desaparecer. Probemos que ocurre con este código: switch ( op ) { case '+': System.out.println( a + b ); case '-': System.out.println( a - b ); break; ...

Es el mismo que el original, solo "olvidamos" de agregarle un break al final de la alternativa suma. Si cambiamos el valor de op por el carácter '+' y ejecutamos el programa, nos responde de esta manera: El resultado es : 2 0

Nos dio los resultados de la suma y la resta. Al no tener un break en la suma, se pasó de largo y ejecuto la de abajo, que justamente era la resta. En algunas circunstancias, el break está de más. Es posible construir una estructura en donde se ejecuten más de una alternativa al mismo tiempo. Vemos un ejemplo: public class Lineas{

29

public static void main(String args[]){ int j = 2; switch (j) { case 5: System.out.println("********"); case 4: System.out.println("********"); case 3: System.out.println("********"); case 2: System.out.println("********"); case 1: System.out.println("********"); } } }

El programa tiene por objetivo listar un número dado de líneas. Se pueden dibujar hasta 5 líneas trazadas con el símbolo *. La cantidad dependerá del valor de la variable j. Por ejemplo, si j vale 2, activa la alternativa que tiene esta constante y como no tiene un break que la obstaculice sigue con la alternativa de abajo. Operador if-else Salvo ciertos detalles, el programador que conozca el lenguaje C, no notará la diferencia con este "dialecto". Sobre todo a esta altura del curso, que sólo sometemos a Java a una programación de tipo imperativa. Entre todas las construcciones sintácticas que Java heredó del C, existe un curioso operador ternario. Se lo llama operador if-else. Su función es abreviar la notación de la cláusula de selección simple. La sintaxis es la siguiente. variable = op1?op2:op3

El operando op1 debe ser de tipo booleano. Los operandos op2 y op3 pueden ser de cualquier tipo, pero compatibles entre sí y con la variable que almacene el resultado. Funciona como una selección simple. De acuerdo al valor lógico del operando op1, si es verdadero el resultado de la operación es igual al valor del operando op2. De lo contrario, si op1 es falso, el resultado es el valor del operando op3. Probemos este nuevo operando. Supongamos que debemos realizar un programa que calcule la distancia entre dos números. Para lograrlo, tomamos el mayor y le restamos el menor. Para empezar, veremos cómo se realiza con la cláusula if-else, que ya conocemos. public class Distancia{ public static void main(String args[]){ int a = 23; int b = 12; int j; if ( a > b ) { j = a - b; } else { j = b - a; }

30

System.out.println("El resultado es: " + j ); } }

La construcción equivalente, utilizando el operador, es la siguiente: public class Distancia2{ public static void main(String args[]){ int a = 23; int b = 12; int j = a > b ? a - b : b - a; System.out.println("El resultado es: " + j ); } }

Como verán, redujo bastante la notación. La expresión booleana, que conforma la condición de selección, pasó al primer operando, la operación verdadera al segundo y la falsa al tercero. Como cualquier operador silvestre, se puede combinar tranquilamente con el resto para formar expresiones compuestas. Para verlo, mostramos una expresión equivalente a la anterior. int j = (a > b ? a : b ) - ( a 0 ) System.out.println("Esto nunca lo verás");

Dentro del conjunto de sentencia que controla, debe existir alguna que cambie el valor de la condición que se está evaluando. boolean prueba = true; while ( prueba ) { System.out.println("Esto lo verás una vez"); prueba = false; }

Entraríamos en un ciclo infinito si nunca se modifica la condición y permanece verdadera. boolean prueba = true; while ( prueba ) { System.out.println("Esto lo verás muchas veces"); }

Generalmente esta estructura se utiliza en situaciones en donde desconocemos la cantidad de ciclos que se deben ejecutar para producir un resultado. Mostraremos como se utiliza en estas circunstancias con el ejemplo de pase a binario, mostrado en el capítulo anterior. Teníamos que transformar un número decimal a binario. El programa en java nos queda de esta manera:

33

public class Dec2Bin{ public static void main(String args[]){ int decimal = 252222; String binario = ""; while ( decimal > 0 ) { binario = decimal % 2 + binario; decimal /= 2; } System.out.println(binario); } }

Como no sabemos de antemano cuantas vueltas debe dar, simplemente esperamos que el resultado de las divisiones sucesivas sea igual a cero. También se pueden realizar ciclos con while en donde ya conocemos, antes de entrar en la estructura, cuantas vueltas debe dar para terminar. Para esto nos auxiliamos con un contador de vueltas. Previamente tiene que inicializarse antes de ingresar al ciclo. Luego en cada vuelta se modificara según la lógica del algoritmo. Realicemos el programa que despliegue por pantalla cinco líneas de caracteres. public class Cuadrado{ public static void main(String args[]){ int contador = 1; while ( contador 0 ); System.out.println(dígitos); } }

Sentencia for Trabajamos con casos de interacción en donde a priori no conocíamos la cantidad de ciclos que se ejecutaban hasta cumplir con una condición. Para esto utilizamos la sentencia while. Pero ahora estudiaremos con más detalle aquellos casos en donde se sabe de antemano cuantos ciclos se deben cumplir para terminar la ejecución. Imprimiremos una tabla de multiplicar hasta el factor noveno. Si no utilizamos ninguna estructura de interacción, deberíamos imprimir nueve líneas de código secuencial. System.out.println("3 System.out.println("3 System.out.println("3 System.out.println("3 System.out.println("3 System.out.println("3 System.out.println("3 System.out.println("3 System.out.println("3

x x x x x x x x x

1 2 3 4 5 6 7 8 9

= = = = = = = = =

3"); 6"); 9"); 12"); 15"); 18"); 21"); 24"); 27");

Pero ya conocemos las estructuras que nos ahorran el esfuerzo de escribir tanto código. Utilizaremos una sentencia que ya conocemos: el while. int factor = 1; while ( factor