Vous êtes sur la page 1sur 39

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 );
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.
2.- Se utiliza el fichero. Para ello cada clase presenta diferentes métodos de acceso para
escribir o leer en el fichero.
3.- 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.
4.- 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 <= 100 ; i++ )
{
try{

10
f.writeByte( i );
} catch( IOException e){
// Gestion de excepcion de ejemplo
break; // No se puede seguir escribiendo
}
f.close();
}
}

El siguiente método accede al elemento cual de un fichero binario, imprimiendo la


longitud del fichero, el elemento cual y su 10 veces siguiente elemento:
static void imprimeEltoN(String ruta, long cual)
throws IOException{
RandomAccessFile f=new RandomAccessFile(ruta,"r"); // E/
System.out.print( "El fichero " + ruta );
System.out.println( " ocupa " + f.length() + " bytes." );
f.seek( cual-1 ); // Me posiciono (-1 porque empieza en 0)
System.out.print(" En la posicion " + f.getFilePointer() );
System.out.println(" esta el numero " + f.readByte() );
f.skipBytes( 9 ); // Salto 9 => 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 main(String[] args)
{ int i = 12;
int j = 10;
int suma = i + j;
int resta = i - j;
int mult = i * j;
int div = i / j;
int modulo = 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.

Operadores de Relación
Operador Utilización Resultado
> A>B Verdadero si A es mayor que B
>= A >= B Verdadero si A es mayor o igual que B
< A<B Verdadero si A es menor que B
<= A <= B Verdadero si A es menor o igual que B
== A == B Verdadero si A es igual a B
!= A != B Verdadero si A es diferente a B

Aquí tenemos un programa para ver el funcionamiento de estos operadores:


public class Relaciones {
public static void main(String args[]){

13
int i = -3;
byte b = 5;
float f = 1e-10f;
double d = 3.14;
boolean b1 = i > i;
boolean b2 = i < b;
boolean b3 = b <= f;
boolean b4 = f >= d;
boolean b5 = d != 0;
boolean b6 = 1 == f;
System.out.println( i + " > " + i + " = " + b1);
System.out.println( i + " < " + b + " = " + b2);
System.out.println( b + " <= " + f + " = " + b3);
System.out.println( f + " >= " + d + " = " + b4);
System.out.println( d + " != " + 0 + " = " + b5);
System.out.println( 1 + " == " + f + " = " + b6);
}
}

No nos olvidemos de los char, que también participan. Podemos comparar caracteres,
pues internamente están almacenados como números.
char a = 'A';
char b = 'B';
boolean x = a > 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 ).

Operadores Booleanos
Nombre Operador Utilización Resultado
AND && A && B Verdadero cuando A y B son verdaderos. Evaluación
condicional.
OR || A || B Verdadero cuando A o B son verdaderos. Evaluación
condicional.
NOT ! !A Verdadero si A es falso.
AND & A&B Verdadero cuando A y B son verdaderos. Siempre
evalúa ambos operandos.
OR | A|B Verdadero cuando A o B son verdaderos. Siempre
evalúa ambos operandos.
XOR ^ 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 de la conjunción");
System.out.println( x + " AND " + x + " = " + a1 );
System.out.println( x + " AND " + y + " = " + a2 );
System.out.println( y + " AND " + x + " = " + a3 );
System.out.println( y + " AND " + y + " = " + 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 Resultado
<< A << B Desplazamiento de A a la izquierda en B posiciones.
>> A >> B Desplazamiento de A a la derecha en B posiciones, tiene en
cuenta el signo.
>>> A >>> B Desplazamiento de A a la derecha en B posiciones, no tiene en
cuenta el signo.
& A&B Operación AND a nivel de bits.
| A|B Operación OR a nivel de bits.
^ A^B Operación XOR a nivel de bits.
~ ~A 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.

Operadores de Asignación
Operación Operador Utilización Operación
Equivalente
Suma += A += B A=A+B
Resta -= A -= B A=A–B
Multiplicación *= A *= B A=A*B
División /= A /= B A=A/B
Residuo %= A %= B A=A%B
Desplazamiento a la izquierda <<= A <<= B A = A << B
Desplazamiento a la derecha >>= A >>= B A = A >> B
Desplazamiento a la derecha sin signo >>>= A >>>= B A = A >>> B
AND de bits &= A &= B A=A&B
OR de bits |= A |= B A=A|B
XOR de bits ^= 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||++j<--j+j--;

Civilizadamente se organizan de acuerdo al nivel de precedencia de cada uno.


Primeramente proceden los unarios, luego los aritméticos, después los de bits, posteriormente los
relacionales, detrás vienen los booleanos y por último el operador de asignación. La regla de
precedencia establece que los operadores de mayor nivel se ejecuten primero.

Precedencia de Operadores
Descripción Operadores
Operadores posfijos Op++ Op--
Operadores unarios ++Op --Op +Op -Op ~ !
Multiplicación y división * / %
Suma y resta + -
Desplazamiento << >> >>>
Operadores relacionales < > <= >=
Equivalencia == !=
Operador AND &
Operador XOR ^
Operador OR |
AND booleano &&

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 Tamaño Rango Descripción


(en bits)
byte 8 -128 a 127 Entero de 1 byte.
short 16 -32,768 a 32,767 Entero corto.
int 32 -2,147,483,648 a 2,147,483,647 Entero.
long 64 -9,223,372,036,854,775,808L a Entero largo.
9,223,372,036,854,775,807L
float 32 +/- 3.4E+38F (6-7 dígitos importantes) Coma flotante de precisión
simple.
double 64 +/- 1.8E+308 (15 dígitos importantes) Coma flotante de precisión
doble.
char 16 Conjunto de caracteres Unicode ISO Un sólo carácter.
boolean 1 Verdadero o falso 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 catch
boolean char
break class
byte continue
case default

21
do public
double return
else short
extends static
final strictfp
finally super
float switch
for synchronized
if this
implements throw
import throws
instanceof transient
int try
interface void
long volatile
native while
new wesureal
package true (literal booleano)
prívate false (literal booleano)
protected 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 el caso de "1" expulsar lata de Orange Duke.


En el caso de "2" expulsar lata de Lemmon Duke.
En el caso de "3" expulsar lata de Cola Duke.
En el caso de "4" expulsar lata de Coffee Duke.
En el caso de "5" expulsar lata de 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){ if ( condición1 ) {
sentecia1; if ( condición2 ) {
} else { sentencia1;
sentencia2; } else {
} sentencia2;
}
} else {
sentencia2;
}

26
Disyunción
if ( condición1 || condición2 ) { if ( condición1 ){
sentencia1; sentencia1;
} else { } else {
sentencia2; if ( condición2 ) {
} sentencia1;
} else {
sentencia2;
}
}
Negación
if ( ! condición1) { if ( condición1) {
sentencia1; sentencia2;
} else { } else {
sentencia2; 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 <= b ? a : b);

Se han utilizado los paréntesis debido a que su orden de precedencia es bajísima. De otra
forma se hubiera evaluado la resta antes de las selecciones.

1.9.3 Iteración

Supongamos que nos piden que realicemos un dibujo sencillo que se despliegue por
pantalla. El objetivo es mostrar un cuadrado de 5 filas por 5 columnas trazado con cualquier
carácter. Con los elementos de programación que ya conocemos, realizamos un programa que
cumpla con este requisito.

public class Cuadrado{


public static void main(String args[]){
System.out.println("*****");
System.out.println("*****");
System.out.println("*****");
System.out.println("*****");
System.out.println("*****");
}
}

Básicamente este programa cumple con el objetivo. Pero... ¿No notan que algo anda mal?
Se repitió cinco veces una instrucción. ¿Qué ocurriría si nos solicitaran una figura más grande?
Tendríamos que agregar más líneas de código. Y si en vez de una figura, se les ocurría pedirnos que
listáramos una lista de números del uno al millón, el código sería inmenso. También imagínense lo
que tardaríamos en escribirlo.

31
Sin embargo tenemos un recurso para acudir en cuanto surjan estos problemas. Son las
estructuras de iteración. Una iteración consiste en una repetición de un bloque de sentencias un
numero determinando de veces o hasta que se cumpla una condición.

De esta forma el código puede simplificarse notablemente. Antes de entrar en los detalles
de la implementación con Java, veremos cómo podemos realizar algunas rutinas en pseudocódigo:

Repetir 5 veces :
Imprimir ("*****");

Le pasamos al intérprete la tarea de reiterar una acción. Nosotros solamente escribimos


una vez lo que hay que hacer.

Siguiendo con una segunda aproximación, podríamos agregarle un registro que cuente el
número de iteraciones. Además adicionamos una condición para que indicar que el registro no
debe pasar de cierto número. De esta manera, el pseudocódigo quedará así:

N = 1;
Mientras que N <= 5
Imprimir ("*****");
N = N + 1;

Lo que hicimos fue inicializar el contador, agregar una condición de corte, indicar que
sentencia se debe repetir y finalmente incrementamos el contador. La condición es una expresión
lógica que debe tener un valor de verdadero o falso. En la ejecución, cada vez que termina la
última sentencia vuelve a evaluar la condición, si se cumple sigue la iteración, de lo contrario
termina.

Esta es una estructura de iteración útil para los casos en donde sabemos a priori cuantas
repeticiones se deben ejecutar. Con pocos retoques se pueden realizar iteraciones desconociendo
el número de ciclos.

Realicemos un programa que transforme un número decimal en binario. En la vida real,


para hacer esto, realizamos sucesivas divisiones por 2 al numero decimal. A cada división tomamos
el resto y continuamos hasta que el resultado sea cero.

Por ejemplo: Tenemos que pasar el número 25 a binario

25 mod 2 = 1; 25 div 2 = 12;


12 mod 2 = 0; 12 div 2 = 6;
6 mod 2 = 0; 6 div 2 = 3;
3 mod 2 = 1; 3 div 2 = 1;
1 mod 2 = 1; 1 div 2 = 0;

NOTA: en java actualmente se usa el signo "%" para hacer el mod).

El resultado es 11001

32
En general, no sabemos cuántas divisiones tendremos que realizar. Pero si sabemos que
debemos llegar a cero con la división. Las tareas que repetimos son las de tomar el resto y el
resultado de la división. Con estos datos en mente, podremos realizar un algoritmo.

DECIMAL = 25;
Mientras que DECIMAL > 0 :
BINARIO = DECIMAL % 2 + BINARIO;
DECIMAL = DECIMAL / 2;

Sentencia while

La sentencia while es la más sencilla de las estructuras de iteración. La iteración


continuará hasta que su condición sea falsa.

while ( condición ) sentencia ;

La condición tiene que tomar un valor booleano (verdadero o falso). Si este valor es
verdadero, se ejecutará la sentencia. Concluida esta acción se vuelve a evaluar la condición.
Proseguirán los ciclos hasta que la condición no cambie a falso.

Esta es una estructura de iteración preprueba, es decir primero se evalúa la condición


antes de realizar cualquier acción. Si de entrada la condición es falsa nunca ejecutará el conjunto
de sentencias.

int n = 0;
while ( n > 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 <= 5 ) {
System.out.println("*****");
contador++;
}
}
}

En este algoritmo, inicializamos el contador a 1 y luego en cada ciclo se incrementa. La


condición de corte tiene como objetivo no permitir más vueltas si el contador superó el valor 5.

Para tener varias veces el asterisco sin necesidad de imprimirlo así "*****", utilizamos
otro ciclo while y otra variable que inicializaremos dentro del ciclo para que se cumpla la cual
llamaremos "contador2", obtendremos el mismo resultado que el anterior, el código quedaría así:

public class Cuadrado {


public static void main (String args [])
{
int contador = 1;
while (contador <= 5)
{
int contador2 = 1;
while (contador2 <= 5)
{
System.out.print ("*");
contador2++;
}
System.out.println ();
contador++;

34
}
}
}

Sentencia do-while

La sentencia de iteración do-while es de tipo posprueba. Primero realiza las acciones luego
pregunta. La sintaxis es la siguiente:

do sentencia while ( condición );

Observamos que es como un while pero al revés. Primeramente se ejecuta la sentencia y


luego evalúa la condición. Si la expresión de la condición es verdadera vuelve a dar un ciclo. De lo
contrario, termina. Esto nos garantiza que la sentencia se ejecute al menos una vez.

do System.out.println("Lo veras una vez");


while ( false );

Resulta útil para los casos en donde tendremos que realizar ciertas acciones antes de
verificar una condición.

Realicemos un programa que cuente la cantidad de dígitos que posee un número. Para ello
tendremos que dividir por diez el número que nos han dado, hasta que el resultado se vuelva cero.
Entonces recurrimos al while para realice los ciclos necesarios.

public class CuentaDigitos{


public static void main(String args[]){
int número = 4557888;
int dígitos = 0;
while ( número > 0 ) {
número /=10;
dígitos++;
}
System.out.println(dígitos);
}
}

¿Qué ocurre si el número que nos dan es el cero? El resultado nos dará cero. Obviamente
es erróneo, debería devolver un dígito. Pero no entra en el ciclo debido a que de entrada no
satisface la condición. Podríamos implementar una solución "ad hoc".

número /=10;
dígitos++;
while ( número > 0 ) {
número /=10;
dígitos++;
}

Realizamos primeramente las operaciones y luego entramos en el bucle si se verifica la


condición. Pero no hace falta mirar mucho para darnos cuenta que es una solución poco elegante.
En realidad quedará mejor con la sentencia do-while:

35
public class CuentaDigitos{
public static void main(String args[]){
int número = 4557888;
int dígitos = 0;
do {
número /=10;
dígitos++;
}
while ( número > 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 x 1 = 3");
System.out.println("3 x 2 = 6");
System.out.println("3 x 3 = 9");
System.out.println("3 x 4 = 12");
System.out.println("3 x 5 = 15");
System.out.println("3 x 6 = 18");
System.out.println("3 x 7 = 21");
System.out.println("3 x 8 = 24");
System.out.println("3 x 9 = 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 <= 9 ) {
System.out.println("3 x " + factor + " = " + 3*factor );
factor++;
}

Utilizamos la variable factor para contar la cantidad de líneas que imprimimos.


Primeramente la inicializamos en uno. Cuando se ejecuta la interacción se controla que no supere
su valor de 9. Si el valor es menor o igual que nueve, imprime una línea de la tabla e incrementa a
uno el valor de factor. Cualquier caso de interacción que se complete en una cantidad prefijada de
ciclos, necesitamos una variable de control. Si utilizamos la sentencia while, esta variable se debe
definir e inicializar antes del bucle y contar con una instrucción que modifique su valor dentro del
bucle.

Veremos cómo este código cambia ligeramente si en lugar de while presentamos una
nueva sentencia denominada for:

36
for ( int factor = 1; factor <= 9; factor ++ ) {
System.out.println("3 x " + factor + " = " + 3*factor );
}

La sentencia for me permite repetir un ciclo n veces, en donde se debe determinar el valor
inicial y cuantas veces se repetirá.

Sintaxis:

for({valor inicial};{condición de termino};{factor de incremento del


valor inicial}){
//acá va lo que se repetirá n veces de acuerdo a la condición de
termino
}

Bucle infinito: no hay control, entonces no se detiene:

for ( ;;){}

Operador coma:

for ( int k=1, j=10 ;k < j ;k++ ,j-- ){


System.out.println(k + " " + j);
}

for mejorado

Supongamos que tenemos un arreglo de enteros que deseamos presentar en pantalla,


usando la orden for tradicional, el código, podría quedar así:

int[] laiEnteros = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


for (int i = 0; i < 10; i ++)
System.out.println (laiEnteros[i]);

Java ofrece una funcionalidad extra para la orden for, mediante la que se puede simplificar
notablemente el código anterior, quedando así:

int[] laiEnteros = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


for (int liElemento : laiEnteros)
System.out.println (liElemento);

En este caso, liElemento toma el valor de cada uno de los elementos de laiEnteros,
permitiendo una navegación más simple, puesto que se evitan posibles errores derivados del uso
de los arreglos en forma directa. También se puede usar esta forma, para estructuras más
complejas como objetos de la clase Collection

public void Presenta (Collection <Integer> poConjunto){


for (Iterator <Integer> loElemento : poConjunto)
System.out.println (loElemento)
}

37
Ejercicios
1.- Crear una aplicación de consola en donde el usuario proporcione un número entero mayor
que 0 y el programa deberá determinar si ese número es primo o no.
2.- Crear una aplicación de consola que determine, de entre los números enteros del 1 al 5000,
cuál de ellos es un número es primo y cuál no lo es.
3.- Crear una aplicación de consola en donde se obtenga el resultado del cálculo de la siguiente
serie: 5 + 2/2 + 3/4 + 4/8 + 5/16 + 6/32 + 7/64 + 8/128 + 9/256 + 10/512.
4.- Crear una aplicación de consola en donde se obtenga el resultado del cálculo de la siguiente
serie: 10 + 1/2 + 2/4 + 3/6 + 4/8 + 5/10 + 6/12 + 7/14 + 8/16 + 9/18 + 10/20.
5.- Crear una aplicación de consola en donde se obtenga el resultado del cálculo de la siguiente
serie: 2 + ((1/2)+1) + ((2/4)+3) + ((3/6)+5) + ((4/8)+7) + ((5/10)+9) + ((6/12)+11) + ((7/14+13) +
((8/16)+15) + ((9/18)+17) + ((10/20)+19).
6.- Crear una aplicación de consola en donde se obtenga el resultado del cálculo de la siguiente
serie: 1 + (1/2) + ((2/4)+3) + ((3/6)+6) + ((4/8)+9) + ((5/10)+12) + ((6/12)+15) + ((7/14+18) +
((8/16)+21) + ((9/18)+24) + ((10/20)+27).
7.- Leer el radio de un círculo y calcular e imprimir superficie y circunferencia. Superficie = PI *
Radio ^ 2. Longitud = 2 * PI * Radio.
8.- Cálculo de los salarios de los empleados de una empresa, sabiendo que éstos se calculan en
base a las horas semanales trabajadas y de acuerdo a un precio especificado por cada hora. Si
se pasan de 40 horas semanales, las horas extraordinarias se pagarán a razón de 1.5 veces la
hora ordinaria.
9.- Crear una aplicación que lea una secuencia de 20 valores enteros (positivos y negativos). El
programa deberá calcular el promedio de los valores positivos y el promedio de los valores
negativos. A su vez, deberá expresar cuántas veces se proporcionó el valor 0.
10.- Desarrollar una aplicación de consola que imprima la serie de números pares entre 0 y 1000.
11.- Crear una aplicación de consola en donde el usuario proporcione una secuencia de números
enteros a través del teclado. La lectura de la secuencia deberá terminar cuando el usuario
proporcione el valor 0. Para finalizar, el programa deberá determinar cuántos valores fueron
pares.
12.- Crear una aplicación de consola en donde se lean 3 valores enteros desde teclado. El
programa deberá determinar si la suma de cualquier pareja de ellos es igual al tercer número.
Si se cumple esta condición, escribir “Iguales”, y en caso contrario, escribir “Distintas”. Por
ejemplo, si los números son: 3, 6 y 9, la respuesta es “Iguales”, pues 3 + 6 = 9.
13.- Un corredor de maratón (distancia = 42.195 km) ha recorrido la carrera en 2 horas 25
minutos. Se desea un programa que calcule el tiempo medio en minutos por kilómetro.
14.- Escribir un programa para calcular el área de un triángulo dada la base y la altura. Estos dos
datos deberán ser proporcionados desde teclado por el usuario.
15.- Escribir un programa que determine el área y el volumen de un cilindro cuyas dimensiones de
radio y altura se lean desde teclado.

38
Unidad II. Subprogramas

39

Vous aimerez peut-être aussi