Datos H. Tejeda Noviembre 2016 Índice 1. Declaración y uso de constantes y variables 1 2. Tipos de datos enteros 7 3. Tipo de dato boolean 8 4. Tipos de datos de punto flotante 9 5. Tipo de dato char 10 6. Clase Scanner para entrada desde el teclado 12 7. Uso de la clase JOptionPane para entrada desde la GUI 15 8. Aritmética 17 9. Conversión de tipos 18 1. Declaración y uso de constantes y variables Un elemento de datos es constante cuando su valor no puede ser cambiado durante la ejecución de un progroma. Por ejemplo, cuando se incluye la siguiente sentencia en una clase Java, el número 1234 es una constante: 1 System.out.println(1234); Cada vez que la aplicación sea ejecutada el valor 1234 es mostrado. Los programadores se refieren al 1234 como constante literal porque su valor es tomado literalmente en cada uso. El número 1234 es también llamado una constante numérica como opuesto a una constante carácter o cadena. Además, es una constante sin nombre como opuesta a una con nombre, porque no hay un identificador asociado con esta. Se puede configurar un elemento de datos para ser variable. Una variable es una localidad de memoria que puede guardar un valor. Una variable puede tener sólo un valor a la vez, pero el valor que tiene puede cambiar. Ya sea que el elemento de datos sea variable o constante, en Java este siempre tiene un tipo de dato. El tipo de dato de un elemento indica el tipo de dato que puede ser guardado, cuanta memoria ocupa, y que tipos de operaciones pueden hacerse con el dato. Java proporciona ocho tipos primitivos de datos. Un tipo primitivo es el tipo de dato más simple. Enseguida se describen los ocho tipos: Palabra reservada byte short int long float double char boolean Descripción entero tamaño byte entero corto entero (defecto) entero largo punto flotante, precisión simple punto flotante, precisión doble (defecto) un sólo carácter un valor boolean (true o false) Los tipos primitivos también sirven como ladrillos para tipos de datos más complejos, llamados tipos referencia, los cuales guardan direcciones de memoria. Las clases que serán creadas son ejemplos de tipos de referencias, como la clase Scanner que es revisada más adelante. 1.1. Declaración de variables Una declaración de variable es una sentencia que reserva una localidad nombrada de memoria e tiene lo siguiente: Un tipo de dato que identifica el tipo de dato que la variable guardará. Una identificador que es el nombre de la variable. 2 Un operador de asignación opcional y un valor asignado, si se quiere que la variable contenga un valor inicial. Un punto y coma como terminador. Los nombres de variables observan las mismas reglas para nombrar identificadores legales de clase, esto es, inician con una letra y no pueden ser una palabra reservada. Se debe declarar una variable antes de poder usarla. Se puede declarar una variable en cualquier punto antes de usarla, pero es una práctica declarar primero las variables en un método y luego poner las sentencias ejecutables. Java es un lenguaje fuertemente tipado, o uno en el cual cada variable tiene un tipo de dato bien definido que limita las operaciones que se pueden realizar con este: esto también ı́mplica que todas las variables deben ser declaradas antes de que puedan usarse. Por convención los nombres de la variables inician con letras minúsculas para distinguirlas de los nombres de las clases. Se emplea también el estilo caja camello superior donde se pone se capitalizan las siguientes palabras del identificador. El siguiente ejemplo declara una tipo de variable int llamado miEdad y le asigna un valor inicial de 18: int miEdad = 18; Esta declaración es una sentencia completa y ejecutable, por lo que termina con punto y coma. El sı́mbolo de igual (=) es el operador de asignación. Cualquier valor a la derecha del signo de igual es asignado a la variable a la izquierda del signo igual. Una asignación hecha cuando se declara una variable es una inicialización; darle el valor después es sólo una asignación. Para el siguiente código, la primera sentencia es una inicialización, y la segunda es una asignación: int miEdad = 18; miEdad = 21; Se declara una variable una sola vez, pero se le podrı́an asignar valores nuevos las veces que se requiera. Observar que una expresión con una literal a la izquierda del operador de asignación, como en 21 = miEdad, es ilegal. El operador de asignación tiene una asociatividad derecha-izquierda. La asociatividad indica el orden en el cual los operadores son usados con operadores. La asociatividad puede ser derecha-izquierda o izquierda-derecha. Un identificador que puede aparecer en el lado izquierdo de un operador de asignación en ocasiones es referido como un lvalue. Una constante númerica como 18 no es un lvalue; sólo es un rvalue, o un dato que puede aparecer sólo en el lado derecho de un operador de asignación. Una variable puede ser usada como un lvalue o un rvalue. 3 Cuando se declara una variable dentro de un método pero no se le asigna valor, es una variable no inicializada, Por ejemplo, la siguiente declaración de variable indica un tipo int con el nombre miEdad, pero no hay valor asignado al momento de su creación: int miEdad; Una variable no inicializada contiene un valor desconocido llamado un valor basura. Java protege de usar inadvertidamente el valor basura que está guardado en una variable no inicializada, ya que se recibe, al compilar, un error indicando que la variable podrı́a no estar inicializada. Nota. Las variables declaradas en una clase, fuera de cualquier método, son inicializadas automáticamente. Se pueden declarar variables múltiples del mismo tipo en sentencias separadas. También se puede declarar dos, o más, variables del mismo tipo en una sola sentencia separando la declaraciones de variable con una coma, como se muestra enseguida: int miEdad = 18, tuEdad = 15; Por convención, los programadores declaran la mayorı́a de las variables en variables separadas. Se deberı́an declarar variables múltiples en la misma sentencia sólo si están relacionadas estrechamente. En el caso de que se quieran declarar variables de tipos diferentes, se debe usar una sentencia separada para cada tipo. 1.2. Constantes con nombre Una variable es una localidad nombrada de memoria para la cual el contenido puede cambiar. Si un valor de una localidad no deberı́a cambiar en la ejecución de un programa, esta puede ser creada para ser una constante con nombre. También es conocida com una constante simbólica. Una constante con nombre es parecida a una variable en cuanto a que tiene un tipo de dato, un nombre y un valor. Una constante con nombre difiere de una variable en: En la sentencia de declaración, el tipo de dato de una constante con nombre es precedida por la palabra reservada final. A una constante con nombre sólo se le puede asignar un valor una sola vez, y luego este nunca puede ser cambiado. Se inicializa la constante con nombre usualmente cuando se declara, si no se hace, se le conoce como final blanco, y se puede asignar un valor después, pero antes de ser usada. 4 Por convención los identificadores de las constantes con nombres usan sólo mayúsculas, y los guiones bajos se usan para separar palabras, como en el siguiente ejemplo. final final final final int CANT_DE_SALONES = 20; double PI = 3.14159; double IMPUESTO = 0.15; string UNIVERSIDAD = "UMSNH"; Las razones para usar una constante con nombre en vez de una literal son: Usar constantes con nombre hace la lectura y la comprensión de los programas más fácil. Es más fácil cambiar sólo el valor de una constante con nombre que un conjunto de literales que aparezcan en un programa. El uso de constantes con nombre reduce la cantidad de errores tipográficos. Cuando se usa una constante con nombre en una expresión, esta destaca de una variable. 1.3. Alcance de variables y constantes El alcance de un elemento de datos es el área en el cual es visible a un programa y en el cual se puede referir a este usando un identificador simple. Una variable o constante está en el alcance desde el lugar donde está declarado hasta el final del bloque de código en la cual la declaración se encuentra. Un bloque de código es el código contenido entre un conjunto de llaves. Ası́, si se declara una variable o constante dentro de un método, este puede ser usado desde su declaración hasta el final del método, no importando que el método contengo conjuntos múltiples de llaves. Entonces, un elemento de datos es usable sólo hasta el final del bloque que tiene la declaración. 1.4. Concatenar cadenas a variables y constantes Se puede mostrar una variable o una cadena en una sentencia print() o println() sola o en combinación con una cadena. Por ejemplo, la clase MostrarNumeros, código 1, declara un entero diaDeCorte, inicializado a 5 en la lı́nea 3, luego este es enviado sólo al método print() en la lı́nea 5; en las lı́neas 7-8 es combinado, o concatenado, a un String. En Java, cuando una variable numérica es concantenada a un String con el sı́mbolo más, la expresión completa es convertida a una String 5 1 2 3 4 5 6 7 8 9 10 public c l a s s MostrarNumeros { public s t a t i c void main ( S t r i n g [ ] a r g s ) { int diaDeCorte = 5 ; System . out . p r i n t ( ” Las f a c t u r a s son mandadas e l dı́a ” ) ; System . out . p r i n t ( diaDeCorte ) ; System . out . p r i n t l n ( ” d e l mes” ) ; System . out . p r i n t l n ( ” S i g u i e n t e f a c t u r a : d i c i e m b r e ” + diaDeCorte ) ; } } Código 1: clase MostrarNumeros La salida del código anterior debe mostrar algo como lo siguiente: Las facturas son mandadas el dı́a 5 del mes Siguiente factura: diciembre 5 Cuando se concatena un String con números, la expresión entera es un String. Por lo tanto la expresión "A" + 3 + 4 da el String "A34". Si la intención es crear el String A7, entonces se deben agregar paréntesis para escribir "A" + (3 + 4). La clase DialogoNumeros, código 2, usa el método showMessageDialog() dos veces para mostrar un entero declarado como diasCredito e inicializada a 30. En cada sentencia de las lı́neas 5 y 6, la variable numérica es concatenada a un String, haciendo que el segundo argumento un String. En la lı́nea 5, el String concatenado es un String vacı́o, o String null, creado con un conjunto de comillas dobles sin nada dentro de estas. El primer cuadro de diálogo sólo muestra el valor 30, y el segundo muestra la concatenación con cadenas no nulas. 1 2 3 4 5 6 7 8 9 import j a v a x . swing . JOptionPane ; public c l a s s DialogoNumeros { public s t a t i c void main ( S t r i n g [ ] a r g s ) { int d i a s C r e d i t o = 3 0 ; JOptionPane . showMessageDialog ( null , ” ” + d i a s C r e d i t o ) ; JOptionPane . showMessageDialog ( null , ”Cada f a c t u r a e s por ” + d i a s C r e d i t o + ” d i a s ” ) ; } } Código 2: clase DialogoNumeros 1.5. Error al intercambiar valores Cada constante sólo puede tener un valor en la duración del programa; cada variable puede tener sólo un valor a la vez. En el siguiente extracto de código, se tiene en cuenta lo anterior, 6 para intercambiar los valores que tienen las variables x e y, usando como variable auxiliar a z. int z = x = y = 2. x = 2, y = 10, z; x; y; z; Tipos de datos enteros Se pueden usar variables de tipo byte, short, int, y long para guardar enteros en Java; un entero puede representar un subconjunto finito de los números enteros. El tipo de dato int es el tipo de entero más usado. Una variable de tipo int puede contener cualquier valor entero desde -2,147,483,648 hasta +2,147,483,647. Cuando se asigna un valor a una variable int, no se ponen comas ni puntos, se ponen sólo dı́gitos y un signo más o ménos opcional para indicar o entero positivo y negativo. Nota. Los valores enteros legales están de −231 hasta 231 − 1. Estos son los valores más grandes y mas bajos que se pueden guardar en cuatro bytes de memoria,el cual es el tamaño de una variable int. Los tipos byte, short, y long son variaciones del tipo entero. Los tipos byte y short ocupan menos memoria y puede contener sólo valores pequeños; el tipo long ocupa más memoria y puede manejar valores más grandes. La siguiente tabla muestra el lı́mite superior e inferior de estos tipos. Tipo Valor Mı́nimo byte -128 short -32,768 int −2,147,483,648 long −9,223,372,036,854,775,808 Valor Máximo 127 32,767 2,147,483,647 −9,223,372,036,854,775,807 Tamaño en bytes 1 2 4 8 Se debe escoger apropiadamente los tipos para las variables que serán usadas en una aplicación. Si se intenta asignar un valor que más grande para el tipo de dato de la variable, el compilador emite un mensaje de error, por lo que la aplicación no se ejecuta. Si se escoge un tipo de dato que es mayor que el que se ocupa, se desperdicia memoria. Por ejemplo, una aplicación personal podrı́a usar una variable byte para la cantidad de dependientes, un short para las horas trabajadas en un mes, 127 no es suficiente, y un int para un salario anual. 7 Si una aplicación usa un entero constante literal, como 987, el número es un int por defecto. Si se necesita usar una constante mayor que 2,147,483,647, o menor que -2,147,483,648, se debe seguir el número con la letra L, o l, para indicar tipo long. Se prefiere L para evitar confusión con el dı́gito 1. Por ejemplo, la siguiente sentencia guarda un número que es mayor que el lı́mite máximo para el tipo int. long mosquitosEnBosquesDelNorte = 2444555999L; No se requiere algún caracter para guardar una constante numérica en un int, byte, o short. 3. Tipo de dato boolean La lógica de Boole o booleana está basada en comparaciones falso-verdadero. Una variable booleana puede contener sólo uno de dos valores, true o false. Las siguientes sentencias declaran y asignan valores apropiados a variables booleanas: boolean esDı́aDePago = false; boolean estásQuebrado = true; También se pueden asignar valores obtenidos de comparaciones a variables booleanas. Se soportan seis operadores relacionales en Java para realizar comparaciones. Un operador relacional compara dos elementos; es también llamado a veces operador de comparación. Una expresión que contenga un operador relacional da un valor booleano. En la siguiente tabla se describen los operadores relacionales. Operador < > == <= >= != Descripción Ejemplo Ejemplo verdadero falso menor que 1<2 2<1 mayor que 10 > 3 3 > 10 igual a 5 == 5 5 == 6 menor que o igual a 7 <= 7 9 <= 8 mayor que o igual a 7 >= 4 1 >= 3 diferente a 5!=6 5 == 5 Cuando se usa alguno de los operadores relacionales con dos sı́mbolos, como ==, <=, >=, o !=, no se puede poner ningún espacio blanco entre los dos sı́mbolos. Tampoco se puede invertir el orden de los sı́mbolos, ası́ =<, => y =! no son operadores válidos. Sentencias válidas de declaración podrı́an incluir expresiones con operadores relacionales: 8 boolean esCincoMenorOIgual = (5 <= 3); // valor guardado será false Las expresiones booleanas tienen más sentido cuando variables, a las cuales se le han asignado valores, son usadas en las comparaciones, como en los siguientes ejemplos. boolean esHorasExtras = (horas > 40); boolean esLimiteSuperiorImpuesto = (ingreso > 1000000); boolean esPrimerPuntuaciónMayor = (puntuación1 > puntuación2); 4. Tipos de datos de punto flotante Un número punto flotante contiene posiciones decimales. Java soporta dos tipos de dato punto flotante: float y double. Un tipo de dato float puede guardar valores punto flotante hasta con una precisión de 6 o 7 dı́gitos significativos. Un tipo de dato double requiere más memoria que un float y puede contener de 14 a 15 dı́gitos significativos de precisión. El término dı́gitos significativos se refiere a la precisión matemática de un valor. Por ejemplo, un float dado con el valor de 0.324516789 es mostrado como 0.324517 porque el valor es preciso solo hasta la sexta posición decimal. El valor máximo para un double es 3.4x1038 . Nota. Un float dado con el valor 324516789 es mostrado como 3.24617e+008, lo cual es 324517000. La e en el valor mostrado es para el exponente; el +008 indica que el punto decimal está ocho posiciones a la derecha de donde fue muestrado. En caso de ser negativo indica que el punto decimal está a la izquierda, indicando un número muy pequeño. Este formato es llamado notación cientı́fica. Nota. Un programador podrı́a escoger guardar un valor como float en vez de double para ahorrar memoria. Sin embargo, si niveles altos de precisión son necesarios, se deberı́a escoger double. Una constante punto flotante, como 15.67, es un double por defecto. Para indicar que una constante numérica punto flotante es un float, se puede poner la letra F, o f, después del numero, como en: float monedero = 23.50F; Se puede también poner D o d después de una constante de punto flotante para indicar que es un double, pero como es el tipo por defecto en puntos flotantes, si no se pone ası́ será guardada. 9 5. Tipo de dato char Se usa el tipo de dato char para guardar un solo carácter cualquiera. Se encierran los valores de caracteres constantes entre comillas simples. Las siguientes son declaraciones de caracteres: char inicialPaterno = ’L’; char calificaciónComputaciónI = ’A’; char unaEstrella = ’*’; Un carácter puede ser cualquier letra, signo de puntuación, o dı́gito. Un carácter que es un dı́gito es guardado en la memoria de forma diferente a un valor numérico que represente el mismo dı́gito. Por ejemplo, las siguientes dos sentencias son legales: char unValorChar = ’7’; int unValorInt = 7; Si se muestran cada uno de estos valores usando el método println(), se verá un 7. Pero, sólo el valor numérico, unValorInt, puede ser usado para representar el valor 9 en sentencias aritméticas. Una constante numérica puede ser guardada en una variable carácter y un carácter que represente un número puede ser guardado en una variable numérica. Las siguientes dos sentencias son legales, pero al menos que se entienda su significado, podrı́an dar resultados no deseados: char unValorChar = 7; int unValorInt = ’7’; Si estas variables son mostradas con println(), entonces el resultado de la salida será un blanco para unValorChar y el número 55 para unValorInt. El sistema operativo Linux guarda cada carácter como un número o código; a cada carácter se le asigna un código numérico único usando Unicode. Por ejemplo, el carácter A es guardado usando el valor 65, y B con el valor 66. Una variable de tipo char puede guardar sólo un carácter. Para guardar un cadena de caracteres, tales como el nombre de una persona, se debe usar una estructura de datos llamada String. En Java, String es una clase incorporada que da los medios para guardar y manipular cadenas de caracteres. Las constantes String son escritas entre comillas dobles. Por ejemplo, la expresión que guarda el nombre Miguel como una cada en una variable llamada primerNombre es: String primerNombre = "Miguel"; 10 También se puede guardar caracteres no imprimibles, como retroceso o tabulador, en una variable char. Para estos carácteres se tiene que usar una secuencia de escape, la cual inicia con una diagonal invertida seguida por un carácter, ası́ la pareja representa un sólo carácter. El siguiente código guarda un carácter de nueva lı́nea y de tabulador: char unCarNuevaLı́nea = ’\n’; char unCarTabulador = ’\t’; En la siguiente tabla se describe algunas secuencias de escape comunes que se pueden usar en la salida de la ventana de comandos en Java. Secuencia de escape Descripción \b Retroceso; mueve el cursor un espacio a la izquierda \t Tabulador; mueve el cursor a la siguiente parada de tabulación \n Nueva lı́nea; mueve el cursor al inicio de la siguiente lı́nea \r Retorno de carro; mueve el cursor al inicio de la lı́nea actual \" Comillas dobles; muestra el sı́mbolo de comillas dobles \’ Comilla simple, muestra el sı́mbolo de comilla simple \\ Diagonal invertida; muestra el carácter diagonal invertida Nota. Cuando se muestran valores con cuadros de diálogo JOptionPane en vez de la ventana de comandos, la secuencias de escape nueva lı́nea, comillas dobles y diagonal invertida funcionan como se espera, pero retroceso, tabulador, y retorno de carro no lo hacen. Cuando se quieren generar salida en lı́neas múltiples en la ventana de comandos, se puede hacer de dos formas. Se puede usar la secuencia de escape nueva lı́nea, o se puede usar el método println() varias veces. Las siguientes dos clases producen el mismo resultado. public class HolaMundoNuevaLinea { public static void main(String[] args) { System.out.println("Hola\nmundo"); } } public class HolaMundoDosPrintln { public static void main(String[] args) { System.out.println("Hola"); System.out.println("mundo"); } } 11 De las dos clases anteriores, la clase HolaMundoNuevaLinea es más eficiente, desde el punto de vista de tecleado de código, ya que el método System.out.println() aparece una sola vez, y desde el punto de vista del compilador también porque el método es llamado una sola vez. pero quizás la clase HolaMundoDosPrintln es más fácil de leer y entender. Nota. El método println() usar el carácter terminador de lı́nea de la plataforma local, el cual podrı́a ser o no el caracter nueva lı́nea. 6. Clase Scanner para entrada desde el teclado Se pueden asignar valores a las variables al ser declaradas, pero los programas son más útiles cuando el usuario puede dar valores diferentes a las variables cada vez que el programa se ejecuta. Para crear programas interactivos que acepten la entrada de un usuario, se puede usar System.in, la cual se refiere al dispositivo de entrada estándar (el teclado). El objeto System.in no es tan flexible como los métodos println() o print(), los cuales pueden mostrar diferentes tipos de datos (double, int, o String). System.in está diseñado sólo para leer bytes, pero usando conjuntamente la clase Scanner se hace más flexible. Para crear un objeto Scanner y conectarlo con el objeto System.in, se escribe una sentencia como la siguiente: Scanner dispositivoEntrada = new Scanner(System.in); En esta sentencia se declara un objeto de tipo Scanner con el nombre dispositivoEntrada y se le asigna el objeto creado Scanner. El objeto Scanner está conectado al dispositivo de entrada por defecto.La palabra clave new se requiere siempre que se crean objetos a diferencia de cuando se usan tipos primitivos. La clase Scanner contiene métodos que recuperan valores de un dispositivo de entrada. Cada valor recuperado es un token, el cual es un conjunto de caracteres que está separado del siguiente conjunto por un blanco. Frecuentemente esto significa que el dato es aceptado cuando el usuario presiona la tecla Intro, pero podrı́a significar también que el token sea aceptado después del espacio o el tabulador. La tabla siguiente resume algunos de los métodos más útiles que leen diferentes tipos de datos desde el dispositivo de entrada por default. 12 Metodo next() nextBoolean() nextByte() nextDouble() nextFloat() nextInt() nextLine() nextLong() nextShort() Descripción Recupera el siguiente token completo como un String. Recupera la entrada como un boolean. Recupera la entrada como un byte. Recupera la entrada como un double. Recupera la entrada como un float. Recupera la entrada como un int. Recupera la siguiente lı́nea de datos y la regresa como un String. Recupera la entrada como un long. Recupera la entrada como un short. La clase Scanner no tiene un método nextChar(). Para recuperar el primer caracter desde el teclado, usar nextLine().charAt(0). El código 3 de la clase ObtenerInfoUsuario usa dos métodos de la clase Scanner. El programa lee una cadena y un entero desde el teclado y los muestra. La clase Scanner está involucrada en: Lı́nea 1 La sentencia importa la clase Scanner del paquete java.util y es requerida para poder usar la clase. Lı́nea 6 Declara y crea un objeto Scanner. Lı́nea 8 Usa el método nextLine() para recuperar una lı́nea de texto desde el teclado y lo guarda en la variable nom. Lı́nea 10 Usa el método nextInt() para recuperar un entero desde el teclado y lo guarda en la variable edad. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import j a v a . u t i l . Scanner ; public c l a s s O b t e n e r I n f o U s u a r i o { public s t a t i c void main ( S t r i n g [ ] a r g s ) { S t r i n g nom ; int edad ; Scanner d i s p E n t r = new Scanner ( System . i n ) ; System . out . p r i n t ( ” I n g r e s a tu nombre : ” ) ; nom = d i s p E n t r . n e x t L i n e ( ) ; System . out . p r i n t ( ” I n g r e s a tu edad : ” ) ; edad = d i s p E n t r . n e x t I n t ( ) ; System . out . p r i n t l n ( ”Tu nombre e s ” + nom + ” y t i e n e s ” + edad + ” añ o s . ” ) ; } } Código 3: clase ObtenerInfoUsuario 13 Si se usa algún método de la clase Scanner y el siguiente token no puede ser convertido, se recibe un mensaje de error. Las cadenas literales contenidas en las sentencias print() que aparecen antes de cada sentencia de entrada son ejemplos de prompts (mensajes). Un prompt es un mensaje mostrado para el usuario que pide y describe la entrada. No son necesarios pero hacen la aplicación amigable. Se puede escribir un solo prompt que pida múltiples valores de entrada —por ejemplo, “Por favor ingresa tu edad, peso, código postal”. El usuario debera entonces ingresar tres valores separados con espacios, tabuladores o presionando la tecla Intro. Nota. Pidiendo al usuario que ingrese valores múltiples hace que se cometan errores. Es una mejor práctica separar cada prompt por cada valor de entrada pedido. 6.1. nextLine() después de otros métodos de Scanner Se puede presentar un problema cuando se usa alguno de los métodos de recuperación de tipo numérico de Scanner antes del método nextLine(). Cuando se ingresan caracteres usando el teclado, estos son guardados temporalmente en una localidad de memoria llamada el búfer del teclado, también se le conoce como búfer adelantado. Todas las teclas presionadas son guardadas en el búfer, incluyendo la tecla Intro. La forma como los métodos de recuperación de la clase Scanner difieren entre ellos son: Los métodos, como next(), nextInt(), y nextDouble() recuperan el siguiente token en el búfer hasta antes del siguiente blanco, el cual puede ser un espacio, tabulador, o Intro. El método nextLine() lee todos los datos incluyendo el carácter de la tecla Intro. Por lo anterior, cuando se pide al usuario un entero, y este ingresa 28 e Intro. La llamada del método nextInt() recupera el 28 y deja el Intro en el búfer del teclado. Si enseguida se pide una cadena y se llama al método nextLine() se recupera el Intro que estaba en el búfer, y entonces el usuario ya no puede ingresar nada. Para arreglar la situación descrita, después de cada llamada a next(), nextInt(), o nextDouble() se puede agregar una llamada al método nextLine() que recupere el Intro que se quedó en el búfer del teclado. 14 7. Uso de la clase JOptionPane para entrada desde la GUI Se puede aceptar entrada en un cuadro de diálogo GUI (Graphical User Interface) interfaz gráfica de usuario) usando la clase JOptionPane. Los dos cuadros de diálogo que pueden ser usados para aceptar la entrada del usuario son: InputDialog pide al usuario un texto de entrada. ConfirmDialog pide al usuario responder una pregunta usando botones (Si, No, Cancelar) para responder. 7.1. Cuadros de diálogo de entrada Un cuadro de diálogo de entrada hace una pregunta y proporciona un campo de texto para que el usuario pueda dar su respuesta. Se crean los cuadros de diálogo de entrada usando el método showInputDialog(). Hay seis versiones disponibles de este método, pero la versión más simple usa un sólo argumento que es el mensaje que se quiere mostrar dentro del cuadro de diálogo. Por ejemplo, el código 4 de la clase DialogoHolaNombre es una aplicación que crea un cuadro de diálogo de entrada con un mensaje para pedir el nombre. Después de que el usuario ingresa el nombre y pulsa el botón Aceptar o la tecla Intro, se concatena un saludo con el nombre y se muestra en cuadro de diálogo. 1 2 3 4 5 6 7 8 import j a v a x . swing . JOptionPane ; public c l a s s DialogoHolaNombre { public s t a t i c void main ( S t r i n g [ ] a r g s ) { String resultado ; r e s u l t a d o = JOptionPane . s ho wI np ut Dia lo g ( null , ”¿Cómo t e l l a m a s ? ” ) ; JOptionPane . showMessageDialog ( null , ” Hola , ¡” + r e s u l t a d o +” ! ” ) ; } } Código 4: clase DialogoHolaNombre Una versión del método showInputDialog() permite controlar la apariencia del cuadro de diálogo de entrada. Esta versión es la que requiere cuatro argumentos, y son para lo siguiente: El componente padre, el cual es el componente pantalla, como una ventana, en frente del cual aparecerá el cuadro de diálogo. Si este argumento es null, el cuadro de diálogo estará centrado en la pantalla. 15 El mensaje que se muestra al usuario usando un String pero puede ser cualquier tipo de objeto. El tı́tulo que será mostrado en la barra de tı́tulo del cuadro de diálogo de entrada. Un campo de clase describiendo el tipo de cuadro de diálogo; pudiendo ser alguno de los siguientes: ERROR MESSAGE, INFORMATION MESSAGE, PLAIN MESSAGE, QUESTION MESSAGE, y WARNING MESSAGE, El código 5 muestra el uso del cuadro de diálogo con cuatro argumentos. 1 2 3 4 JOptionPane . sh ow In put Di al og ( null , ”¿Qué edad t i e n e s ? ” , ” I n f o r m a c i ón p e r s o n a l ” , JOptionPane .QUESTION MESSAGE ) ; Código 5: cuadro de diálogo con 4 argumentos Los cuadros de diálogo de entrada regresan un objeto String. Si se quiere usar el valor que el usuario ingreso como un número, entonces el String deberá ser convertido. Para convertir un String a un tipo int o double, se deben usar métodos de las clases Integer y Double. Estas dos últimas clases son llamadas type-wrapper (tipo envoltura). Para obtener un valor double se usa el método Double.parseDouble(String), y para obtener un int el método Integer.parseInt(String). 7.2. Cuadros de diálogo de confirmación Si se quiere que la entrada del usuario no sea con el teclado, se pueden presentar botones para que el usuario pulse sobre alguno de estos para confirmar una opción. Un cuadro de diálogo de confirmación presenta los botones Si , No , y Cancelar , el cual puede ser creado usando algunas de las cuatro versiones del método showConfirmDialog() de la clase JoptionPane. Este método devuelve un entero siendo alguno de los siguientes tres valores posibles: JOptionPane.YES OPTION, JOptionPane.NO OPTION, o JOptionPane.CANCEL OPTION. El código 6 de la clase DialogoAerolinea muestra una aplicación que pide al usuario contestar una pregunta. La sentencia que inicia en la lı́nea 6 y termina en la lı́nea 7 muestra el cuadro de diálogo. La respuesta del usuario se guarda en la variable entera llamada seleccion. 16 1 2 3 4 5 6 7 8 9 10 11 12 import j a v a x . swing . JOptionPane ; public c l a s s D i a l o g o A e r o l i n e a { public s t a t i c void main ( S t r i n g [ ] a r g s ) { int s e l e c c i o n ; boolean e s S i ; s e l e c c i o n = JOptionPane . showConfirmDialog ( null , ”¿Desea a s c e n d e r a p r i m e r a c l a s e ? ” ) ; e s S i = ( s e l e c c i o n == JOptionPane . YES\ OPTION ) ; JOptionPane . showMessageDialog ( null , ” Usted r e s p o n d i ó ” + e s S i ) ; } } Código 6: cuadro de diálogo con 4 argumentos 8. Aritmética La tabla siguiente describe los cinco operadores aritméticos estándar que se usan para realizar cálculos con valores. Un valor usado en cada lado de un operador es un operando. Los operadores mostrados son operadores binarios. Operador + * / % Descripción Ejemplo Suma 17 + 3, el resultado es 20 Resta 17 − 3, el resultado es 14 Producto 17 ∗ 3, el resultado es 51 División 17.0/3, el resultado es 5.666667 17/3, el resultado es 5 Residuo 17 %3, el resultado es 2 Las operaciones son hechas siempre por Java con operandos del mismo tipo y el resultado es del mismo tipo de los operandos. Por ejemplo, 17/3 es 5 porque se realiza una división entera (sin ninguna parte fraccional), y 17.0/3 antes de que sea realizada, Java ajusta la precisión a la del operando de mayor precisión, de esta forma se hace 17.0/3.0 y devuelve 5.6666667. 8.1. Asociatividad y precedencia La asociatividad de los operadores aritméticos con la misma precedencia es de izquierda a derecha. En la sentencia respuesta = x + y + z; la x e y son sumados primero, obteniendo un resultado temporal, que después es agregado a z, y esta suma es la que se asigna a respuesta. La precedencia del operador se refiere al orden en el cual partes de la expresión matemática son evaluadas. La multiplicación, la división, y el residuo tienen la misma precedencia, y su 17 precedencia es mayor que la suma y la resta. La suma y la resta tienen la misma precedencia. La precedencia se puede modificar usando paréntesis, es decir, tienen el mismo uso que en las matemáticas. 2+3*4 (2+3)*4 2*(3+(4*5)) 25/8*3 8.2. Imprecisión de los números flotantes Los valores enteros en Java son exactos, pero los números de punto flotante frecuentemente son sólo aproximaciones ya que no pueden ser representados de forma precisa en formato binario en los sistemas de cómputo. Estas imprecisiones llevan a varios problemas: Cuando se genera salida de punto flotante, no podrı́a ser lo que se esperarı́a ver. Cuando se hacen comparaciones con números de punto flotante, no podrı́an ser lo que se esperarı́a obtener. Por ejemplo, el código 7 de la clase DemoImprecision muestra un respuesta cuando es calculado 2.20 - 2.00. El resultado matemático debe ser 0.20, pero la salida es 0.20000000000000018. Si se quieren eliminar las imprecisiones se pueden usar algunas de las varias técnicas para redondear valores. 1 public c l a s s DemoImprecision { 2 public s t a t i c void main ( S t r i n g [ ] a r g s ) { double r e s p u e s t a = 2 . 2 0 − 2 . 0 0 ; boolean e s I g u a l = r e s p u e s t a == 0 . 2 0 ; System . out . p r i n t l n ( ” r e s p u e s t a e s ” + r e s p u e s t a ) ; System . out . p r i n t l n ( ” e s I g u a l e s ” + e s I g u a l ) ; } 3 4 5 6 7 8 9 } Código 7: Programa DemoImprecision 9. Conversión de tipos Cuando se realiza aritmética con variables y constantes del mismo tipo, el resultado de la operación retiene el mismo tipo. Frecuentemente se quiere realizar operaciones matemáticas con operandos de tipos diferentes. El proceso de convertir de un tipo de datos a otro es la 18 conversión de tipo. Java realiza algunas conversiones automáticamente o implı́citamente, pero otras conversiones deben ser pedidas explı́citamente por el programador. 9.1. Conversión automática de tipo Al realizar operaciones aritméticas con operando de tipos diferentes, Java escoge un tipo unificado para el resultado. El tipo unificado es el tipo para el cual todos los operandos en una expresión son convertidos para que estos sean compatibles con el resto. Java realiza una conversión implı́cita, es decir, Java convierte automáticamentee operandos diferentes al tipo unificado. Las conversiones implı́citas son también llamadas promociones. En la siguiente tabla se muestra el orden o rango para establecer tipos unificados entre valores. Los tipos short y byte son convertidos automáticamente a int cuando son usados en expresiones. double Más alto float ^ long | int Más bajo Cuando dos tipos diferentes son usados en una expresión, el tipo unificado es aquel que esté más alto en la tabla, es decir, cuando un operando que es un tipo menor en la lista es combinado con un tipo que es mayor, el operando tipo menor es convertido al mayor. Por ejemplo, la suma de un double y un int da un double, y la resta de un long de un float da un float. Nota. Los valores booleanos no pueden ser convertidos a otro tipo. El resultado de la multiplicación, en el siguiente código, es un double porque un double y un int son multiplicados, el int es promovido al tipo unificador de rango más alto (double). Por lo que la asignación del resultado a pagoBruto es legal. int horasTrabajadas = 36; double pagoHora = 234.56; double pagoBruto = horasTrabajadas * pagoHora; El siguiente extracto de código no compilará porque horasTrabajadas por pagoHora es un double, y Java no permite la pérdida de precisión que ocurre si se intenta guardar el resultado double calculado en un int. int horasTrabajadas = 36; double pagoHora = 234.56; int pagoBruto = horasTrabajadas * pagoHora; 19 Los tipos de dato char, short, y byte son promovidos a int cuando se usan en sentencias con tipos diferentes, por lo que un cálculo que incluya cualquier combinación de los tipos anteriores dará un resultado int por defecto. 9.2. Conversiones explı́citas Se puede anular a propósito el tipo unificado impuesto por Java haciendo una conversión de tipo. La conversión de tipo forza un valor de un tipo de dato para ser usado como un valor de otro tipo. Para realizar lo anterior se usa el operador de conversión, el cual es creado poniendo el tipo del resultado deseado entre paréntesis. Usando un operador de conversión es una operación explı́citia. El operador de conversión es seguido por la constante, variable o expresión a ser convertida. En el siguiente código un conversión de tipo es hecha: double saldoBancario = 12345.67; float presupuestoSemanal = (float) (saldoBancario / 4); Sin la conversión, la sentencia que asigna el resultado a presupuestoSemanal no compiları́a. Nota. El operador de conversión es también llamado completamente operador de conversión unario. Este operador sólo usa un operando, que es el que sigue al operador de conversión. En el siguiente código ocurre una conversión de float a int: float miDinero = 543.21f; int pesos = (int) miDinero; Es fácil perder datos cuando se realiza una conversión. Por ejemplo, el valor byte más grande es 127 y el valor int mas grande es 2,147,483,647 ası́ que las siguientes sentencias da resultados distorsionados; int unEnteroCorrecto = 200; byte unMalByte = (byte)unEnteroCorrecto; Un tipo byte está formado por ocho valores uno ó cero, o dı́gitos binarios. El primer dı́gito binario, o bit, guarda un 0 o un 1 para indicar positivo o negativo respectivamente. Los siete bits restantes guardan el valor actual. Cuando el valor entero 200 es guardado en la variable byte, su valor más gran consume el octavo bit, poniendo un 1, y forzando a la variable unMalByte para que guarde el valor -72, el cual es inexacto y engañoso. No es necesario realizar una operación de conversión cuando se asigna un valor a un tipo unificador mayor. En la siguiente sentencia Java automáticamente promueve la constante entera 123 a un double para que pueda ser guardada en la variable pagoHora: 20 double pagoHora = 123; Sin embargo, por claridad, para una asignación como la anterior, se podrı́a preferir escribir lo siguiente: double pagoHora = 123.0; 21